using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameTranslator.Patches; using GameTranslator.Patches.Hooks; using GameTranslator.Patches.Hooks.texture; using GameTranslator.Patches.InteractiveTerminalAPI; using GameTranslator.Patches.Translatons; using GameTranslator.Patches.Translatons.Manipulator; using GameTranslator.Patches.Utils; using GameTranslator.Patches.Utils.Textures; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.UIElements; using XUnity.Common.Constants; using XUnity.Common.Extensions; using XUnity.Common.Harmony; using XUnity.Common.Logging; using XUnity.Common.MonoMod; using XUnity.Common.Utilities; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("GameTranslator")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+85fd74ad5ea55eda6a32b45b169004784bb767d6")] [assembly: AssemblyProduct("GameTranslator")] [assembly: AssemblyTitle("GameTranslator")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 GameTranslator { public class TranslateConfig { public class TranslateConfigFile { public string ConfigFilePath; public string ConfigFileName; public bool shouldTranslate; public int shouldTranslateMinLength = 200; public int shouldTranslateMaxLength; public bool shouldLoad = true; public IDictionary normal = new Dictionary(); public IDictionary special = new Dictionary(); public static HashSet configs = new HashSet(); public Dictionary translatePairs = new Dictionary(); private List logs = new List(); public List regexTranslations = new List(); public List splitterRegexTranslations = new List(); private static Regex _regex; internal readonly ConcurrentDictionary _translatePairLastAccess = new ConcurrentDictionary(); public static Regex regex { get { if (_regex == null) { Type typeFromHandle = typeof(TranslateConfigFile); lock (typeFromHandle) { if (_regex == null) { _regex = new Regex("(? list = new List(); string[] array = File.ReadAllLines(ConfigFilePath); foreach (string text in array) { if (text.StartsWith("#") || !text.Contains("=")) { continue; } string[] array2 = regex.Split(text); if (array2.Length != 2) { continue; } string text2 = array2[0].Replace("\\=", "="); string text3 = array2[1].Replace("\\=", "="); if (text2.StartsWith("r:")) { try { RegexTranslation item = new RegexTranslation(text2, text3); regexTranslations.Add(item); } catch (Exception ex) { string text4 = text2 + "=" + text3; list.Add("Invalid regex: " + text4 + " - " + ex.Message); TranslatePlugin.logger.LogWarning((object)("Failed to parse regex: " + text4 + ". Error: " + ex.Message)); } continue; } if (text2.StartsWith("sr:")) { try { RegexTranslationSplitter item2 = new RegexTranslationSplitter(text2, text3); splitterRegexTranslations.Add(item2); } catch (Exception ex2) { string text5 = text2 + "=" + text3; list.Add("Invalid splitter regex: " + text5 + " - " + ex2.Message); TranslatePlugin.logger.LogWarning((object)("Failed to parse splitter regex: " + text5 + ". Error: " + ex2.Message)); } continue; } if (normal.ContainsKey(text2)) { normal[text2] = text3; special[text3] = text2; } else { normal.Add(text2, text3); if (!special.ContainsKey(text3)) { special.Add(text3, text2); } } if (text2.Length < shouldTranslateMinLength) { shouldTranslateMinLength = text2.Length; } if (text2.Length > shouldTranslateMaxLength) { shouldTranslateMaxLength = text2.Length; } } if (list.Count > 0) { File.AppendAllLines(Path.Combine(Path.GetDirectoryName(ConfigFilePath), ConfigFileName + "_errors.log"), list); } } public void Log(string text) { logs.Add(text); string directoryName = Path.GetDirectoryName(ConfigFilePath); if (directoryName == null) { Directory.CreateDirectory(directoryName); } new List().Add("##" + ConfigFileName); File.WriteAllLines(ConfigFilePath, logs); } public void Save() { string directoryName = Path.GetDirectoryName(ConfigFilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(ConfigFilePath)) { File.Create(ConfigFilePath).Close(); } else { if (!shouldLoad) { return; } List list = new List(); list.Add("##" + ConfigFileName); foreach (KeyValuePair item in normal) { string text = item.Key.Replace("=", "\\="); string text2 = item.Value.Replace("=", "\\="); list.Add(text + "=" + text2); } list.Add("##RegularExpression"); foreach (RegexTranslation regexTranslation in regexTranslations) { list.Add("r:" + regexTranslation.Key + "=" + regexTranslation.Value); } list.Add("##SplitterRegularExpression"); foreach (RegexTranslationSplitter splitterRegexTranslation in splitterRegexTranslations) { list.Add("sr:" + splitterRegexTranslation.Key + "=" + splitterRegexTranslation.Value); } File.WriteAllLines(ConfigFilePath, list.ToArray()); } } } private static SafeFileWatcher _fileWatcher; private static ConcurrentDictionary _fileLastModifiedTimes = new ConcurrentDictionary(); private static Timer _pollingTimer; private static readonly object _updateLock = new object(); public static TranslateConfigFile normal; public static TranslateConfigFile hud; public static TranslateConfigFile items; public static TranslateConfigFile terminal; public static TranslateConfigFile text; public static TranslateConfigFile cmd_py; public static TranslateConfigFile cmd_zh; public static TranslateConfigFile gui; public static TranslateConfigFile interactiveTerminalAPI; public static NormalTextTranslator normalText; public static NormalTextTranslator hudText; public static NormalTextTranslator guiText; public static TextureTranslationCache cache; public static NormalTextTranslator itemsText; public static NormalTextTranslator terminalText; public static NormalTextTranslator textText; public static NormalTextTranslator cmdPyText; public static NormalTextTranslator cmdZhText; public static NormalTextTranslator interactiveTerminalAPIText; private static DateTime _lastCleanupTime = DateTime.Now; private static readonly TimeSpan CLEANUP_INTERVAL = TimeSpan.FromMinutes(30.0); public static void Load() { hud = CreateNewConfig("HUD-Translate"); hud.shouldTranslate = TranslatePlugin.shouldTranslateHUD.Value; hudText = new NormalTextTranslator(hud.ConfigFileName + ".cfg"); hudText.Load(); items = CreateNewConfig("Item-Translate"); items.shouldTranslate = TranslatePlugin.shouldTranslateItems.Value; itemsText = new NormalTextTranslator(items.ConfigFileName + ".cfg"); itemsText.Load(); terminal = CreateNewConfig("Terminal-Translate"); terminal.shouldTranslate = TranslatePlugin.shouldTranslateTerimal.Value; terminalText = new NormalTextTranslator(terminal.ConfigFileName + ".cfg"); terminalText.Load(); cmd_py = CreateNewConfig("CMD-PY-Translate"); cmdPyText = new NormalTextTranslator(cmd_py.ConfigFileName + ".cfg"); cmdPyText.Load(); cmd_zh = CreateNewConfig("CMD-ZH-Translate"); cmdZhText = new NormalTextTranslator(cmd_zh.ConfigFileName + ".cfg"); cmdZhText.Load(); text = CreateNewConfig("SpecialText-Translate"); text.shouldTranslate = TranslatePlugin.shouldTranslateSpecialText.Value; textText = new NormalTextTranslator(text.ConfigFileName + ".cfg"); textText.Load(); gui = CreateNewConfig("GuiText-Translate"); gui.shouldTranslate = TranslatePlugin.shouldTranslateGui.Value; guiText = new NormalTextTranslator(gui.ConfigFileName + ".cfg"); guiText.Load(); interactiveTerminalAPI = CreateNewConfig("InteractiveTerminalAPI-Translate"); interactiveTerminalAPI.shouldTranslate = TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value; interactiveTerminalAPIText = new NormalTextTranslator(interactiveTerminalAPI.ConfigFileName + ".cfg"); interactiveTerminalAPIText.Load(); normal = CreateNewConfig("Normal-Translate"); normal.shouldTranslate = TranslatePlugin.shouldTranslateNormalText.Value; normalText = new NormalTextTranslator(normal.ConfigFileName + ".cfg"); normalText.Load(); cache = new TextureTranslationCache(); cache.LoadTranslationFiles(); string fullPath = Path.GetFullPath(TranslatePlugin.DefaultPath); ConfigEntry enableFileWatcher = TranslatePlugin.enableFileWatcher; if (enableFileWatcher != null && enableFileWatcher.Value) { _fileWatcher = new SafeFileWatcher(fullPath); _fileWatcher.DirectoryUpdated += OnDirectoryUpdated; TranslatePlugin.logger.LogInfo((object)("Tracking path " + fullPath)); } foreach (TranslateConfigFile config in TranslateConfigFile.configs) { if (File.Exists(config.ConfigFilePath)) { _fileLastModifiedTimes[config.ConfigFilePath] = File.GetLastWriteTime(config.ConfigFilePath); } } AsyncTranslationManager.Instance.ClearCache(); ConfigEntry enablePollingCheck = TranslatePlugin.enablePollingCheck; if (enablePollingCheck != null && enablePollingCheck.Value) { _pollingTimer = new Timer(delegate { OnDirectoryUpdated(); }, null, TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(10.0)); TranslatePlugin.logger.LogInfo((object)("Polling check tracking path " + fullPath)); } } public static void Unload() { _fileWatcher?.Dispose(); _fileWatcher = null; cache?.Dispose(); cache = null; _pollingTimer?.Dispose(); _pollingTimer = null; } private static void OnDirectoryUpdated() { lock (_updateLock) { try { bool flag = false; foreach (TranslateConfigFile config in TranslateConfigFile.configs) { if (!config.shouldLoad || !File.Exists(config.ConfigFilePath)) { continue; } DateTime lastWriteTime = File.GetLastWriteTime(config.ConfigFilePath); if (_fileLastModifiedTimes.TryGetValue(config.ConfigFilePath, out var value)) { if (!(lastWriteTime > value)) { continue; } _fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime; for (int i = 0; i < 3; i++) { try { config.Reload(); GetModuleTranslator(config)?.Load(); TextTranslate.ChangeTime++; flag = true; } catch (IOException) when (i < 2) { Thread.Sleep(100 * (i + 1)); continue; } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Unexpected error reloading config " + config.ConfigFileName + ": " + ex2.Message)); } break; } } else { _fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime; } } if (flag) { TranslatePlugin.logger.LogInfo((object)"Translate files reloaded due to file changes."); } } catch (Exception ex3) { TranslatePlugin.logger.LogError((object)("Error in OnDirectoryUpdated: " + ex3.Message)); } } } public static void show(TranslateConfigFile file) { foreach (string key in file.normal.Keys) { TranslatePlugin.LogInfo(key + "=" + file.normal[key]); } } private static TranslateConfigFile CreateNewConfig(string fileName, bool should) { TranslatePlugin.logger.LogInfo((object)("GameTranslator is loading config file call " + fileName)); return new TranslateConfigFile(fileName, saveOnInit: true, should); } public static bool IsStringContainsEnglish(string input) { if (!string.IsNullOrEmpty(input)) { return new Regex("[a-zA-Z]").IsMatch(input); } return false; } public static bool IsStringContainsChinese(string input) { if (!string.IsNullOrEmpty(input)) { return new Regex("[\\u4e00-\\u9fa5]").IsMatch(input); } return false; } public static string useRegularExpression(string raw, string pattern, string result) { return Regex.Replace(raw, pattern, result); } public static string replaceByMap(string text, TranslateConfigFile file) { if (file.normal.Count == 0 && file.regexTranslations.Count == 0 && file.splitterRegexTranslations.Count == 0) { return text; } Stopwatch stopwatch = Stopwatch.StartNew(); try { if (DateTime.Now - _lastCleanupTime > CLEANUP_INTERVAL) { CleanupTranslatePairs(); _lastCleanupTime = DateTime.Now; } if (!file.shouldTranslate) { return text; } if (file.translatePairs.ContainsKey(text)) { file._translatePairLastAccess.TryAdd(text, DateTime.Now); file._translatePairLastAccess[text] = DateTime.Now; return file.translatePairs[text]; } StringBuffer stringBuffer = new StringBuffer(text); NormalTextTranslator moduleTranslator = GetModuleTranslator(file); if (moduleTranslator != null) { foreach (RegexTranslationSplitter splitterRegex in moduleTranslator._splitterRegexes) { string str = moduleTranslator.SplitterTranslate(stringBuffer.ToString(), splitterRegex, ignoreCase: false); stringBuffer.Clear().Append(str); } foreach (RegexTranslation defaultRegex in moduleTranslator._defaultRegexes) { if (defaultRegex.CompiledRegex != null && defaultRegex.CompiledRegex.IsMatch(stringBuffer.ToString())) { string str2 = defaultRegex.CompiledRegex.Replace(stringBuffer.ToString(), defaultRegex.Translation); stringBuffer.Clear().Append(str2); } } } foreach (KeyValuePair item in file.normal.OrderByDescending((KeyValuePair kv) => kv.Key.Length)) { stringBuffer.ReplaceFull(item.Key, item.Value); } string text2 = stringBuffer.ToString(); file.translatePairs[text] = text2; file._translatePairLastAccess.TryAdd(text, DateTime.Now); return text2; } finally { stopwatch.Stop(); if (stopwatch.ElapsedMilliseconds > 500) { string arg = ((text.Length > 50) ? (text.Substring(0, 50) + "...") : text); TranslatePlugin.logger.LogWarning((object)$"replaceByMap took {stopwatch.ElapsedMilliseconds}ms for text: {arg}"); } } } private static NormalTextTranslator GetModuleTranslator(TranslateConfigFile file) { if (file == normal) { return normalText; } if (file == hud) { return hudText; } if (file == items) { return itemsText; } if (file == terminal) { return terminalText; } if (file == text) { return textText; } if (file == cmd_py) { return cmdPyText; } if (file == cmd_zh) { return cmdZhText; } if (file == gui) { return guiText; } if (file == interactiveTerminalAPI) { return interactiveTerminalAPIText; } return null; } private static void CleanupTranslatePairs() { bool flag = GC.GetTotalMemory(forceFullCollection: false) > 104857600; foreach (TranslateConfigFile config in TranslateConfigFile.configs) { int num = 0; if (flag) { num = (int)((float)config.translatePairs.Count * 0.2f); num = Math.Max(1, Math.Min(num, config.translatePairs.Count)); } else if (config.translatePairs.Count > 6000) { num = config.translatePairs.Count - 6000; } if (num <= 0) { continue; } List list = config._translatePairLastAccess.OrderBy((KeyValuePair kv) => kv.Value).Take(num).Select(delegate(KeyValuePair kv) { KeyValuePair keyValuePair = kv; return keyValuePair.Key; }) .ToList(); foreach (string item in list) { config.translatePairs.Remove(item); config._translatePairLastAccess.TryRemove(item, out var _); } TranslatePlugin.logger.LogInfo((object)$"Cleaned {list.Count} translate pairs from {config.ConfigFileName}. Remaining: {config.translatePairs.Count}"); } } private static TranslateConfigFile CreateNewConfig(string fileName) { return CreateNewConfig(fileName, should: true); } } [BepInPlugin("GameTranslator", "GameTranslator", "2.1.6")] public class TranslatePlugin : BaseUnityPlugin { private class TranslationUpdater : MonoBehaviour { private void Update() { try { AsyncTranslationManager.Instance.ProcessMainThreadActions(); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TranslationUpdater Update: " + ex.Message)); } } } } private readonly Harmony harmony = new Harmony("GameTranslator"); private const string PLUGIN_GUID = "GameTranslator"; private const string PLUGIN_NAME = "GameTranslator"; private const string PLUGIN_VERSION = "2.1.6"; public static ManualLogSource logger; public static ConfigEntry syncTranslationThreshold; public static ConfigEntry showAvailableText; public static ConfigEntry showOtherDebug; public static ConfigEntry enableFileWatcher; public static ConfigEntry enablePollingCheck; public static ConfigEntry replaceUnsupportedCharacters; public static ConfigEntry cacheUnmodifiedTextures; public static ConfigEntry stabilizationMinTextLength; public static ConfigEntry stabilizationDelay; public static ConfigEntry stabilizationMaxRetries; public static ConfigEntry enableGrabbableObjectPatch; public static ConfigEntry enableHUDManagerPatch; public static ConfigEntry enableTerminalPatch; public static ConfigEntry changeFont; public static ConfigEntry fallbackFontTextMeshPro; public static ConfigEntry shouldRemoveChar; public static ConfigEntry language; public static ConfigEntry shouldTranslateNormalText; public static ConfigEntry shouldTranslateSpecialText; public static ConfigEntry shouldTranslateTerimal; public static ConfigEntry shouldTranslateInteractiveTerminalAPI; public static ConfigEntry TerimalCanUseChinese; public static ConfigEntry TerimalCanUsePinyinAbbreviation; public static ConfigEntry shouldTranslateGui; public static ConfigEntry shouldTranslateItems; public static ConfigEntry shouldTranslateHUD; public static ConfigEntry changeTexture; public static ConfigEntry cacheTexturesInMemory; public static ConfigEntry disableDuplicateTextureCheck; public static ConfigEntry ignoredTextureNames; public static ConfigEntry generateTerimalCommand; internal static TranslatePlugin Instance; public static string DefaultPath; public static string TexturesPath; public static bool shouldTranslate; public static bool CacheTexturesInMemory => cacheTexturesInMemory.Value; private void Awake() { logger = ((BaseUnityPlugin)this).Logger; Instance = this; ((Component)this).gameObject.AddComponent(); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); ConfigFile(); HookingHelper.PatchAll((IEnumerable)ImageHooks.All, false); HookingHelper.PatchAll((IEnumerable)ImageHooks.Sprite, false); HookingHelper.PatchAll((IEnumerable)ImageHooks.SpriteRenderer, false); ApplyBasicPatches(); ApplyGrabbableObjectPatch(); ApplyHUDManagerPatch(); ApplyTerminalPatch(); ApplyInteractiveTerminalAPIPatch(); if (replaceUnsupportedCharacters.Value) { FontSupportChecker.InitializeFonts(); } AsyncTranslationManager.Instance.Start(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator is loaded"); } private void OnDestroy() { try { AsyncTranslationManager.Instance.Stop(); } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Error in OnDestroy: " + ex.Message)); } } TranslateConfig.Unload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator destroyed"); } private void ConfigFile() { syncTranslationThreshold = ((BaseUnityPlugin)this).Config.Bind("ASync", "Sync Translation Threshold", 300, "Define the character threshold to not use async translation"); showAvailableText = ((BaseUnityPlugin)this).Config.Bind("Debug", "Show Available Text", false, "Define whether to show available text"); showOtherDebug = ((BaseUnityPlugin)this).Config.Bind("Debug", "Show Other Debug", false, "Define whether to show other debug"); enableFileWatcher = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable File Watcher", false, "If true, enable file system watcher for file updates"); enablePollingCheck = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable Polling Check", false, "If true, enable the 10-seconds polling fallback for file updates"); replaceUnsupportedCharacters = ((BaseUnityPlugin)this).Config.Bind("Debug", "Replace Unsupported Characters", false, "Define whether to replace unsupported characters with Unicode character u25A1"); cacheUnmodifiedTextures = ((BaseUnityPlugin)this).Config.Bind("Debug", "Cache Unmodified Textures", false, "Define whether to cache textures that have not been modified"); stabilizationMinTextLength = ((BaseUnityPlugin)this).Config.Bind("Debug", "Stabilization Min Text Length", 100, "Define minimum text length to trigger stabilization. Set to 0 to disable stabilization"); stabilizationDelay = ((BaseUnityPlugin)this).Config.Bind("Debug", "Stabilization Delay", 0.9f, "Define delay in seconds between stabilization checks. Must be greater than 0"); stabilizationMaxRetries = ((BaseUnityPlugin)this).Config.Bind("Debug", "Stabilization Max Retries", 60, "Define maximum retries for text stabilization safeguard. Set to 0 for unlimited retries"); enableGrabbableObjectPatch = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable GrabbableObject Patch", true, "Define whether to patch GrabbableObject"); enableHUDManagerPatch = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable HUDManager Patch", true, "Define whether to patch HUDManager"); enableTerminalPatch = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable Terminal Patch", true, "Define whether to patch Terminal"); changeFont = ((BaseUnityPlugin)this).Config.Bind("Font", "Change Font", false, "Define whether to change the font"); fallbackFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind("Font", "FallbackFontTextMeshPro", "", "Define the fallback font file used"); shouldRemoveChar = ((BaseUnityPlugin)this).Config.Bind("Font", "Custom Characters", "", "Define what vanilla characters will use custom ones"); language = ((BaseUnityPlugin)this).Config.Bind("General", "Language", "Default", "Define what language folder is used"); shouldTranslateNormalText = ((BaseUnityPlugin)this).Config.Bind("General", "Translate Normal Text", true, "Define whether to use Normal Translate method"); shouldTranslateSpecialText = ((BaseUnityPlugin)this).Config.Bind("General", "Translate Special Text", false, "Define whether to use SpecialText Translate method"); shouldTranslateTerimal = ((BaseUnityPlugin)this).Config.Bind("General", "Translate Terminal", false, "Define whether translate Terminal"); shouldTranslateInteractiveTerminalAPI = ((BaseUnityPlugin)this).Config.Bind("General", "Translate InteractiveTerminalAPI", false, "Define whether translate InteractiveTerminalAPI"); TerimalCanUseChinese = ((BaseUnityPlugin)this).Config.Bind("General", "Terminal Can Use Non-English Shortcut Commands", false, "Define whether the terminal can use non-English shortcut commands, such as Chinese"); TerimalCanUsePinyinAbbreviation = ((BaseUnityPlugin)this).Config.Bind("General", "Terminal Can Use Custom Shortcut Commands", false, "Define whether the terminal can use custom shortcut commands"); shouldTranslateGui = ((BaseUnityPlugin)this).Config.Bind("General", "Translate Gui", false, "Define whether translate Gui"); shouldTranslateItems = ((BaseUnityPlugin)this).Config.Bind("General", "Translate Items", false, "Define whether translate Items"); shouldTranslateHUD = ((BaseUnityPlugin)this).Config.Bind("General", "Translate HUD", false, "Define whether translate HUD"); changeTexture = ((BaseUnityPlugin)this).Config.Bind("Texture", "Change Texture", false, "Define whether to change the texture"); cacheTexturesInMemory = ((BaseUnityPlugin)this).Config.Bind("Texture", "Cache Textures In Memory", true, "Define whether to cache texture data in memory for faster loading"); disableDuplicateTextureCheck = ((BaseUnityPlugin)this).Config.Bind("Texture", "Disable Duplicate Texture Check", true, "Define whether to disable duplicate texture name check"); ignoredTextureNames = ((BaseUnityPlugin)this).Config.Bind("Texture", "Ignored Texture Names", "", "Define what texture names to skip duplicate check"); DefaultPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("GameTranslator.cfg", "translations\\" + language.Value + "\\"); if (!Directory.Exists(DefaultPath)) { logger.LogWarning((object)("Translation path does not exist: " + DefaultPath)); try { Directory.CreateDirectory(DefaultPath); logger.LogInfo((object)("Created translation directory: " + DefaultPath)); } catch (Exception ex) { logger.LogError((object)("Failed to create translation directory: " + ex.Message)); DefaultPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), "translations", "default"); Directory.CreateDirectory(DefaultPath); logger.LogInfo((object)("Using fallback translation directory: " + DefaultPath)); } } TexturesPath = DefaultPath + "Texture\\"; if (!Directory.Exists(TexturesPath)) { Directory.CreateDirectory(TexturesPath); } TranslateConfig.Load(); TranslateExtensions.Load(); } public static List getShouldRemoveChars() { return shouldRemoveChar.Value.ToCharArray().ToList(); } public static void LogInfo(string info) { if (logger != null) { logger.LogInfo((object)info); } } private void ApplyBasicPatches() { try { logger.LogInfo((object)"Applying basic patches..."); Type[] array = new Type[64] { typeof(GameObjectHook), typeof(GuiContentHook), typeof(TeshMeshProHook), typeof(TeshMeshProUGUIHook), typeof(TextArea2DHook), typeof(TextFieldHook), typeof(TextElement_text_Hook), typeof(TextHook), typeof(TextMeshHook), typeof(Texture2DHook), typeof(TMP_FontAssetHook), typeof(TMP_TextHook), typeof(CubismRenderer_MainTexture_Hook), typeof(CubismRenderer_TryInitialize_Hook), typeof(Cursor_SetCursor_Hook), typeof(DicingTextures_GetTexture_Hook), typeof(Image_material_Hook), typeof(Image_overrideSprite_Hook), typeof(Image_sprite_Hook), typeof(ImageHooks), typeof(MaskableGraphic_OnEnable_Hook), typeof(Material_mainTexture_Hook), typeof(RawImage_texture_Hook), typeof(Sprite_texture_Hook), typeof(SpriteRenderer_sprite_Hook), typeof(UI2DSprite_material_Hook), typeof(UI2DSprite_sprite2D_Hook), typeof(UIAtlas_spriteMaterial_Hook), typeof(UIPanel_clipTexture_Hook), typeof(UIRect_OnInit_Hook), typeof(UISprite_atlas_Hook), typeof(UISprite_material_Hook), typeof(UISprite_OnInit_Hook), typeof(UITexture_mainTexture_Hook), typeof(UITexture_material_Hook), typeof(AsyncTranslationManager), typeof(ImageTranslationInfo), typeof(NormalTextTranslator), typeof(RegexTranslation), typeof(RegexTranslationSplitter), typeof(TextTranslationInfo), typeof(TextureDataResult), typeof(TextureTranslationCache), typeof(TextureTranslationInfo), typeof(TranslatedImage), typeof(TranslateExtensions), typeof(DefaultTextComponentManipulator), typeof(FairyGUITextComponentManipulator), typeof(ITextComponentManipulator), typeof(TextArea2DComponentManipulator), typeof(UguiNovelTextComponentManipulator), typeof(FontCache), typeof(FontHelper), typeof(FontSupportChecker), typeof(SceneManagerLoader), typeof(StringBuffer), typeof(TextHelper), typeof(TextTranslate), typeof(TextureTranslate), typeof(TranslationScopeHelper), typeof(ITextureLoader), typeof(LoadImageImageLoader), typeof(TextureLoader), typeof(TgaImageLoader) }; List list = array.Select((Type t) => t.Name).ToList(); logger.LogDebug((object)string.Format("Found {0} basic patch types: {1}", list.Count, string.Join(", ", list))); int num = 0; List list2 = new List(); Type[] array2 = array; foreach (Type type in array2) { try { harmony.PatchAll(type); num++; list2.Add(type.Name); logger.LogDebug((object)("Applied basic patch: " + type.Name)); } catch (Exception ex) { logger.LogWarning((object)("Failed to apply basic patch " + type.Name + ": " + ex.Message)); } } logger.LogInfo((object)$"Basic patches applied. Successfully applied {num}/{array.Length} patches."); if (list2.Count > 0) { logger.LogDebug((object)("Successfully applied patches: " + string.Join(", ", list2))); } if (num < array.Length) { List list3 = list.Except(list2).ToList(); logger.LogWarning((object)string.Format("Failed to apply {0} patches: {1}", list3.Count, string.Join(", ", list3))); } } catch (Exception ex2) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Error applying basic patches: " + ex2.Message)); } } } private void ApplyGrabbableObjectPatch() { try { if (enableGrabbableObjectPatch != null && enableGrabbableObjectPatch.Value) { harmony.PatchAll(typeof(GrabbableObjectPatcher)); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"GrabbableObject patch applied successfully"); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"GrabbableObject patch disabled by config"); } } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Error applying GrabbableObject patch: " + ex.Message)); } } } private void ApplyHUDManagerPatch() { try { if (enableHUDManagerPatch != null && enableHUDManagerPatch.Value) { harmony.PatchAll(typeof(HUDManagerPatcher)); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"HUDManager patch applied successfully"); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"HUDManager patch disabled by config"); } } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Error applying HUDManager patch: " + ex.Message)); } } } private void ApplyTerminalPatch() { try { if (enableTerminalPatch != null && enableTerminalPatch.Value) { harmony.PatchAll(typeof(TerminalPatch)); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Terminal patch applied successfully"); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"Terminal patch disabled by config"); } } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Error applying Terminal patch: " + ex.Message)); } } } private void ApplyInteractiveTerminalAPIPatch() { try { if (shouldTranslateInteractiveTerminalAPI != null && shouldTranslateInteractiveTerminalAPI.Value) { InteractiveTerminalAPIPatch.Initialize(harmony); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"InteractiveTerminalAPI patch applied successfully"); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"InteractiveTerminalAPI patch disabled by config"); } } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Error applying InteractiveTerminalAPI patch: " + ex.Message)); } } } } } namespace GameTranslator.Patches { [HarmonyPatch(typeof(GrabbableObject))] internal class GrabbableObjectPatcher { public static Dictionary originToTranslated = new Dictionary(); public static HashSet translatedItems = new HashSet(); [HarmonyPostfix] [HarmonyPatch("Start")] private static void start(ref GrabbableObject __instance) { if (!((Object)(object)__instance.itemProperties != (Object)null) || !TranslatePlugin.shouldTranslateItems.Value || Utility.IsNullOrWhiteSpace(__instance.itemProperties.itemName)) { return; } if (!translatedItems.Contains(__instance.itemProperties.itemName)) { originToTranslated[__instance.itemProperties.itemName] = TranslateConfig.replaceByMap(__instance.itemProperties.itemName, TranslateConfig.items); translatedItems.Add(originToTranslated[__instance.itemProperties.itemName]); __instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName]; } if (originToTranslated.ContainsKey(__instance.itemProperties.itemName)) { __instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName]; } ScanNodeProperties componentInChildren = ((Component)__instance).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.headerText != null) { if (!translatedItems.Contains(componentInChildren.headerText)) { originToTranslated[componentInChildren.headerText] = TranslateConfig.replaceByMap(componentInChildren.headerText, TranslateConfig.items); translatedItems.Add(originToTranslated[componentInChildren.headerText]); componentInChildren.headerText = originToTranslated[componentInChildren.headerText]; } if (originToTranslated.ContainsKey(componentInChildren.headerText)) { componentInChildren.headerText = originToTranslated[componentInChildren.headerText]; } } } } [HarmonyPatch(typeof(HUDManager))] internal class HUDManagerPatcher { public static FieldInfo scanNodes = typeof(HUDManager).GetField("scanNodes", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo nodesOnScreen = typeof(HUDManager).GetField("nodesOnScreen", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static Dictionary originToTranslated = new Dictionary(); public static HashSet translatedItems = new HashSet(); private static string lastChat = ""; private static readonly BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo CanTipDisplay = typeof(HUDManager).GetMethod("CanTipDisplay", All); private static MethodInfo StopCoroutine = typeof(HUDManager).GetMethod("StopCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null); private static MethodInfo StartCoroutine = typeof(HUDManager).GetMethod("StartCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null); private static MethodInfo TipsPanelTimer = typeof(HUDManager).GetMethod("TipsPanelTimer", All); private static FieldInfo tipsPanelCoroutine = typeof(HUDManager).GetField("tipsPanelCoroutine", All); [HarmonyPostfix] [HarmonyPatch("Start")] private static void start(ref HUDManager __instance) { } [HarmonyPostfix] [HarmonyPatch("Update")] private static void update(HUDManager __instance) { if (!TranslatePlugin.shouldTranslateHUD.Value) { return; } string text = ((TMP_Text)__instance.chatText).text; if (lastChat.Length != text.Length) { string text2 = TranslateConfig.hudText.TryTranslate(text); if (!string.IsNullOrEmpty(text2) && text2 != text) { ((TMP_Text)__instance.chatText).text = text2; } } lastChat = text; } private static void fadeText(TextMeshProUGUI text, ref bool fade, float duration, float newAlpha) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) float a = ((Graphic)text).color.a; float num = 0f; fade = true; while (num < duration) { num += Time.deltaTime; float num2 = Mathf.Lerp(a, newAlpha, num / duration); ((Graphic)text).color = new Color(((Graphic)text).color.r, ((Graphic)text).color.g, ((Graphic)text).color.b, num2); } ((Behaviour)text).enabled = false; fade = false; } [HarmonyPostfix] [HarmonyPatch("AddChatMessage")] private static void changeChatMessage(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped) { if (!TranslatePlugin.shouldTranslateHUD.Value || (Object)(object)__instance == (Object)null || ((object)__instance).Equals((object?)null)) { return; } TMP_Text chatText = (TMP_Text)(object)__instance.chatText; if ((Object)(object)chatText == (Object)null || ((object)chatText).Equals((object?)null) || !((Component)chatText).gameObject.activeInHierarchy) { return; } try { string text = chatText.text; if (!string.IsNullOrEmpty(text)) { string text2 = TranslateConfig.hudText.TryTranslate(text); if (!string.IsNullOrEmpty(text2) && !text2.Equals(text)) { chatText.text = text2; } } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error in changeChatMessage: " + ex.Message)); } } [HarmonyPrefix] [HarmonyPatch("DisplayTip")] [HarmonyPriority(int.MaxValue)] private static bool changeTip(HUDManager __instance, ref string headerText, ref string bodyText, bool isWarning = false, bool useSave = false, string prefsKey = "LC_Tip1") { if (TranslatePlugin.shouldTranslateHUD.Value) { headerText = TranslateConfig.hudText.TryTranslate(headerText); bodyText = TranslateConfig.hudText.TryTranslate(bodyText); } return true; } [HarmonyPrefix] [HarmonyPatch("DisplayGlobalNotification")] [HarmonyPriority(int.MaxValue)] private static bool changeNotification(HUDManager __instance, ref string displayText) { if (TranslatePlugin.shouldTranslateHUD.Value) { displayText = TranslateConfig.hudText.TryTranslate(displayText); } return true; } } [HarmonyPatch(typeof(Terminal))] internal class TerminalPatch { public class Translator { public Dictionary keyValuePairs = new Dictionary(); public Dictionary keys = new Dictionary(); public TerminalKeyword getTranslateKey(TerminalKeyword old, bool useC) { if (!keyValuePairs.ContainsKey(((Object)old).name + old.word)) { if ((useC && !HaveChineseCMD(old.word)) || (!useC && !HavePinyinCMD(old.word))) { keyValuePairs.Add(((Object)old).name + old.word, old); return old; } string text = (useC ? getChineseCMD(old.word) : getPinyinCMD(old.word)); if (text == old.word) { keyValuePairs.Add(((Object)old).name + old.word, old); return old; } TerminalKeyword val = Copy(old); val.word = text; keyValuePairs.Add(((Object)old).name + old.word, val); if (val.compatibleNouns != null) { List list = val.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns = val.compatibleNouns; foreach (CompatibleNoun val2 in compatibleNouns) { if (!getTranslateKey(val2.noun, useC).word.Equals(val2.noun.word)) { CompatibleNoun val3 = TransReflection(val2); val3.noun = getTranslateKey(val2.noun, useC); list.Add(val3); } } val.compatibleNouns = list.ToArray(); } if ((Object)(object)val.defaultVerb != (Object)null && val.defaultVerb.compatibleNouns != null) { List list2 = val.defaultVerb.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns2 = val.defaultVerb.compatibleNouns; foreach (CompatibleNoun val4 in compatibleNouns2) { if (!getTranslateKey(val4.noun, useC).word.Equals(val4.noun.word)) { CompatibleNoun val5 = TransReflection(val4); val5.noun = getTranslateKey(val4.noun, useC); list2.Add(val5); } } val.defaultVerb.compatibleNouns = list2.ToArray(); } return val; } return keyValuePairs[((Object)old).name + old.word]; } public void getTranslateKey(TerminalKeyword old) { if (keys.ContainsKey(old.word)) { return; } keys.Add(old.word, old.word); if (old.compatibleNouns != null) { old.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns = old.compatibleNouns; foreach (CompatibleNoun val in compatibleNouns) { getTranslateKey(val.noun); } } if ((Object)(object)old.defaultVerb != (Object)null && old.defaultVerb.compatibleNouns != null) { old.defaultVerb.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns2 = old.defaultVerb.compatibleNouns; foreach (CompatibleNoun val2 in compatibleNouns2) { getTranslateKey(val2.noun); } } } } public static FieldInfo hasGottenVerb = typeof(Terminal).GetField("hasGottenVerb", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo hasGottenNoun = typeof(Terminal).GetField("hasGottenNoun", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static bool shouldTranslate = false; public static bool noText = false; public static int texdAdded = 0; public static FieldInfo modifyingText = typeof(Terminal).GetField("modifyingText", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static Dictionary keyValuePairs = new Dictionary(); public static TMP_InputField screenText = null; public static TextTranslationInfo info; public static HashSet ig = new HashSet(); private static int CheckForPlayerNameCommand(string firstWord, string secondWord) { if (firstWord == "radar") { return -1; } if (secondWord.Length <= 2) { return -1; } Debug.Log((object)("first word: " + firstWord + "; second word: " + secondWord)); List list = new List(); for (int i = 0; i < StartOfRound.Instance.mapScreen.radarTargets.Count; i++) { list.Add(StartOfRound.Instance.mapScreen.radarTargets[i].name); Debug.Log((object)$"name {i}: {list[i]}"); } string text = secondWord.ToLower(); for (int j = 0; j < list.Count; j++) { if (list[j].ToLower() == text) { return j; } } Debug.Log((object)$"Target names length: {list.Count}"); for (int k = 0; k < list.Count; k++) { Debug.Log((object)"A"); string text2 = list[k].ToLower(); Debug.Log((object)$"Word #{k}: {text2}; length: {text2.Length}"); for (int num = secondWord.Length; num > 2; num--) { Debug.Log((object)$"c: {num}"); Debug.Log((object)secondWord.Substring(0, num)); if (text2.StartsWith(secondWord.Substring(0, num))) { return k; } } } return -1; } [HarmonyPostfix] [HarmonyPatch("ParseWordOverrideOptions")] private static void ParseWordOverrideOptions(string playerWord, CompatibleNoun[] options, ref TerminalNode __result) { for (int i = 0; i < options.Length; i++) { for (int num = playerWord.Length; num > 0; num--) { if (getChineseCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { __result = options[i].result; return; } if (getPinyinCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { __result = options[i].result; return; } } } } [HarmonyPostfix] [HarmonyPatch("CheckForExactSentences")] private static void CheckForExactSentences(Terminal __instance, string playerWord, ref TerminalKeyword __result) { for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++) { if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; break; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; break; } } } private static string RemovePunctuation(string s) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in s) { if (!char.IsPunctuation(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString().ToLower(); } [HarmonyPostfix] [HarmonyPatch("CallFunctionInAccessibleTerminalObject")] private static void CallFunctionInAccessibleTerminalObject(Terminal __instance, string word) { TerminalAccessibleObject[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { if (getChineseCMD(array[i].objectCode).EqualsIgnoreCase(word) || getPinyinCMD(array[i].objectCode).EqualsIgnoreCase(word)) { Debug.Log((object)"Found accessible terminal object with corresponding string, calling function"); ((object)__instance).GetType().GetField("broadcastedCodeThisFrame", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(TranslateConfig.terminal, true); array[i].CallFunctionFromTerminal(); break; } } } [HarmonyPostfix] [HarmonyPatch("ParseWord")] private static void ParseWord(Terminal __instance, string playerWord, int specificityRequired, ref TerminalKeyword __result) { if (!TranslatePlugin.TerimalCanUseChinese.Value && !TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value) { return; } if (playerWord.Length < specificityRequired) { __result = null; return; } TerminalKeyword val = null; for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++) { if (__instance.terminalNodes.allKeywords[i].isVerb && (bool)hasGottenVerb.GetValue(__instance)) { continue; } bool accessTerminalObjects = __instance.terminalNodes.allKeywords[i].accessTerminalObjects; if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; return; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; return; } if (!((Object)(object)val == (Object)null)) { continue; } for (int num = playerWord.Length; num > specificityRequired; num--) { if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { val = __instance.terminalNodes.allKeywords[i]; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { val = __instance.terminalNodes.allKeywords[i]; } } } if ((Object)(object)val != (Object)null) { __result = val; } } [HarmonyPostfix] [HarmonyPatch("ParsePlayerSentence")] private static void customParser(Terminal __instance, ref TerminalNode __result) { string[] array = RemovePunctuation(__instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded)).Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["transmit"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["transmit"])))) { try { string text = array[1]; SignalTranslator val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && Time.realtimeSinceStartup - val.timeLastUsingSignalTranslator > 8f && text.Length > 1) { if (!((NetworkBehaviour)__instance).IsServer) { val.timeLastUsingSignalTranslator = Time.realtimeSinceStartup; } __result = __instance.terminalNodes.specialNodes[22]; HUDManager.Instance.UseSignalTranslatorServerRpc(text.Substring(0, Mathf.Min(text.Length, 10))); } return; } catch (Exception ex) { TranslatePlugin.logger.LogError((object)ex.Message); return; } } if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["switch"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["switch"])))) { int num = CheckForPlayerNameCommand(array[0], array[1]); if (num != -1) { StartOfRound.Instance.mapScreen.SwitchRadarTargetAndSync(num); __result = __instance.terminalNodes.specialNodes[20]; } } else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["ping"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["ping"])))) { int num2 = CheckForPlayerNameCommand(array[0], array[1]); if (num2 != -1) { StartOfRound.Instance.mapScreen.PingRadarBooster(num2); __result = __instance.terminalNodes.specialNodes[21]; } } else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["flash"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["flash"])))) { int num3 = CheckForPlayerNameCommand(array[0], array[1]); if (num3 != -1) { StartOfRound.Instance.mapScreen.FlashRadarBooster(num3); __result = __instance.terminalNodes.specialNodes[23]; } else if (StartOfRound.Instance.mapScreen.radarTargets[StartOfRound.Instance.mapScreen.targetTransformIndex].isNonPlayer) { StartOfRound.Instance.mapScreen.FlashRadarBooster(StartOfRound.Instance.mapScreen.targetTransformIndex); __result = __instance.terminalNodes.specialNodes[23]; } } } private static bool HaveChineseCMD(string name) { return TranslateConfig.cmd_zh.normal.ContainsKey(name); } private static bool HavePinyinCMD(string name) { return TranslateConfig.cmd_py.normal.ContainsKey(name); } private static string getChineseCMD(string name) { if (TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey(name)) { return TranslateConfig.cmd_zh.normal[name]; } return ""; } private static string getPinyinCMD(string name) { if (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey(name)) { return TranslateConfig.cmd_py.normal[name]; } return ""; } private static string getOrginByChineseCMD(string name) { if (TranslateConfig.cmd_zh.special.ContainsKey(name)) { return TranslateConfig.cmd_zh.special[name]; } return name; } private static string getOrginByPinyinCMD(string name) { if (TranslateConfig.cmd_py.special.ContainsKey(name)) { return TranslateConfig.cmd_py.special[name]; } return name; } private static T TransReflection(T tIn) { T val = Activator.CreateInstance(); Type typeFromHandle = typeof(T); foreach (FieldInfo runtimeField in typeFromHandle.GetRuntimeFields()) { typeFromHandle.GetRuntimeField(runtimeField.Name).SetValue(val, runtimeField.GetValue(tIn)); } return val; } public static TerminalKeyword Copy(TerminalKeyword oldOne) { return TransReflection(oldOne); } [HarmonyPostfix] [HarmonyPatch("LoadNewNode")] private static void changeNewNodeText(Terminal __instance, TerminalNode node) { if (info != null) { info.Reset(__instance.screenText.text); } } [HarmonyPrefix] [HarmonyPatch("OnSubmit")] private static void changeSubmit(Terminal __instance) { if (info != null) { texdAdded = __instance.currentText.Length - info.OriginalText.Length; if (texdAdded != 0) { info.Reset(__instance.currentText); } } } [HarmonyPostfix] [HarmonyPatch("Update")] private static void changeUpdateText(Terminal __instance) { try { if ((info == null || !info.IsTranslated) && info != null && !info.IsTranslated && TranslatePlugin.shouldTranslateTerimal.Value) { if (TranslatePlugin.showAvailableText.Value && !string.IsNullOrEmpty(__instance.currentText) && TextTranslate.ShouldOutputDebug("terminal:" + __instance.currentText)) { TranslatePlugin.LogInfo("[Debug] Terminal available text: '" + __instance.currentText + "'"); } string translatedText = TranslateConfig.replaceByMap(__instance.currentText, TranslateConfig.terminal); info.SetTranslatedText(translatedText); SetText(info.TranslatedText, __instance); info.OriginalText = __instance.currentText; } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)ex); } } public static void SetText(string text, Terminal Instance) { if (!((Object)(object)Instance == (Object)null)) { modifyingText.SetValue(Instance, true); ((Selectable)Instance.screenText).interactable = true; Instance.screenText.text = text; Instance.currentText = Instance.screenText.text; if ((Object)(object)Instance.screenText.verticalScrollbar != (Object)null) { Instance.screenText.verticalScrollbar.value = 0f; } } } [HarmonyPatch("Start")] [HarmonyPostfix] private static void startTerminal(Terminal __instance) { info = __instance.screenText.GetOrCreateTextTranslationInfo(); ig.Clear(); foreach (FieldInfo runtimeField in ((object)__instance).GetType().GetRuntimeFields()) { if (runtimeField.GetValue(__instance) != null && UnityTypes.TMP_Text.IsAssignableFrom(runtimeField.GetValue(__instance).GetType())) { ig.Add(runtimeField.GetValue(__instance)); } } info.MustIgnore = true; } } } namespace GameTranslator.Patches.Utils { internal static class ComponentExtensions { private static readonly string XuaIgnore = "XUAIGNORE"; private static readonly string XuaIgnoreTree = "XUAIGNORETREE"; private static bool _guiContentCheckFailed = false; public static bool SupportsStabilization(this object ui) { if (ui == null) { return false; } if (!_guiContentCheckFailed) { return !IsGUIContentSafe(ui); } return true; } public static bool IsSpammingComponent(this object ui) { if (ui != null) { if (!_guiContentCheckFailed) { return IsGUIContentSafe(ui); } return false; } return true; } public static bool ShouldIgnoreTextComponent(this object ui) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown Component val = (Component)((ui is Component) ? ui : null); if (val != null && (Object)(object)val != (Object)null) { Transform val2 = val.transform; if (((Object)val2).name.Contains(XuaIgnore)) { return true; } while ((Object)(object)val2.parent != (Object)null) { val2 = val2.parent; if (((Object)val2).name.Contains(XuaIgnoreTree)) { return true; } } Component val3 = null; if (GetTypeByName("UnityEngine.UI.InputField") != null) { val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("UnityEngine.UI.InputField")); if ((Object)(object)val3 != (Object)null) { PropertyInfo property = ((object)val3).GetType().GetProperty("placeholder"); if (property != null) { Component val4 = (Component)property.GetValue(val3); return val4 != val; } } } if (GetTypeByName("TMPro.TMP_InputField") != null) { val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("TMPro.TMP_InputField")); if ((Object)(object)val3 != (Object)null) { PropertyInfo property2 = ((object)val3).GetType().GetProperty("placeholder"); if (property2 != null) { Component val5 = (Component)property2.GetValue(val3); return val5 != val; } } } val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("UIInput")); return (Object)(object)val3 != (Object)null; } return false; } public static string GetPath(this object ui) { Component val = (Component)((ui is Component) ? ui : null); if (val != null && (Object)(object)val != (Object)null) { StringBuilder stringBuilder = new StringBuilder(); Transform val2 = val.transform; List list = new List(); while ((Object)(object)val2 != (Object)null) { list.Add(((Object)val2).name); val2 = val2.parent; } list.Reverse(); foreach (string item in list) { stringBuilder.Append("/").Append(item); } return stringBuilder.ToString(); } return "Unknown"; } private static bool IsGUIContentSafe(object ui) { try { return IsGUIContentUnsafe(ui); } catch { _guiContentCheckFailed = true; } return false; } private static bool IsGUIContentUnsafe(object ui) { return ui is GUIContent; } private static bool SetTextOnGUIContentSafe(object ui, string text) { try { return SetTextOnGUIContentUnsafe(ui, text); } catch { _guiContentCheckFailed = true; } return false; } private static bool SetTextOnGUIContentUnsafe(object ui, string text) { GUIContent val = (GUIContent)((ui is GUIContent) ? ui : null); if (val != null) { val.text = text; return true; } return false; } private static bool TryGetTextFromGUIContentSafe(object ui, out string text) { try { return TryGetTextFromGUIContentUnsafe(ui, out text); } catch { _guiContentCheckFailed = false; } text = null; return false; } private static bool TryGetTextFromGUIContentUnsafe(object ui, out string text) { GUIContent val = (GUIContent)((ui is GUIContent) ? ui : null); if (val != null) { text = val.text; return true; } text = null; return false; } private static Type GetTypeByName(string typeName) { try { return Type.GetType(typeName); } catch { return null; } } private static Component GetFirstComponentInSelfOrAncestor(this GameObject go, Type type) { if (type == null) { return null; } GameObject val = go; while ((Object)(object)val != (Object)null) { Component component = val.GetComponent(type); if ((Object)(object)component != (Object)null) { return component; } Transform transform = val.transform; object obj; if (transform == null) { obj = null; } else { Transform parent = transform.parent; obj = ((parent != null) ? ((Component)parent).gameObject : null); } val = (GameObject)obj; } return null; } private static string GetTextFromComponent(object ui, TextTranslationInfo info) { if (ui == null || info?.TextManipulator == null) { return null; } try { return info.TextManipulator.GetText(ui); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error in GetTextFromComponent: " + ex.Message)); return null; } } private static void SetTextOnComponent(object ui, string text, TextTranslationInfo info) { if (ui == null || info?.TextManipulator == null) { return; } try { info.TextManipulator.SetText(ui, text); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error in SetTextOnComponent: " + ex.Message)); } } } internal static class FontCache { private static bool _hasReadFallbackFontTextMeshPro; private static Object FallbackFontTextMeshPro; public static object GetOrCreateFallbackFontTextMeshPro() { if (!_hasReadFallbackFontTextMeshPro) { try { _hasReadFallbackFontTextMeshPro = true; if (string.IsNullOrEmpty(TranslatePlugin.fallbackFontTextMeshPro.Value)) { FallbackFontTextMeshPro = null; return null; } string assetBundle = Path.Combine(TranslatePlugin.DefaultPath, TranslatePlugin.fallbackFontTextMeshPro.Value); FallbackFontTextMeshPro = FontHelper.GetTextMeshProFont(assetBundle); } catch (Exception ex) when (ex.ToString().ToLowerInvariant().Contains("missing") || ex.ToString().ToLowerInvariant().Contains("not found")) { TranslatePlugin.logger.LogWarning((object)("An error occurred while loading text mesh pro fallback font. This may be due to missing font file. Error: " + ex.Message)); } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("An error occurred while loading text mesh pro fallback font: " + TranslatePlugin.fallbackFontTextMeshPro.Value + ". Error: " + ex2.Message)); } } return FallbackFontTextMeshPro; } } internal static class FontHelper { private static readonly List _loadedBundles = new List(); public static Object GetTextMeshProFont(string assetBundle) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown if (string.IsNullOrEmpty(assetBundle)) { return null; } Object val = null; string text = Path.Combine(Paths.GameRoot, assetBundle); if (File.Exists(text)) { TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from asset bundle: " + text)); AssetBundle val2 = null; if (AssetBundle_Methods.LoadFromFile != null) { val2 = (AssetBundle)AssetBundle_Methods.LoadFromFile.Invoke((object)null, new object[1] { text }); } else { if (AssetBundle_Methods.CreateFromFile == null) { TranslatePlugin.logger.LogError((object)("Could not find an appropriate asset bundle load method while loading font: " + text)); return null; } val2 = (AssetBundle)AssetBundle_Methods.CreateFromFile.Invoke((object)null, new object[1] { text }); } if ((Object)(object)val2 == (Object)null) { TranslatePlugin.logger.LogWarning((object)("Could not load asset bundle while loading font: " + text)); return null; } _loadedBundles.Add(val2); if (UnityTypes.TMP_FontAsset != null) { if (AssetBundle_Methods.LoadAllAssets != null) { val = ((Object[])AssetBundle_Methods.LoadAllAssets.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault(); } else if (AssetBundle_Methods.LoadAll != null) { val = ((Object[])AssetBundle_Methods.LoadAll.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault(); } } } else { TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from internal Resources API: " + text)); val = Resources.Load(assetBundle); } if (val != (Object)null) { CachedProperty version = TMP_FontAsset_Properties.Version; string text2 = ((string)((version != null) ? version.Get((object)val) : null)) ?? "Unknown"; TranslatePlugin.logger.LogInfo((object)("Loaded TextMesh Pro font uses version: " + text2)); Object.DontDestroyOnLoad(val); } else { TranslatePlugin.logger.LogError((object)("Could not find the TextMeshPro font asset: " + assetBundle)); } return val; } public static string[] GetOSInstalledFontNames() { return Font.GetOSInstalledFontNames(); } public static void UnloadAllBundles() { foreach (AssetBundle loadedBundle in _loadedBundles) { try { if ((Object)(object)loadedBundle != (Object)null) { loadedBundle.Unload(true); } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error unloading bundle: " + ex.Message)); } } _loadedBundles.Clear(); } } public static class FontSupportChecker { private static readonly ConcurrentDictionary _availableFonts = new ConcurrentDictionary(); private static readonly Dictionary _characterSupportCache = new Dictionary(); private static readonly LRUCache _textCache = new LRUCache(1000); private static bool _isInitialized = false; private static readonly object _lockObject = new object(); public static void InitializeFonts() { if (_isInitialized) { return; } lock (_lockObject) { if (_isInitialized) { return; } _availableFonts.Clear(); _characterSupportCache.Clear(); _textCache.Clear(); object orCreateFallbackFontTextMeshPro = FontCache.GetOrCreateFallbackFontTextMeshPro(); TMP_FontAsset val = (TMP_FontAsset)((orCreateFallbackFontTextMeshPro is TMP_FontAsset) ? orCreateFallbackFontTextMeshPro : null); if ((Object)(object)val != (Object)null) { AddFont(val); } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); TMP_FontAsset[] array2 = array; foreach (TMP_FontAsset val2 in array2) { if ((Object)(object)val2 != (Object)null && !_availableFonts.ContainsKey(val2)) { AddFont(val2); } } _isInitialized = true; TranslatePlugin.logger.LogInfo((object)$"FontSupportChecker initialized with {_availableFonts.Count} fonts"); } } private static void AddFont(TMP_FontAsset font) { if ((Object)(object)font == (Object)null || _availableFonts.ContainsKey(font)) { return; } _availableFonts.TryAdd(font, value: true); if (font.fallbackFontAssetTable == null) { return; } foreach (TMP_FontAsset item in font.fallbackFontAssetTable) { if ((Object)(object)item != (Object)null && !_availableFonts.ContainsKey(item)) { _availableFonts.TryAdd(item, value: true); } } } public static void RegisterFont(TMP_FontAsset font) { if ((Object)(object)font == (Object)null) { return; } lock (_lockObject) { AddFont(font); _characterSupportCache.Clear(); _textCache.Clear(); TranslatePlugin.logger.LogDebug((object)("Registered new font: " + ((Object)font).name)); } } public static bool IsCharacterSupported(char character) { if (!_isInitialized) { return true; } if (_characterSupportCache.TryGetValue(character, out var value)) { return value; } value = _availableFonts.Keys.Any((TMP_FontAsset font) => (Object)(object)font != (Object)null && font.HasCharacter(character, true, true)); _characterSupportCache[character] = value; return value; } public static string ReplaceUnsupportedCharacters(string text, TMP_Text textComponent = null) { if (string.IsNullOrEmpty(text) || !TranslatePlugin.replaceUnsupportedCharacters.Value) { return text; } if (!_isInitialized) { InitializeFonts(); } if (_textCache.TryGetValue(text, out var value)) { return value; } bool flag = true; foreach (char character in text) { if (!IsCharacterSupported(character)) { flag = false; break; } } if (flag) { _textCache.Add(text, text); return text; } StringBuilder stringBuilder = new StringBuilder(); bool flag2 = false; foreach (char c in text) { if (char.IsControl(c)) { stringBuilder.Append(c); continue; } if (IsCharacterSupported(c)) { stringBuilder.Append(c); continue; } stringBuilder.Append('□'); flag2 = true; } string text2 = stringBuilder.ToString(); if (flag2 && TranslatePlugin.showOtherDebug.Value) { TranslatePlugin.logger.LogInfo((object)("[FontSupport] Replaced unsupported characters in text: '" + text + "' -> '" + text2 + "'")); } _textCache.Add(text, text2); return text2; } public static void ClearCache() { lock (_lockObject) { _characterSupportCache.Clear(); _textCache.Clear(); TranslatePlugin.logger.LogDebug((object)"FontSupportChecker cache cleared"); } } public static string GetStats() { return $"Fonts: {_availableFonts.Count}, CharacterCache: {_characterSupportCache.Count}, TextCache: {_textCache.Count}"; } } public class LRUCache { private class CacheItem { public TKey Key { get; set; } public TValue Value { get; set; } } private readonly int _capacity; private readonly Dictionary> _cacheMap; private readonly LinkedList _lruList; public int Count => _cacheMap.Count; public LRUCache(int capacity) { _capacity = capacity; _cacheMap = new Dictionary>(capacity); _lruList = new LinkedList(); } public bool TryGetValue(TKey key, out TValue value) { if (_cacheMap.TryGetValue(key, out var value2)) { value = value2.Value.Value; _lruList.Remove(value2); _lruList.AddFirst(value2); return true; } value = default(TValue); return false; } public void Add(TKey key, TValue value) { if (_cacheMap.TryGetValue(key, out var value2)) { _lruList.Remove(value2); } else if (_cacheMap.Count >= _capacity) { RemoveLeastRecentlyUsed(); } LinkedListNode linkedListNode = new LinkedListNode(new CacheItem { Key = key, Value = value }); _lruList.AddFirst(linkedListNode); _cacheMap[key] = linkedListNode; } public void Clear() { _cacheMap.Clear(); _lruList.Clear(); } private void RemoveLeastRecentlyUsed() { LinkedListNode last = _lruList.Last; if (last != null) { _cacheMap.Remove(last.Value.Key); _lruList.RemoveLast(); } } } internal sealed class SafeFileWatcher : IDisposable { private int _counter; private FileSystemWatcher _watcher; private bool _disposed; private object _sync = new object(); private Timer _timer; private readonly string _directory; public event Action DirectoryUpdated; public SafeFileWatcher(string directory) { _directory = directory; _timer = new Timer(RaiseEvent, null, -1, -1); EnableWatcher(); } public void EnableWatcher() { if (_watcher == null) { _watcher = new FileSystemWatcher(_directory); _watcher.Changed += Watcher_Changed; _watcher.Created += Watcher_Created; _watcher.Deleted += Watcher_Deleted; _watcher.EnableRaisingEvents = true; } } public void DisableWatcher() { if (_watcher != null) { _watcher.EnableRaisingEvents = false; _watcher.Dispose(); _watcher = null; } } public void RaiseEvent(object state) { this.DirectoryUpdated?.Invoke(); } public void Disable() { int num = Interlocked.Increment(ref _counter); UpdateRaisingEvents(num == 0); } public void Enable() { int num = Interlocked.Decrement(ref _counter); UpdateRaisingEvents(num == 0); } private void Watcher_Deleted(object sender, FileSystemEventArgs e) { _timer.Change(1000, -1); } private void Watcher_Created(object sender, FileSystemEventArgs e) { FileInfo file = new FileInfo(e.FullPath); WaitForFile(file); _timer.Change(1000, -1); } private void Watcher_Changed(object sender, FileSystemEventArgs e) { _timer.Change(1000, -1); } private void UpdateRaisingEvents(bool enabled) { lock (_sync) { if (enabled) { EnableWatcher(); } else { DisableWatcher(); } } } private void WaitForFile(FileInfo file) { while (IsFileLocked(file)) { Thread.Sleep(100); } } private bool IsFileLocked(FileInfo file) { try { using (file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } } catch (IOException) { return true; } return false; } private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { _watcher?.Dispose(); _watcher = null; _timer.Dispose(); } _disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal static class SceneManagerLoader { public static void EnableSceneLoadScanInternal(Action sceneLoaded) { SceneManager.sceneLoaded += delegate(Scene arg1, LoadSceneMode arg2) { sceneLoaded(((Scene)(ref arg1)).buildIndex); }; } } public class StringBuffer { private char[] value; private int length; private int capacity; private const int DEFAULT_CAPACITY = 16; public int Length { get { return length; } set { if (value < 0 || value > capacity) { throw new ArgumentOutOfRangeException("value"); } if (value < length) { Array.Clear(this.value, value, length - value); } length = value; } } public int Capacity { get { return capacity; } set { if (value < length) { throw new ArgumentOutOfRangeException("value"); } if (value != capacity) { char[] destinationArray = new char[value]; Array.Copy(this.value, 0, destinationArray, 0, length); this.value = destinationArray; capacity = value; } } } public StringBuffer() { value = new char[16]; length = 0; capacity = 16; } public StringBuffer(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity"); } value = new char[capacity]; length = 0; this.capacity = capacity; } public StringBuffer(string str) { if (str == null) { throw new ArgumentNullException("str"); } value = new char[str.Length + 16]; str.CopyTo(0, value, 0, str.Length); length = str.Length; capacity = str.Length + 16; } public void EnsureCapacity(int minimumCapacity) { if (minimumCapacity < 0) { throw new ArgumentOutOfRangeException("minimumCapacity"); } if (minimumCapacity > capacity) { int num = capacity * 2; if (num < minimumCapacity) { num = minimumCapacity; } Capacity = num; } } public StringBuffer Append(object obj) { if (obj == null) { return this; } return Append(obj.ToString()); } public StringBuffer Append(string str) { if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); str.CopyTo(0, value, length, num); length += num; return this; } public StringBuffer Append(char c) { EnsureCapacity(length + 1); value[length] = c; length++; return this; } public StringBuffer Insert(int index, object obj) { if (obj == null) { return this; } return Insert(index, obj.ToString()); } public StringBuffer Insert(int index, string str) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); Array.Copy(value, index, value, index + num, length - index); str.CopyTo(0, value, index, num); length += num; return this; } public StringBuffer Insert(int index, char c) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } EnsureCapacity(length + 1); Array.Copy(value, index, value, index + 1, length - index); value[index] = c; length++; return this; } public StringBuffer Remove(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } Array.Copy(value, startIndex + length, value, startIndex, this.length - startIndex - length); Array.Clear(value, this.length - length, length); this.length -= length; return this; } public StringBuffer Replace(char oldChar, char newChar) { for (int i = 0; i < length; i++) { if (value[i] == oldChar) { value[i] = newChar; } } return this; } public StringBuffer Replace(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOf(oldValue); num3 >= 0; num3 = IndexOf(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } public int IndexOf(char c) { return IndexOf(c, 0, length); } public int IndexOf(char c, int startIndex) { return IndexOf(c, startIndex, length - startIndex); } public int IndexOf(char c, int startIndex, int count) { if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return Array.IndexOf(value, c, startIndex, count); } public int IndexOf(string str) { return IndexOf(str, 0, length); } public int IndexOf(string str, int startIndex) { return IndexOf(str, startIndex, length - startIndex); } public int IndexOf(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return value.ToString(startIndex, count).IndexOf(str); } public string Substring(int startIndex) { return Substring(startIndex, length - startIndex); } public string Substring(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } return new string(value, startIndex, length); } public bool Contains(string text) { return IndexOf(text) >= 0; } public StringBuffer ReplaceFull(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOfWord(oldValue); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } private int IndexOfWord(string str) { return IndexOfWord(str, 0, length); } private int IndexOfWord(string str, int startIndex) { return IndexOfWord(str, startIndex, length - startIndex); } public int IndexOfWord(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } int num = str.Length; int[] array = new int[num]; int num2 = 0; computeLPSArray(str, num, array); int num3 = startIndex; while (num3 < startIndex + count) { if (str[num2] == value[num3]) { num2++; num3++; } if (num2 == num) { if ((num3 - num2 == 0 || !char.IsLetter(value[num3 - num2 - 1])) && (num3 == length || !char.IsLetter(value[num3]))) { return num3 - num2; } num2 = array[num2 - 1]; } else if (num3 < startIndex + count && str[num2] != value[num3]) { if (num2 != 0) { num2 = array[num2 - 1]; } else { num3++; } } } return -1; } private void computeLPSArray(string str, int M, int[] lps) { int num = 0; int num2 = 1; lps[0] = 0; while (num2 < M) { if (str[num2] == str[num]) { num = (lps[num2] = num + 1); num2++; } else if (num != 0) { num = lps[num - 1]; } else { lps[num2] = num; num2++; } } } private static bool IsWordChar(char c) { if (!char.IsLetter(c)) { return c == '_'; } return true; } public StringBuffer Clear() { Length = 0; return this; } public override string ToString() { return new string(value, 0, length); } } public static class StringBuilderExtensions { public static bool EndsWithWhitespaceOrNewline(this StringBuilder builder) { if (builder.Length == 0) { return true; } char c = builder[builder.Length - 1]; if (!char.IsWhiteSpace(c)) { return c == '\n'; } return true; } internal static StringBuilder Reverse(this StringBuilder text) { if (text.Length > 1) { int num = text.Length / 2; for (int i = 0; i < num; i++) { int index = text.Length - (i + 1); char value = text[i]; char value2 = text[index]; text[i] = value2; text[index] = value; } } return text; } } internal static class TemplatingHelper { public static bool ContainsUntemplatedCharacters(string text) { bool flag = false; int length = text.Length; for (int i = 0; i < length; i++) { char c = text[i]; if (flag) { if (c == '}') { int num = i + 1; if (num < length && text[num] == '}') { i++; flag = false; } } } else if (c == '{') { int num2 = i + 1; if (num2 < length && text[num2] == '{') { i++; flag = true; } } else if (!char.IsWhiteSpace(c)) { return true; } } return false; } public static TemplatedString TemplatizeByReplacementsAndNumbers(this string str) { TemplatedString templatedString = str.TemplatizeByReplacements(); if (templatedString == null) { return str.TemplatizeByNumbers(); } return templatedString.Template.TemplatizeByNumbers(templatedString); } public static TemplatedString TemplatizeByReplacements(this string str) { return null; } public static bool IsNumberOrDotOrControl(char c) { if ('*' > c || c > ':') { if ('0' <= c) { return c <= '9'; } return false; } return true; } public static bool IsNumber(char c) { if ('0' > c || c > '9') { if ('0' <= c) { return c <= '9'; } return false; } return true; } public static TemplatedString TemplatizeByNumbers(this string str, TemplatedString existingTemplatedString = null) { int num = 0; if (existingTemplatedString != null) { num = existingTemplatedString.Arguments?.Count ?? 0; str = existingTemplatedString.Template ?? str; } Dictionary dictionary = new Dictionary(); bool flag = false; StringBuilder stringBuilder = null; char c = (char)(65 + num); int num2 = -1; int num3 = -1; for (int i = 0; i < str.Length; i++) { char c2 = str[i]; if (flag) { if (IsNumber(c2)) { num3 = i; } if (IsNumberOrDotOrControl(c2)) { stringBuilder.Append(c2); continue; } int num4 = i - num3 - 1; stringBuilder.Remove(stringBuilder.Length - num4, num4); string text = stringBuilder.ToString(); string text2 = "{{" + c + "}}"; dictionary.Add(text2, text); c = (char)(c + 1); stringBuilder = null; flag = false; str = str.Remove(num2, num3 - num2 + 1).Insert(num2, text2); i += text2.Length - text.Length; } else if (IsNumber(c2)) { flag = true; stringBuilder = new StringBuilder(); stringBuilder.Append(c2); num2 = i; num3 = i; } } if (stringBuilder != null) { int num5 = str.Length - num3 - 1; stringBuilder.Remove(stringBuilder.Length - num5, num5); string value = stringBuilder.ToString(); string text3 = "{{" + c + "}}"; dictionary.Add(text3, value); str = str.Remove(num2, str.Length - num2 - num5).Insert(num2, text3); } if (dictionary.Count > 0) { Dictionary dictionary2 = existingTemplatedString?.Arguments ?? dictionary; if (dictionary != dictionary2) { foreach (KeyValuePair item in dictionary) { dictionary2.Add(item.Key, item.Value); } } return new TemplatedString(str, dictionary2); } return existingTemplatedString; } } internal static class TextHelper { public static string Encode(string text) { return EscapeNewlines(text); } public static string[] ReadTranslationLineAndDecode(string str) { if (string.IsNullOrEmpty(str)) { return null; } string[] array = new string[2]; int num = 0; bool flag = false; int length = str.Length; StringBuilder stringBuilder = new StringBuilder((int)((double)length / 1.3)); for (int i = 0; i < length; i++) { char c = str[i]; if (flag) { char c2 = c; if (c2 <= '\\') { if (c2 != '=' && c2 != '\\') { stringBuilder.Append('\\'); stringBuilder.Append(c); flag = false; continue; } stringBuilder.Append(c); } else { switch (c2) { default: stringBuilder.Append('\\'); stringBuilder.Append(c); flag = false; continue; case 'u': { if (i + 4 >= length) { throw new Exception("Found invalid unicode in line: " + str); } if (i + 1 >= length || i + 2 >= length || i + 3 >= length || i + 4 >= length) { throw new Exception("Invalid unicode escape sequence at position " + i + " in line: " + str); } int num2 = int.Parse(new string(new char[4] { str[i + 1], str[i + 2], str[i + 3], str[i + 4] }), NumberStyles.HexNumber); stringBuilder.Append((char)num2); i += 4; break; } case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; } } flag = false; continue; } switch (c) { case '\\': flag = true; break; case '=': if (num > 1) { return null; } array[num++] = stringBuilder.ToString(); stringBuilder.Length = 0; break; case '%': if (i + 2 < length && i + 1 < length && i + 2 < length && str[i + 1] == '3' && str[i + 2] == 'D') { stringBuilder.Append('='); i += 2; } else { stringBuilder.Append(c); } break; case '/': { int num3 = i + 1; if (num3 < length && num3 < length && str[num3] == '/') { array[num++] = stringBuilder.ToString(); if (num == 2) { return array; } return null; } stringBuilder.Append(c); break; } default: stringBuilder.Append(c); break; } } if (num != 1) { return null; } array[num++] = stringBuilder.ToString(); return array; } internal static string EscapeNewlines(string str) { if (str == null || str.Length == 0) { return ""; } int length = str.Length; StringBuilder stringBuilder = new StringBuilder(length + 4); int num = 0; while (num < length) { char c = str[num]; char c2 = c; if (c2 <= '\r') { switch (c2) { default: stringBuilder.Append(c); num++; continue; case '\r': stringBuilder.Append("\\r"); break; case '\n': stringBuilder.Append("\\n"); break; } } else { switch (c2) { default: stringBuilder.Append(c); num++; continue; case '\\': stringBuilder.Append('\\'); stringBuilder.Append(c); break; case '=': stringBuilder.Append('\\'); stringBuilder.Append(c); break; case '/': { int num2 = num + 1; if (num2 < length && str[num2] == '/') { stringBuilder.Append('\\'); stringBuilder.Append(c); stringBuilder.Append('\\'); stringBuilder.Append(c); num++; } else { stringBuilder.Append(c); } break; } } } num++; } return stringBuilder.ToString(); } } internal class TextTranslate { private static readonly Dictionary _debugOutputCache = new Dictionary(); private static readonly TimeSpan _debugOutputInterval = TimeSpan.FromSeconds(10.0); private static readonly TimeSpan _cacheCleanupInterval = TimeSpan.FromMinutes(5.0); private static DateTime _lastCleanupTime = DateTime.Now; public static TextTranslate Instance = new TextTranslate(); public static long ChangeTime = 0L; public static bool ShouldOutputDebug(string text) { if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value) { return true; } DateTime now = DateTime.Now; if (now - _lastCleanupTime > _cacheCleanupInterval) { CleanupDebugCache(); _lastCleanupTime = now; } if (_debugOutputCache.TryGetValue(text, out var value) && now - value < _debugOutputInterval) { return false; } _debugOutputCache[text] = now; return true; } private static void CleanupDebugCache() { if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value) { _debugOutputCache.Clear(); return; } DateTime now = DateTime.Now; List list = new List(); foreach (KeyValuePair item in _debugOutputCache) { if (now - item.Value > _cacheCleanupInterval) { list.Add(item.Key); } } foreach (string item2 in list) { _debugOutputCache.Remove(item2); } if (TranslatePlugin.showOtherDebug.Value && list.Count > 0) { TranslatePlugin.logger.LogInfo((object)$"[Debug] Cleaned up {list.Count} old debug cache entries"); } } internal void Hook_TextChanged(object ui) { if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value) { try { Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator"); if (type != null) { FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is HashSet hashSet && hashSet.Contains(ui)) { return; } } } catch { } } TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo); if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value) { string text = ui.GetText(orCreateTextTranslationInfo); string text2 = TranslateOrQueue(ui, text, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState); if (!string.IsNullOrEmpty(text2) && !text2.Equals(text) && IsUIObjectValid(ui)) { SetText(ui, text2, isTranslated: true, text, orCreateTextTranslationInfo); } } } internal void Hook_TextChanged(object ui, ref string value) { if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value) { try { Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator"); if (type != null) { FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is HashSet hashSet && hashSet.Contains(ui)) { return; } } } catch { } } TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo); if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value) { string text = TranslateOrQueue(ui, value, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState); if (!string.IsNullOrEmpty(text) && !text.Equals(value) && IsUIObjectValid(ui)) { value = text; } } } public string TranslateOrQueue(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { if (info != null && (info.IsCurrentlySettingText || info.MustIgnore)) { return null; } text = text ?? ui.GetText(info); if (Utility.IsNullOrWhiteSpace(text)) { return null; } if (info != null && info.IsTranslated) { if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime) { info.Reset(text); } else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text)) { return info.TranslatedText; } } string cachedTranslation = AsyncTranslationManager.Instance.GetCachedTranslation(text, config); if (cachedTranslation != null) { if (TranslatePlugin.showOtherDebug.Value && ShouldOutputDebug("cached:" + text)) { TranslatePlugin.logger.LogInfo((object)("[Debug] Cached translation found for: '" + text + "' -> '" + cachedTranslation + "'")); } if (info != null) { info.OriginalText = text; info.SetTranslatedText(cachedTranslation); } if (!IsUIObjectValid(ui)) { return null; } try { if (info != null) { info.IsCurrentlySettingText = true; } ui.SetText(cachedTranslation, info); } catch (NullReferenceException) { } catch (IndexOutOfRangeException ex2) { TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in cached translation: " + ex2.Message)); } catch (Exception ex3) { TranslatePlugin.logger.LogError((object)("Exception in cached translation: " + ex3.Message)); } finally { if (info != null) { info.IsCurrentlySettingText = false; } } return cachedTranslation; } if (normalText == null || normalText.IsTranslatable(text, isToken: false)) { if (text.Length <= TranslatePlugin.syncTranslationThreshold.Value) { string text2 = TranslateImmediate(ui, text, info, normalText, config, ignoreComponentState); if (text2 != null) { AsyncTranslationManager.Instance.GetCachedTranslation(text, config); return text2; } } else { if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("queued:" + text)) { TranslatePlugin.logger.LogInfo((object)("[Debug] Queued available text: '" + text + "'")); } AsyncTranslationManager.Instance.QueueTranslation(ui, text, info, normalText, config, ignoreComponentState); if (info != null && info.IsTranslated && info.TranslatedText != null) { return info.TranslatedText; } } } return null; } public string TranslateImmediate(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { if (info != null && (info.IsCurrentlySettingText || info.MustIgnore)) { return null; } text = text ?? ui.GetText(info); if (Utility.IsNullOrWhiteSpace(text)) { return null; } if (info != null && info.IsTranslated) { if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime) { info.Reset(text); } else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text)) { return info.TranslatedText; } } string text2 = null; if ((normalText == null || normalText.IsTranslatable(text, isToken: false)) && (ignoreComponentState || ui.IsComponentActive())) { if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value) { if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("available:" + text)) { TranslatePlugin.logger.LogInfo((object)("[Debug] Found available text: '" + text + "'")); } text2 = normalText.TryTranslate(text); } if (text2 != null) { text2 = TranslateConfig.replaceByMap(text2, config); if (info != null) { info.OriginalText = text; info.SetTranslatedText(text2); } } } return text2; } internal void SetTranslatedText(object ui, string translatedText, string originalText, TextTranslationInfo info) { if (info != null) { info.OriginalText = originalText; info.SetTranslatedText(translatedText); } if (!IsUIObjectValid(ui)) { return; } try { if (info != null) { info.IsCurrentlySettingText = true; } ui.SetText(translatedText, info); } catch (NullReferenceException) { } catch (IndexOutOfRangeException ex2) { TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in SetTranslatedText: " + ex2.Message)); } catch (Exception ex3) { TranslatePlugin.logger.LogError((object)("Exception in SetTranslatedText: " + ex3.Message)); } finally { if (info != null) { info.IsCurrentlySettingText = false; } } } private void SetText(object ui, string text, bool isTranslated, string originalText, TextTranslationInfo info) { if ((info != null && info.IsCurrentlySettingText) || !IsUIObjectValid(ui)) { return; } try { if (info != null) { info.IsCurrentlySettingText = true; } ui.SetText(text, info); } catch (NullReferenceException) { } catch (IndexOutOfRangeException ex2) { TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in SetText: " + ex2.Message)); } catch (Exception ex3) { TranslatePlugin.logger.LogError((object)("Exception in SetText: " + ex3.Message)); } finally { if (info != null) { info.IsCurrentlySettingText = false; } } } private bool IsUIObjectValid(object ui) { if (ui == null) { return false; } try { Component val = (Component)((ui is Component) ? ui : null); if (val != null && Object.op_Implicit((Object)(object)val)) { GameObject gameObject = val.gameObject; if (Object.op_Implicit((Object)(object)gameObject)) { Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); if (val2 != null) { return gameObject.activeInHierarchy && val2.enabled; } return gameObject.activeInHierarchy; } } return ui != null; } catch { return false; } } public bool DiscoverComponent(object ui, TextTranslationInfo info) { if (info != null && TranslatePlugin.changeFont.Value) { try { bool flag = ui.IsComponentActive(); if (TranslatePlugin.fallbackFontTextMeshPro.Value != null && flag) { info.ChangeFont(ui); return true; } return flag; } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; string text = "An error occurred while processing the UI."; string newLine = Environment.NewLine; logger.LogWarning((object)(text + newLine + ex)); } return false; } return true; } } public class TextureTranslate { public static TextureTranslate Instance = new TextureTranslate(); public static bool ImageHooksEnabled = true; internal void Hook_ImageChangedOnComponent(object source, ref Texture2D texture, bool isPrefixHooked, bool onEnable = false) { if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && source.IsKnownImageType()) { Sprite sprite = null; HandleImage(source, ref sprite, ref texture, isPrefixHooked); } } internal void Hook_ImageChangedOnComponent(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked, bool onEnable) { if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && source.IsKnownImageType()) { HandleImage(source, ref sprite, ref texture, isPrefixHooked); } } internal void Hook_ImageChanged(ref Texture2D texture, bool isPrefixHooked) { if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && !((Object)(object)texture == (Object)null)) { Sprite sprite = null; HandleImage(null, ref sprite, ref texture, isPrefixHooked); } } public void HandleImage(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked) { try { if (ShouldProcessTexture(source, texture)) { TranslateTexture(source, ref sprite, ref texture, isPrefixHooked); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while translating texture."); } } private void TranslateTexture(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked) { try { ImageHooksEnabled = false; Texture2D val = texture; texture = texture ?? source.GetTexture(); if ((Object)(object)texture == (Object)null) { return; } TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo(); string key = orCreateTextureTranslationInfo.GetKey(); if (string.IsNullOrEmpty(key)) { return; } if (TranslateConfig.cache != null) { TranslateConfig.cache.UpdateTextureStatistics(key); } if (TranslateConfig.cache.TryGetTranslatedImage(key, out var data, out var image)) { bool flag = texture.IsCompatible(image.ImageFormat); if (!orCreateTextureTranslationInfo.IsTranslated) { try { if (flag) { texture.LoadImageEx(data, image.ImageFormat, null); } else { orCreateTextureTranslationInfo.CreateTranslatedTexture(data, image.ImageFormat); } } finally { orCreateTextureTranslationInfo.IsTranslated = true; } } if (source != null && !orCreateTextureTranslationInfo.IsTranslated) { if (!flag) { Sprite val2 = source.SetTexture(orCreateTextureTranslationInfo.Translated, sprite, isPrefixHooked); if ((Object)(object)val2 != (Object)null) { orCreateTextureTranslationInfo.TranslatedSprite = val2; if (isPrefixHooked && (Object)(object)sprite != (Object)null) { sprite = val2; } } } if (!isPrefixHooked) { source.SetAllDirtyEx(); } } } if ((Object)(object)val == (Object)null) { texture = null; } else if (orCreateTextureTranslationInfo.UsingReplacedTexture) { if (orCreateTextureTranslationInfo.IsTranslated) { Texture2D translated = orCreateTextureTranslationInfo.Translated; if ((Object)(object)translated != (Object)null) { texture = translated; } } else { Texture2D target = orCreateTextureTranslationInfo.Original.Target; if ((Object)(object)target != (Object)null) { texture = target; } } } else { texture = val; } } catch (FileNotFoundException ex) { XuaLogger.AutoTranslator.Warn("Texture file not found: " + ex.FileName); } catch (FormatException ex2) { XuaLogger.AutoTranslator.Error((Exception)ex2, "Invalid image format."); } catch (Exception ex3) { XuaLogger.AutoTranslator.Error(ex3, "An unexpected error occurred while translating texture."); } finally { ImageHooksEnabled = true; } } private bool ShouldProcessTexture(object source, Texture2D texture) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected I4, but got Unknown if ((Object)(object)texture == (Object)null && source == null) { return false; } if ((Object)(object)texture != (Object)null) { TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo(); if (orCreateTextureTranslationInfo.IsTranslated && (Object)(object)orCreateTextureTranslationInfo.Translated != (Object)null) { return false; } int num = (int)texture.format; if (num == 1 || num == 9 || num == 63) { return false; } } return true; } private bool IsTextureFormatCompatible(Texture2D texture, TranslateExtensions.ImageFormat format) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 TextureFormat format2 = texture.format; switch (format) { case TranslateExtensions.ImageFormat.TGA: if ((int)format2 != 5 && (int)format2 != 4) { return (int)format2 == 3; } return true; default: return false; case TranslateExtensions.ImageFormat.PNG: return true; } } } internal static class TranslationScopeHelper { internal static class TranslationScopes { public const int None = -1; } public static bool EnableTranslationScoping = true; public static int GetScope(object ui) { if (EnableTranslationScoping) { try { Component val = (Component)((ui is Component) ? ui : null); if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val)) { return GetScopeFromComponent(val); } if (ui is GUIContent) { return -1; } return GetActiveSceneId(); } catch (MissingMemberException ex) { XuaLogger.AutoTranslator.Error((Exception)ex, "A 'missing member' error occurred while retriving translation scope. Disabling translation scopes."); EnableTranslationScoping = false; } return -1; } return -1; } private static int GetScopeFromComponent(Component component) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).buildIndex; } public static int GetActiveSceneId() { return GetActiveSceneIdBySceneManager(); } private static int GetActiveSceneIdBySceneManager() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).buildIndex; } public static void RegisterSceneLoadCallback(Action sceneLoaded) { SceneManager_Methods.add_sceneLoaded(delegate(Scene scene, LoadSceneMode mode) { sceneLoaded(((Scene)(ref scene)).buildIndex); }); SceneManagerLoader.EnableSceneLoadScanInternal(sceneLoaded); } } } namespace GameTranslator.Patches.Utils.Textures { internal interface ITextureLoader { void Load(Texture2D texture, byte[] data); bool Verify(); } internal class LoadImageImageLoader : ITextureLoader { public void Load(Texture2D texture, byte[] data) { if (ImageConversion_Methods.LoadImage != null) { ImageConversion_Methods.LoadImage(texture, data, arg3: false); } else if (Texture2D_Methods.LoadImage != null) { Texture2D_Methods.LoadImage(texture, data); } } public bool Verify() { if (Texture2D_Methods.LoadImage == null) { return ImageConversion_Methods.LoadImage != null; } return true; } } internal static class TextureLoader { private static readonly Dictionary Loaders; static TextureLoader() { Loaders = new Dictionary(); Register(TranslateExtensions.ImageFormat.PNG, new LoadImageImageLoader()); Register(TranslateExtensions.ImageFormat.TGA, new TgaImageLoader()); } public static bool Register(TranslateExtensions.ImageFormat format, ITextureLoader loader) { try { if (loader.Verify()) { Loaders[format] = loader; return true; } } catch (Exception ex) { XuaLogger.AutoTranslator.Warn(ex, "An image loader could not be registered."); } return false; } public static void Load(Texture2D texture, byte[] data, TranslateExtensions.ImageFormat imageFormat) { if (Loaders.TryGetValue(imageFormat, out var value)) { value.Load(texture, data); } } } internal class TgaImageLoader : ITextureLoader { public void Load(Texture2D texture, byte[] data) { //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null && data == null) { return; } TextureFormat format = texture.format; using MemoryStream input = new MemoryStream(data); using BinaryReader binaryReader = new BinaryReader(input); binaryReader.BaseStream.Seek(12L, SeekOrigin.Begin); short num = binaryReader.ReadInt16(); short num2 = binaryReader.ReadInt16(); int num3 = binaryReader.ReadByte(); binaryReader.BaseStream.Seek(1L, SeekOrigin.Current); Color32[] array = (Color32[])(object)new Color32[num * num2]; if ((int)format == 3) { if (num3 == 32) { for (int i = 0; i < num * num2; i++) { byte b = binaryReader.ReadByte(); byte b2 = binaryReader.ReadByte(); byte b3 = binaryReader.ReadByte(); binaryReader.ReadByte(); array[i] = new Color32(b3, b2, b, byte.MaxValue); } } else { for (int j = 0; j < num * num2; j++) { byte b4 = binaryReader.ReadByte(); byte b5 = binaryReader.ReadByte(); byte b6 = binaryReader.ReadByte(); array[j] = new Color32(b6, b5, b4, byte.MaxValue); } } } else if (num3 == 32) { for (int k = 0; k < num * num2; k++) { byte b7 = binaryReader.ReadByte(); byte b8 = binaryReader.ReadByte(); byte b9 = binaryReader.ReadByte(); byte b10 = binaryReader.ReadByte(); array[k] = new Color32(b9, b8, b7, b10); } } else { for (int l = 0; l < num * num2; l++) { byte b11 = binaryReader.ReadByte(); byte b12 = binaryReader.ReadByte(); byte b13 = binaryReader.ReadByte(); array[l] = new Color32(b13, b12, b11, byte.MaxValue); } } texture.SetPixels32(array); texture.Apply(); } public bool Verify() { Load(null, null); return true; } } } namespace GameTranslator.Patches.Translatons { internal class AsyncTranslationManager { [CompilerGenerated] private sealed class d__17 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public AsyncTranslationManager <>4__this; public object ui; public TextTranslationInfo info; public float delay; public Action onTextStabilized; public int currentTries; public int maxTries; public Action onMaxTriesExceeded; private bool 5__2; private string 5__3; private float 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__3 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; AsyncTranslationManager asyncTranslationManager = <>4__this; float realtimeSinceStartup; string text; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; 5__2 = false; goto IL_013c; case 2: { <>1__state = -1; goto IL_00be; } IL_013c: if (currentTries >= maxTries) { break; } 5__3 = null; text = null; try { 5__3 = asyncTranslationManager.GetUIText(ui, info); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error getting before text during stabilization: " + ex.Message)); break; } realtimeSinceStartup = Time.realtimeSinceStartup; 5__4 = realtimeSinceStartup + delay; goto IL_00be; IL_00be: if (Time.realtimeSinceStartup < 5__4) { <>2__current = null; <>1__state = 2; return true; } try { text = asyncTranslationManager.GetUIText(ui, info); } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Error getting after text during stabilization: " + ex2.Message)); break; } if (5__3 == text) { onTextStabilized(text); 5__2 = true; break; } currentTries++; 5__3 = null; goto IL_013c; } if (!5__2) { onMaxTriesExceeded(); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private readonly TranslationManager _translationManager; private readonly ConcurrentQueue _mainThreadActions; private readonly ConcurrentDictionary _immediatelyTranslating; private readonly Dictionary _stabilizationContexts; private readonly ConcurrentDictionary> _pendingStabilizationUIs; private bool _temporarilyDisabled; public static AsyncTranslationManager Instance { get; } = new AsyncTranslationManager(); private AsyncTranslationManager() { _translationManager = new TranslationManager(); _mainThreadActions = new ConcurrentQueue(); _immediatelyTranslating = new ConcurrentDictionary(); _stabilizationContexts = new Dictionary(); _pendingStabilizationUIs = new ConcurrentDictionary>(); _translationManager.JobCompleted += OnTranslationJobCompleted; _translationManager.JobFailed += OnTranslationJobFailed; TranslationEndpointManager translationEndpointManager = new TranslationEndpointManager(); _translationManager.RegisterEndpoint(translationEndpointManager); } public void Start() { } public void Stop() { _translationManager?.ClearAllJobs(); _stabilizationContexts.Clear(); } public bool IsTemporarilyDisabled() { return _temporarilyDisabled; } public string GetCachedTranslation(string originalText, TranslateConfig.TranslateConfigFile config) { if (_translationManager?.PrimaryEndpoint == null) { return null; } string cacheKey = GetCacheKey(originalText, config); if (!_translationManager.PrimaryEndpoint.TryGetTranslation(cacheKey, out var value)) { return null; } return value; } public void QueueTranslation(object ui, string originalText, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { if (string.IsNullOrEmpty(originalText) || string.IsNullOrWhiteSpace(originalText) || _temporarilyDisabled) { return; } try { if (info != null) { if (info.IsTranslated) { info.Reset(originalText); } else { info.OriginalText = originalText; } } string cachedTranslation = GetCachedTranslation(originalText, config); if (cachedTranslation != null) { if (IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, cachedTranslation, originalText, info, TextTranslate.ChangeTime); }); } } else if (originalText.Length <= TranslatePlugin.syncTranslationThreshold.Value) { string translatedText = TranslateText(originalText, normalText, config); if (string.IsNullOrEmpty(translatedText) || translatedText.Equals(originalText)) { return; } string cacheKey = GetCacheKey(originalText, config); _translationManager.PrimaryEndpoint?.AddTranslationToCache(cacheKey, translatedText); if (IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, translatedText, originalText, info, TextTranslate.ChangeTime); }); } } else { if (_translationManager?.PrimaryEndpoint == null) { return; } bool isTranslatable = normalText?.IsTranslatable(originalText, isToken: false) ?? true; if (ShouldStabilizeText(ui, originalText)) { string text = (config?.ConfigFileName ?? "global") + ":" + originalText; if (_immediatelyTranslating.TryAdd(text, 0)) { StartTextStabilization(ui, originalText, info, normalText, config, text); return; } string cached = GetCachedTranslation(originalText, config); if (cached != null && IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, cached, originalText, info, TextTranslate.ChangeTime); }); } else if (cached == null) { _pendingStabilizationUIs.GetOrAdd(text, (string _) => new ConcurrentDictionary()).TryAdd(ui, 0); } } else { _translationManager.PrimaryEndpoint.EnqueueTranslation(ui, originalText, info, normalText, config, isTranslatable); } } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An unexpected error occurred in QueueTranslation: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private bool ShouldStabilizeText(object ui, string text) { if (ui == null) { return false; } if (!ui.SupportsStabilization()) { return false; } int num = TranslatePlugin.stabilizationMinTextLength?.Value ?? 100; if (num == 0) { return false; } return text.Length > num; } private void StartTextStabilization(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, string immKey) { TextStabilizationContext obj = new TextStabilizationContext { UI = ui, OriginalText = text, Info = info, NormalText = normalText, Config = config, StartTime = Time.realtimeSinceStartup, MaxTries = (((TranslatePlugin.stabilizationMaxRetries?.Value ?? 60) == 0) ? int.MaxValue : (TranslatePlugin.stabilizationMaxRetries?.Value ?? 60)), CurrentTries = 0 }; ConfigEntry stabilizationDelay = TranslatePlugin.stabilizationDelay; obj.Delay = ((stabilizationDelay != null && stabilizationDelay.Value > 0f) ? TranslatePlugin.stabilizationDelay.Value : 0.9f); TextStabilizationContext context = obj; string stabilizationKey = GetStabilizationKey(ui, text); _stabilizationContexts[stabilizationKey] = context; object obj2 = ui; MonoBehaviour val = (MonoBehaviour)((obj2 is MonoBehaviour) ? obj2 : null); if (val != null && ((Component)val).gameObject.activeInHierarchy) { ((MonoBehaviour)TranslatePlugin.Instance).StartCoroutine(WaitForTextStablization(ui, info, context.Delay, context.MaxTries, 0, delegate(string stabilizedText) { OnTextStabilized(context, stabilizedText); try { _stabilizationContexts.Remove(GetStabilizationKey(ui, context.OriginalText)); _immediatelyTranslating.TryRemove(immKey, out var _); } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Error cleaning up stabilization context: " + ex2.Message)); } }, delegate { OnStabilizationFailed(context); try { _stabilizationContexts.Remove(GetStabilizationKey(ui, context.OriginalText)); _immediatelyTranslating.TryRemove(immKey, out var _); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error cleaning up stabilization context: " + ex.Message)); } })); } else { _stabilizationContexts.Remove(stabilizationKey); _immediatelyTranslating.TryRemove(immKey, out var _); } } [IteratorStateMachine(typeof(d__17))] private IEnumerator WaitForTextStablization(object ui, TextTranslationInfo info, float delay, int maxTries, int currentTries, Action onTextStabilized, Action onMaxTriesExceeded) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__17(0) { <>4__this = this, ui = ui, info = info, delay = delay, maxTries = maxTries, currentTries = currentTries, onTextStabilized = onTextStabilized, onMaxTriesExceeded = onMaxTriesExceeded }; } private void OnTextStabilized(TextStabilizationContext context, string stabilizedText) { TextTranslationInfo info = context.Info; if (info != null && info.IsTranslated) { return; } context.Info?.Reset(stabilizedText); if (!string.IsNullOrWhiteSpace(stabilizedText)) { string cachedTranslation = GetCachedTranslation(stabilizedText, context.Config); if (cachedTranslation != null) { SafeUpdateUI(context.UI, cachedTranslation, stabilizedText, context.Info, context.Info?.ChangeTime ?? TextTranslate.ChangeTime); return; } bool isTranslatable = context.NormalText == null || context.NormalText.IsTranslatable(stabilizedText, isToken: false); TranslationJob translationJob = new TranslationJob(context.UI, stabilizedText, saveResult: true, isTranslatable); translationJob.Associate(stabilizedText, context.UI, context.Info, context.NormalText, context.Config, saveResult: true, allowFallback: true); _translationManager.PrimaryEndpoint.EnqueueTranslation(context.UI, stabilizedText, context.Info, context.NormalText, context.Config, isTranslatable); } } private void OnStabilizationFailed(TextStabilizationContext context) { context.Info?.Reset(context.OriginalText); } public void ProcessMainThreadActions() { Action result; while (_mainThreadActions.TryDequeue(out result)) { try { result(); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error in main thread action: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } } private void OnTranslationJobCompleted(TranslationJob job) { try { string key = (job.Config?.ConfigFileName ?? "global") + ":" + job.OriginalText; _immediatelyTranslating.TryRemove(key, out var _); if (string.IsNullOrEmpty(job.TranslatedText)) { return; } foreach (object ui in job.AssociatedUIs) { if (IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, job.TranslatedText, job.OriginalText, job.TranslationInfo, job.StartVersion); }); } } if (_pendingStabilizationUIs.TryRemove(key, out var value2)) { foreach (KeyValuePair kvp in value2) { if (IsUIObjectValid(kvp.Key)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(kvp.Key, job.TranslatedText, job.OriginalText, job.TranslationInfo, job.StartVersion); }); } } } string cacheKey = GetCacheKey(job.OriginalText, job.Config); _translationManager.PrimaryEndpoint?.AddTranslationToCache(cacheKey, job.TranslatedText); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error handling translation job completion: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private void OnTranslationJobFailed(TranslationJob job) { try { string key = (job.Config?.ConfigFileName ?? "global") + ":" + job.OriginalText; _immediatelyTranslating.TryRemove(key, out var _); _pendingStabilizationUIs.TryRemove(key, out var _); TranslatePlugin.logger.LogWarning((object)("Translation failed for '" + job.OriginalText + "': " + job.ErrorMessage)); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error handling translation job failure: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private void SafeUpdateUI(object ui, string translatedText, string originalText, object translationInfo, long expectedVersion) { if (!IsUIObjectValid(ui)) { return; } try { TextTranslationInfo textTranslationInfo = translationInfo as TextTranslationInfo; if (textTranslationInfo != null) { if (textTranslationInfo.IsCurrentlySettingText) { return; } string uIText = GetUIText(ui, textTranslationInfo); if ((textTranslationInfo.OriginalText != null && textTranslationInfo.OriginalText != originalText) || (uIText != originalText && uIText != textTranslationInfo.TranslatedText) || (textTranslationInfo.ChangeTime != expectedVersion && uIText != originalText)) { return; } textTranslationInfo.OriginalText = originalText; textTranslationInfo.SetTranslatedText(translatedText); } TextTranslate.Instance.SetTranslatedText(ui, translatedText, originalText, textTranslationInfo); } catch (NullReferenceException) { } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Failed to safely update UI for text '" + originalText + "': " + ex2.Message)); TranslatePlugin.logger.LogError((object)ex2); } } private string GetUIText(object ui, TextTranslationInfo info) { try { if (ui == null) { return string.Empty; } return ui.GetText(info); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error getting UI text: " + ex.Message)); return string.Empty; } } private string TranslateText(string text, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config) { if (string.IsNullOrEmpty(text)) { return text; } string text2 = text; try { if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value && normalText.IsTranslatable(text, isToken: false)) { text2 = normalText.TryTranslate(text2); } return TranslateConfig.replaceByMap(text2, config); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Translation error for text '" + text + "': " + ex.Message)); return text; } } private bool IsUIObjectValid(object ui) { if (ui == null) { return false; } try { Component val = (Component)((ui is Component) ? ui : null); if (val != null && Object.op_Implicit((Object)(object)val)) { GameObject gameObject = val.gameObject; if (Object.op_Implicit((Object)(object)gameObject)) { Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); if (val2 != null) { return gameObject.activeInHierarchy && val2.enabled; } return gameObject.activeInHierarchy; } } return ui != null; } catch { return false; } } public void ClearCache() { _translationManager?.ClearAllJobs(); _stabilizationContexts.Clear(); _immediatelyTranslating.Clear(); _pendingStabilizationUIs.Clear(); TextTranslate.ChangeTime++; } private string GetCacheKey(string originalText, TranslateConfig.TranslateConfigFile config) { return (config?.ConfigFileName ?? "global") + ":" + originalText; } private string GetStabilizationKey(object ui, string text) { return $"{ui.GetHashCode()}:{text}"; } } internal class TextStabilizationContext { public object UI { get; set; } public string OriginalText { get; set; } public TextTranslationInfo Info { get; set; } public NormalTextTranslator NormalText { get; set; } public TranslateConfig.TranslateConfigFile Config { get; set; } public float StartTime { get; set; } public int MaxTries { get; set; } public int CurrentTries { get; set; } public float Delay { get; set; } } internal class ImageTranslationInfo { public bool IsTranslated { get; set; } public WeakReference Original { get; private set; } public void Initialize(Texture2D texture) { Original = WeakReference.Create(texture); } public void Reset(Texture2D newTexture) { IsTranslated = false; Original = WeakReference.Create(newTexture); } } public class InternalTranslationResult : IEnumerator { private readonly Action _onCompleted; internal bool IsGlobal { get; private set; } public bool IsCompleted { get; private set; } public string TranslatedText { get; private set; } public string ErrorMessage { get; private set; } public bool HasError => ErrorMessage != null; public object Current => null; internal InternalTranslationResult(bool isGlobal, Action onCompleted) { IsGlobal = isGlobal; _onCompleted = onCompleted; } internal void SetCompleted(string translatedText) { if (!IsCompleted) { IsCompleted = true; SetCompletedInternal(translatedText); } } internal void SetEmptyResponse() { SetError("Received empty response."); } internal void SetErrorWithMessage(string errorMessage) { SetError(errorMessage); } private void SetError(string errorMessage) { if (!IsCompleted) { IsCompleted = true; SetErrorInternal(errorMessage); } } private void SetErrorInternal(string errorMessage) { ErrorMessage = errorMessage ?? "Unknown error"; try { _onCompleted?.Invoke(new TranslationResult(null, ErrorMessage)); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while notifying of translation failure: " + ex.Message)); } } private void SetCompletedInternal(string translatedText) { TranslatedText = translatedText; try { _onCompleted?.Invoke(new TranslationResult(TranslatedText, null)); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while notifying of translation completion: " + ex.Message)); } } public bool MoveNext() { return !IsCompleted; } public void Reset() { } } public class NormalTextTranslator { public class ScopedTranslationData { public Dictionary Translations { get; set; } = new Dictionary(); public Dictionary ReverseTranslations { get; set; } = new Dictionary(); public List DefaultRegexes { get; set; } = new List(); public HashSet RegisteredRegexes { get; set; } = new HashSet(); public List SplitterRegexes { get; set; } = new List(); public HashSet RegisteredSplitterRegexes { get; set; } = new HashSet(); public HashSet FailedRegexLookups { get; set; } = new HashSet(); } public Dictionary _translations = new Dictionary(); public Dictionary _reverseTranslations = new Dictionary(); public Dictionary _tokenTranslations = new Dictionary(); public Dictionary _reverseTokenTranslations = new Dictionary(); private HashSet _partialTranslations = new HashSet(); public List _defaultRegexes = new List(); private HashSet _registeredRegexes = new HashSet(); public List _splitterRegexes = new List(); public HashSet _registeredSplitterRegexes = new HashSet(); private ConcurrentDictionary _failedRegexLookups = new ConcurrentDictionary(); private Dictionary _scopedTranslations = new Dictionary(); public string FileName; public string FilePath; private static readonly ConcurrentDictionary _regexCache; private static readonly ConcurrentDictionary _regexCacheLastAccess; private static DateTime _lastRegexCacheCleanupTime; private static readonly TimeSpan REGEX_CACHE_CLEANUP_INTERVAL; public static ConcurrentDictionary keyValuePairs; private static RegexOptions _regexCompiledSupportedFlag; public static RegexOptions RegexCompiledSupportedFlag => _regexCompiledSupportedFlag; public NormalTextTranslator(string fileName) { try { FileName = fileName; FilePath = Path.Combine(TranslatePlugin.DefaultPath, fileName); keyValuePairs.TryAdd(FileName, this); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while initializing the translation file: " + fileName)); TranslatePlugin.logger.LogError((object)ex); } } public void Load() { TranslatePlugin.logger.LogInfo((object)("--- Loading " + FileName + " file ---")); if (!File.Exists(FilePath)) { TranslatePlugin.logger.LogWarning((object)("Translation file not found: " + FilePath)); return; } try { CleanupCurrentFileRegexCache(); _defaultRegexes.Clear(); _translations.Clear(); _reverseTranslations.Clear(); _partialTranslations.Clear(); _tokenTranslations.Clear(); _reverseTokenTranslations.Clear(); _splitterRegexes.Clear(); _registeredRegexes.Clear(); _registeredSplitterRegexes.Clear(); _failedRegexLookups.Clear(); LoadTranslationsInStream(FilePath, FileName, isOutputFile: false, isLoad: true); PrecompileAndCacheRegexes(); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while loading " + FileName + " file!")); TranslatePlugin.logger.LogError((object)ex); } } private void LoadTranslationsInStream(string stream, string fullFileName, bool isOutputFile, bool isLoad) { if (isLoad) { TranslatePlugin.logger.LogInfo((object)("Loading text file: " + stream + ".")); } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); string[] array = streamReader.ReadToEnd().Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (text.TrimStart(Array.Empty()).StartsWith("#") || text.TrimStart(Array.Empty()).StartsWith("//")) { continue; } try { string[] array2 = TextHelper.ReadTranslationLineAndDecode(text); if (array2 == null) { continue; } string text2 = array2[0]; string value = array2[1]; if (string.IsNullOrEmpty(text2) || string.IsNullOrEmpty(value)) { continue; } if (text2.StartsWith("sr:")) { try { RegexTranslationSplitter regex = new RegexTranslationSplitter(text2, value); AddTranslationSplitterRegex(regex); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; string[] array3 = new string[5] { "An error occurred while constructing the regexTranslationSplitter: '", text, "'.", Environment.NewLine, null }; int num = 4; array3[num] = ex?.ToString(); logger.LogWarning((object)string.Concat(array3)); } } else if (text2.StartsWith("r:")) { try { RegexTranslation regex2 = new RegexTranslation(text2, value); AddTranslationRegex(regex2); } catch (Exception ex2) { ManualLogSource logger2 = TranslatePlugin.logger; string[] array4 = new string[5] { "An error occurred while constructing the regexTranslation: '", text, "'.", Environment.NewLine, null }; int num2 = 4; array4[num2] = ex2?.ToString(); logger2.LogWarning((object)string.Concat(array4)); } } else { AddTranslation(text2, value); } } catch (Exception ex3) { ManualLogSource logger3 = TranslatePlugin.logger; string[] array5 = new string[5] { "An error occurred while reading the translation: '", text, "'.", Environment.NewLine, null }; int num3 = 4; array5[num3] = ex3?.ToString(); logger3.LogWarning((object)string.Concat(array5)); } } } private void AddTranslation(string key, string value) { if (key != null && value != null) { _translations[key] = value; _reverseTranslations[value] = key; } } private void AddTranslationSplitterRegex(RegexTranslationSplitter regex) { if (!_registeredSplitterRegexes.Contains(regex.Original)) { _registeredSplitterRegexes.Add(regex.Original); _splitterRegexes.Add(regex); } } private void AddTranslationRegex(RegexTranslation regex) { if (!_registeredRegexes.Contains(regex.Original)) { _registeredRegexes.Add(regex.Original); _defaultRegexes.Add(regex); } } private bool HasTranslated(string key) { return _translations.ContainsKey(key); } private bool IsTranslation(string translation) { return _reverseTranslations.ContainsKey(translation); } private bool IsTokenTranslation(string translation) { return _reverseTokenTranslations.ContainsKey(translation); } public bool IsTranslatable(string text, bool isToken) { bool flag = !IsTranslation(text); if (isToken && flag) { flag = !IsTokenTranslation(text); } return flag; } public string SplitterTranslate(string text, RegexTranslationSplitter splitter, bool ignoreCase) { if (!string.IsNullOrEmpty(text) && splitter?.CompiledRegex != null) { try { Func translationFunc = null; return splitter.CompiledRegex.Replace(text, delegate(Match match) { if (translationFunc == null) { translationFunc = (string groupValue) => _translations.TryGetValue(groupValue, out var value) ? value : groupValue; } string translation = splitter.Translation; return ApplyDotNetReplacement(translation, match, translationFunc); }); } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("Splitter regex '" + splitter.Original + "' error: " + ex.Message)); return text; } } return text; } public static string getRegex(string pattern) { if (string.IsNullOrEmpty(pattern)) { return pattern; } return pattern.Replace("=", "\\="); } public string TryTranslate(string text) { if (string.IsNullOrEmpty(text)) { return text; } Stopwatch stopwatch = Stopwatch.StartNew(); string value; try { CheckAndCleanupRegexCache(); if (!_translations.TryGetValue(text, out value)) { string text2 = text; try { if (!_failedRegexLookups.ContainsKey(text2)) { bool flag = false; List list = new List(); foreach (RegexTranslationSplitter splitter in _splitterRegexes) { string key = "splitter_" + splitter.Original; _regexCache.GetOrAdd(key, (string _) => new Regex(splitter.Original, RegexOptions.Multiline | _regexCompiledSupportedFlag | RegexOptions.Singleline)); _regexCacheLastAccess[key] = DateTime.Now; text2 = SplitterTranslate(text2, splitter, ignoreCase: false); } for (int i = 0; i < _defaultRegexes.Count; i++) { RegexTranslation regexTrans = _defaultRegexes[i]; string key2 = "default_" + regexTrans.Original; Regex orAdd = _regexCache.GetOrAdd(key2, (string _) => new Regex(regexTrans.Original, RegexOptions.Multiline | _regexCompiledSupportedFlag | RegexOptions.Singleline)); _regexCacheLastAccess[key2] = DateTime.Now; try { if (orAdd.IsMatch(text2)) { text2 = orAdd.Replace(text2, regexTrans.Translation); flag = true; } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("Regex '" + regexTrans.Original + "' error, removing from cache: " + ex.Message)); list.Add(regexTrans); } } foreach (RegexTranslation item in list) { _defaultRegexes.Remove(item); } if (!flag) { _failedRegexLookups.TryAdd(text2, 0); if (_failedRegexLookups.Count > 10000) { _failedRegexLookups.Clear(); TranslatePlugin.logger.LogInfo((object)"Failed regex lookup cache reached limit, cleared"); } } } } catch (Exception ex2) { string textSnippet = GetTextSnippet(text, 50); TranslatePlugin.logger.LogError((object)("There is a problem with the translation method: " + textSnippet)); TranslatePlugin.logger.LogError((object)("Translation error: " + ex2.Message + "\n" + ex2.StackTrace)); } value = text2; return value; } } finally { stopwatch.Stop(); if (stopwatch.ElapsedMilliseconds > 500) { string textSnippet2 = GetTextSnippet(text, 50); TranslatePlugin.logger.LogWarning((object)$"TryTranslate took {stopwatch.ElapsedMilliseconds}ms for text: {textSnippet2}"); } } return value; } [CompilerGenerated] internal static string ChangeRegexReplaceFunc(Match match) { int num = int.Parse(match.Groups[1].Value); return "{" + (num - 1) + "}"; } private static void CheckAndCleanupRegexCache() { if (DateTime.Now - _lastRegexCacheCleanupTime <= REGEX_CACHE_CLEANUP_INTERVAL) { return; } try { bool flag = GC.GetTotalMemory(forceFullCollection: false) > 104857600; int num = 0; if (flag) { num = (int)((float)_regexCache.Count * 0.3f); num = Math.Max(1, Math.Min(num, _regexCache.Count)); } else if (_regexCache.Count > 800) { num = _regexCache.Count - 800; } if (num > 0) { List list = (from kv in (from kv in _regexCacheLastAccess where _regexCache.ContainsKey(kv.Key) orderby kv.Value select kv).Take(num) select kv.Key).ToList(); int num2 = 0; foreach (string item in list) { if (_regexCache.TryRemove(item, out var _) && _regexCacheLastAccess.TryRemove(item, out var _)) { num2++; } } if (num2 > 0) { TranslatePlugin.logger.LogInfo((object)$"Cleaned {num2} regex cache entries. Remaining: {_regexCache.Count}"); } } _lastRegexCacheCleanupTime = DateTime.Now; } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error cleaning up regex cache: " + ex.Message + "\n" + ex.StackTrace)); } } private void CleanupCurrentFileRegexCache() { IEnumerable first = _splitterRegexes.Select((RegexTranslationSplitter sr) => "splitter_" + sr.Original).ToList(); List second = _defaultRegexes.Select((RegexTranslation r) => "default_" + r.Original).ToList(); List list = first.Concat(second).ToList(); int num = 0; foreach (string item in list) { if (_regexCache.TryRemove(item, out var _)) { num++; } _regexCacheLastAccess.TryRemove(item, out var _); } TranslatePlugin.logger.LogInfo((object)$"Cleaned {num} old regex cache entries for {FileName}"); } private void PrecompileAndCacheRegexes() { foreach (RegexTranslationSplitter splitter in _splitterRegexes) { string key = "splitter_" + splitter.Original; _regexCache.GetOrAdd(key, (string _) => new Regex(splitter.Original, RegexOptions.Multiline | _regexCompiledSupportedFlag | RegexOptions.Singleline)); _regexCacheLastAccess[key] = DateTime.Now; } foreach (RegexTranslation regexTrans in _defaultRegexes) { string key2 = "default_" + regexTrans.Original; _regexCache.GetOrAdd(key2, (string _) => new Regex(regexTrans.Original, RegexOptions.Multiline | _regexCompiledSupportedFlag | RegexOptions.Singleline)); _regexCacheLastAccess[key2] = DateTime.Now; } } private static string GetTextSnippet(string text, int maxLength) { if (string.IsNullOrEmpty(text)) { return "[Empty Text]"; } if (text.Length <= maxLength) { return text; } if (text.Length > maxLength) { return text.Substring(0, maxLength) + "..."; } return text; } private string ApplyDotNetReplacement(string replacement, Match match, Func translate) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; while (num < replacement.Length) { if (replacement[num] == '$') { if (num + 1 < replacement.Length) { if (replacement[num + 1] == '$') { stringBuilder.Append('$'); num += 2; } else if (char.IsDigit(replacement[num + 1])) { int num2 = num + 1; int i; for (i = num2; i < replacement.Length && char.IsDigit(replacement[i]); i++) { } if (int.TryParse(replacement.Substring(num2, i - num2), out var result) && result >= 0 && result < match.Groups.Count) { string value = match.Groups[result].Value; stringBuilder.Append(translate(value)); num = i; } else { stringBuilder.Append('$'); num++; } } else { stringBuilder.Append('$'); num++; } } else { stringBuilder.Append('$'); num++; } } else { stringBuilder.Append(replacement[num]); num++; } } return stringBuilder.ToString(); } public ScopedTranslationData GetOrCreateScopedData(int scope) { if (!_scopedTranslations.TryGetValue(scope, out var value)) { value = new ScopedTranslationData(); _scopedTranslations[scope] = value; } return value; } public void ClearScopedFailedRegexLookups(int scope) { if (_scopedTranslations.TryGetValue(scope, out var value)) { value.FailedRegexLookups.Clear(); } } static NormalTextTranslator() { _regexCache = new ConcurrentDictionary(); _regexCacheLastAccess = new ConcurrentDictionary(); _lastRegexCacheCleanupTime = DateTime.Now; REGEX_CACHE_CLEANUP_INTERVAL = TimeSpan.FromMinutes(30.0); keyValuePairs = new ConcurrentDictionary(); _regexCompiledSupportedFlag = RegexOptions.None; CheckRegexCompiledSupport(); } private static void CheckRegexCompiledSupport() { try { string input = "She believed"; string pattern = ".he ..lie..d"; Regex regex = new Regex(pattern, RegexOptions.Compiled); Match match = regex.Match(input); if (match.Success) { _regexCompiledSupportedFlag = RegexOptions.Compiled; } else { TranslatePlugin.logger.LogInfo((object)"Regex compilation support check encountered unknown error"); } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"Current game version does not support compiled regex, using non-compiled mode"); } } } public class RegexTranslation { public Regex CompiledRegex { get; set; } public string Original { get; set; } public string Translation { get; set; } public string Key { get; set; } public string Value { get; set; } public RegexTranslation(string key, string value) { Key = key; Value = value; if (key.StartsWith("r:")) { key = key.Substring(2); } int num = key.IndexOf('"'); if (num != -1) { num++; int num2 = key.LastIndexOf('"'); if (num2 <= num - 1) { throw new Exception("Splitter regex with key: '" + Key + "' starts with a \" but does not end with a \"."); } key = key.Substring(num, num2 - num); } if (value.StartsWith("r:")) { value = value.Substring(2); } num = value.IndexOf('"'); if (num != -1) { num++; int num3 = value.LastIndexOf('"'); if (num3 == num - 1) { throw new Exception("Splitter regex with value: '" + Value + "' starts with a \" but does not end with a \"."); } value = value.Substring(num, num3 - num); } CompiledRegex = new Regex(key, RegexOptions.Multiline | NormalTextTranslator.RegexCompiledSupportedFlag); Original = key; Translation = value; } } public class RegexTranslationSplitter { public Regex CompiledRegex { get; set; } public string Original { get; set; } public string Translation { get; set; } public string Key { get; set; } public string Value { get; set; } public RegexTranslationSplitter(string key, string value) { Key = key; Value = value; if (key.StartsWith("sr:")) { key = key.Substring(3); } int num = key.IndexOf('"'); if (num != -1) { num++; int num2 = key.LastIndexOf('"'); if (num2 <= num - 1) { throw new Exception("Regex with key: '" + Key + "' starts with a \" but does not end with a \"."); } key = key.Substring(num, num2 - num); } if (value.StartsWith("sr:")) { value = value.Substring(3); } num = value.IndexOf('"'); if (num != -1) { num++; int num3 = value.LastIndexOf('"'); if (num3 == num - 1) { throw new Exception("Regex with value: '" + Value + "' starts with a \" but does not end with a \"."); } value = value.Substring(num, num3 - num); } CompiledRegex = new Regex(key, RegexOptions.Multiline | NormalTextTranslator.RegexCompiledSupportedFlag); Original = key; Translation = value; } } public class TemplatedString { public string Template { get; private set; } public Dictionary Arguments { get; private set; } public TemplatedString(string template, Dictionary arguments) { Template = template; Arguments = arguments; } public string Untemplate(string text) { foreach (KeyValuePair argument in Arguments) { text = text.Replace(argument.Key, argument.Value); } return text; } public string PrepareUntranslatedText(string untranslatedText) { foreach (KeyValuePair argument in Arguments) { string key = argument.Key; string newValue = CreateTranslatorFriendlyKey(key); untranslatedText = untranslatedText.Replace(key, newValue); } return untranslatedText; } public string FixTranslatedText(string translatedText, bool useTranslatorFriendlyArgs) { foreach (KeyValuePair argument in Arguments) { string key = argument.Key; string translatorFriendlyKey = (useTranslatorFriendlyArgs ? CreateTranslatorFriendlyKey(key) : key); translatedText = ReplaceApproximateMatches(translatedText, translatorFriendlyKey, key); } return translatedText; } public static string CreateTranslatorFriendlyKey(string key) { char c = key[2]; return "ZM" + (char)(c + 2) + "Z"; } public static string ReplaceApproximateMatches(string translatedText, string translatorFriendlyKey, string key) { int num = translatorFriendlyKey.Length - 1; int num2 = num; int num3 = num; int num4 = translatedText.Length - 1; for (int num5 = num4; num5 >= 0; num5--) { char c = translatedText[num5]; if (c != ' ' && c != '\u3000') { if ((c = char.ToUpperInvariant(c)) == char.ToUpperInvariant(translatorFriendlyKey[num2]) || c == char.ToUpperInvariant(translatorFriendlyKey[num2 = num])) { if (num2 == num) { num3 = num5; } num2--; } if (num2 < 0) { int count = num3 + 1 - num5; translatedText = translatedText.Remove(num5, count).Insert(num5, key); num2 = num; } } } return translatedText; } } internal class TextTranslationInfo { private Action _unfont; private bool _initialized; private HashSet _redirectedTranslations; public long changeTime; private static HashSet _processedFonts = new HashSet(); public ITextComponentManipulator TextManipulator { get; set; } public string OriginalText { get; set; } public string TranslatedText { get; set; } public bool IsTranslated { get; set; } public bool IsCurrentlySettingText { get; set; } public bool IsKnownTextComponent { get; set; } public bool ShouldIgnore { get; set; } public bool MustIgnore { get; set; } public long TextVersion { get; set; } public long ChangeTime { get { return changeTime; } set { changeTime = value; } } public HashSet RedirectedTranslations { get { HashSet result; if ((result = _redirectedTranslations) == null) { result = (_redirectedTranslations = new HashSet()); } return result; } } public void Init(object ui) { if (!_initialized) { _initialized = true; MustIgnore = false; ShouldIgnore = ui.ShouldIgnoreTextComponent(); TextManipulator = ui.GetTextManipulator(); } } public void Reset(string newText) { IsTranslated = false; TranslatedText = null; OriginalText = newText; ChangeTime = TextTranslate.ChangeTime; TextVersion++; } public void SetTranslatedText(string translatedText) { IsTranslated = true; TranslatedText = translatedText; } public void ChangeFont(object ui) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown if (ui == null) { return; } Type unityType = ui.GetUnityType(); if ((UnityTypes.Text != null && UnityTypes.Text.IsAssignableFrom(unityType)) || ((UnityTypes.TextMeshPro == null || !UnityTypes.TextMeshPro.IsAssignableFrom(unityType)) && (UnityTypes.TextMeshProUGUI == null || !UnityTypes.TextMeshProUGUI.IsAssignableFrom(unityType)))) { return; } try { CachedProperty val = ReflectionCache.CachedProperty(unityType, "font"); TMP_FontAsset val2 = (TMP_FontAsset)val.Get(ui); if (!((Object)(object)val2 != (Object)null) || StringExtensions.IsNullOrWhiteSpace(TranslatePlugin.fallbackFontTextMeshPro.Value)) { return; } if (!_processedFonts.Contains(val2)) { _processedFonts.Add(val2); foreach (char shouldRemoveChar in TranslatePlugin.getShouldRemoveChars()) { val2.TryRemoveCharacter(shouldRemoveChar); } } TMP_FontAsset val3 = (TMP_FontAsset)FontCache.GetOrCreateFallbackFontTextMeshPro(); if ((Object)(object)val3 != (Object)null && !val2.fallbackFontAssetTable.Contains(val3)) { val2.fallbackFontAssetTable.Add(val3); } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("There was a problem when changing the font!" + ex.Message)); } } public void UnchangeFont(object ui) { if (ui != null) { _unfont?.Invoke(ui); _unfont = null; } } } internal class TextureDataResult { public byte[] Data { get; } public bool NonReadable { get; } public float CalculationTime { get; set; } public TextureDataResult(byte[] data, bool nonReadable, float calculationTime) { Data = data; NonReadable = nonReadable; CalculationTime = calculationTime; } } public class TextureTranslationCache { internal class FileSystemTranslatedImageSource : TranslatedImage.ITranslatedImageSource { private readonly string _fileName; public FileSystemTranslatedImageSource(string fileName) { _fileName = fileName; } public byte[] GetData() { for (int i = 0; i < 3; i++) { try { using FileStream fileStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); return StreamExtensions.ReadFully((Stream)fileStream, 16384); } catch (IOException) when (i < 2) { Thread.Sleep(100 * (i + 1)); } } throw new IOException("Unable to read file '" + _fileName + "' after 3 attempts due to sharing violation."); } } internal static class HashHelper { private static readonly SHA1Managed SHA1 = new SHA1Managed(); private static readonly uint[] Lookup32 = CreateLookup32(); public static string Compute(byte[] data) { return ByteArrayToHexViaLookup32(SHA1.ComputeHash(data)).Substring(0, 10); } private static uint[] CreateLookup32() { uint[] array = new uint[256]; for (int i = 0; i < 256; i++) { string text = i.ToString("X2"); array[i] = text[0] + ((uint)text[1] << 16); } return array; } private static string ByteArrayToHexViaLookup32(byte[] bytes) { uint[] lookup = Lookup32; char[] array = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { uint num = lookup[bytes[i]]; array[2 * i] = (char)num; array[2 * i + 1] = (char)(num >> 16); } return new string(array); } } private SafeFileWatcher _textureFileWatcher; private readonly ConcurrentDictionary _textureFileLastModifiedTimes = new ConcurrentDictionary(); private Timer _texturePollingTimer; private readonly object _loadLock = new object(); public ConcurrentDictionary _untranslatedImages = new ConcurrentDictionary(); private ConcurrentDictionary _keyToFileName = new ConcurrentDictionary(); private bool _disposed; public ConcurrentDictionary _translatedImages = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); private static readonly TimeSpan CLEANUP_INTERVAL = TimeSpan.FromMinutes(15.0); private DateTime _lastCleanupTime = DateTime.Now; private readonly ConcurrentDictionary _textureAccessTime = new ConcurrentDictionary(); public TextureTranslationCache() { try { Directory.CreateDirectory(TranslatePlugin.TexturesPath); string fullPath = Path.GetFullPath(TranslatePlugin.TexturesPath); ConfigEntry enableFileWatcher = TranslatePlugin.enableFileWatcher; if (enableFileWatcher != null && enableFileWatcher.Value) { _textureFileWatcher = new SafeFileWatcher(fullPath); _textureFileWatcher.DirectoryUpdated += TextureFileWatcher_DirectoryUpdated; TranslatePlugin.logger.LogInfo((object)("Tracking texture path: " + fullPath)); } ConfigEntry enablePollingCheck = TranslatePlugin.enablePollingCheck; if (enablePollingCheck != null && enablePollingCheck.Value) { _texturePollingTimer = new Timer(delegate { TextureFileWatcher_DirectoryUpdated(); }, null, TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(10.0)); TranslatePlugin.logger.LogInfo((object)("Polling check tracking texture path " + fullPath)); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while initializing translation file watching for textures."); } } public IEnumerable GetTextureFiles() { return from x in Directory.GetFiles(TranslatePlugin.TexturesPath, "*.*", SearchOption.AllDirectories) where x.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) select x; } public void LoadTranslationFiles() { lock (_loadLock) { try { float realtimeSinceStartup = Time.realtimeSinceStartup; _translatedImages.Clear(); _untranslatedImages.Clear(); _keyToFileName.Clear(); Directory.CreateDirectory(TranslatePlugin.TexturesPath); foreach (string textureFile in GetTextureFiles()) { RegisterImageFromFile(textureFile); } CleanupInvalidEntries(); float realtimeSinceStartup2 = Time.realtimeSinceStartup; XuaLogger.AutoTranslator.Debug($"Loaded texture files (took {Math.Round(realtimeSinceStartup2 - realtimeSinceStartup, 2)} seconds)"); } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while loading translations."); } } } private void RegisterImageFromStream(string fullFileName, TranslatedImage.ITranslatedImageSource source) { try { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullFileName); int num = fileNameWithoutExtension.LastIndexOf("["); int num2 = fileNameWithoutExtension.LastIndexOf("]"); if (num2 > -1 && num > -1 && num2 > num) { int num3 = num + 1; string[] array = fileNameWithoutExtension.Substring(num3, num2 - num3).Split(new char[1] { '-' }); string key; string x; if (array.Length == 1) { key = array[0]; x = array[0]; } else { if (array.Length != 2) { XuaLogger.AutoTranslator.Warn("Image not loaded (Unknown hash): " + fullFileName + "."); return; } key = array[0]; x = array[1]; } byte[] data = source.GetData(); string y = HashHelper.Compute(data); bool flag = StringComparer.InvariantCultureIgnoreCase.Compare(x, y) != 0; _keyToFileName[key] = fullFileName; if (flag || TranslatePlugin.cacheUnmodifiedTextures.Value) { RegisterTranslatedImage(fullFileName, key, data); if (!flag) { XuaLogger.AutoTranslator.Debug("Image loaded (Unmodified): " + fullFileName + "."); } else { XuaLogger.AutoTranslator.Debug("Image loaded (Modified): " + fullFileName + "."); } } else { RegisterUntranslatedImage(key); XuaLogger.AutoTranslator.Debug("Image not loaded (Unmodified): " + fullFileName + "."); } } else { XuaLogger.AutoTranslator.Warn("Image not loaded (No hash): " + fullFileName + "."); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while loading texture file: " + fullFileName); } } private void RegisterImageFromFile(string fullFileName) { if (File.Exists(fullFileName)) { FileSystemTranslatedImageSource source = new FileSystemTranslatedImageSource(fullFileName); RegisterImageFromStream(fullFileName, source); _textureFileLastModifiedTimes[fullFileName] = File.GetLastWriteTime(fullFileName); } } public void RenameFileWithKey(string name, string key, string newKey) { try { if (_keyToFileName.TryGetValue(key, out var value)) { _keyToFileName.TryRemove(key, out var _); if (!IsImageRegistered(newKey)) { byte[] data = File.ReadAllBytes(value); RegisterImageFromData(name, newKey, data); File.Delete(value); XuaLogger.AutoTranslator.Warn("Replaced old file with name '" + name + "' registered with key old '" + key + "'."); } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while trying to rename file with key '" + key + "'."); } } internal void RegisterImageFromData(string textureName, string key, byte[] data) { string text = StringExtensions.SanitizeForFileSystem(textureName); string text2 = HashHelper.Compute(data); string text3 = ((!(key == text2)) ? (text + " [" + key + "-" + text2 + "].png") : (text + " [" + key + "].png")); string text4 = Path.Combine(TranslatePlugin.TexturesPath, text3); File.WriteAllBytes(text4, data); XuaLogger.AutoTranslator.Info("Dumped texture file: " + text3); _keyToFileName[key] = text4; if (TranslatePlugin.cacheUnmodifiedTextures.Value) { RegisterTranslatedImage(text4, key, data); } else { RegisterUntranslatedImage(key); } } private void RegisterTranslatedImage(string fileName, string key, byte[] data) { if (TranslatePlugin.cacheTexturesInMemory.Value) { _translatedImages[key] = new TranslatedImage(fileName, data, null); return; } TranslatedImage.ITranslatedImageSource source = new FileSystemTranslatedImageSource(fileName); _translatedImages[key] = new TranslatedImage(fileName, null, source); } private void RegisterUntranslatedImage(string key) { _untranslatedImages.TryAdd(key, 0); } internal bool IsImageRegistered(string key) { if (!_translatedImages.ContainsKey(key)) { return _untranslatedImages.ContainsKey(key); } return true; } internal bool TryGetTranslatedImage(string key, out byte[] data, out TranslatedImage image) { PeriodicCleanup(); if (_translatedImages.TryGetValue(key, out image)) { try { data = image.GetData(); if (data != null) { _textureAccessTime.AddOrUpdate(key, DateTime.Now, (string k, DateTime v) => DateTime.Now); } return data != null; } catch (Exception ex) { XuaLogger autoTranslator = XuaLogger.AutoTranslator; Exception ex2 = ex; string text = "Error loading cached image: "; autoTranslator.Error(ex2, text + image?.FileName); _translatedImages.TryRemove(key, out var _); _textureAccessTime.TryRemove(key, out var _); } } data = null; image = null; if (_keyToFileName.TryGetValue(key, out var value3) && File.Exists(value3)) { try { RegisterImageFromFile(value3); if (_translatedImages.TryGetValue(key, out image)) { data = image.GetData(); if (data != null) { _textureAccessTime.AddOrUpdate(key, DateTime.Now, (string k, DateTime v) => DateTime.Now); } return data != null; } } catch (Exception ex3) { XuaLogger.AutoTranslator.Error(ex3, "Error reloading image: " + value3); } } return false; } private void TextureFileWatcher_DirectoryUpdated() { try { bool flag = false; foreach (string textureFile in GetTextureFiles()) { if (!File.Exists(textureFile)) { continue; } DateTime lastWriteTime = File.GetLastWriteTime(textureFile); if (_textureFileLastModifiedTimes.TryGetValue(textureFile, out var value)) { if (lastWriteTime > value) { _textureFileLastModifiedTimes[textureFile] = lastWriteTime; flag = true; } } else { _textureFileLastModifiedTimes[textureFile] = lastWriteTime; } } if (flag) { LoadTranslationFiles(); XuaLogger.AutoTranslator.Info("Texture files reloaded due to file changes."); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "Error reloading texture files: " + ex.Message); } } private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { _textureFileWatcher?.Dispose(); _textureFileWatcher = null; _texturePollingTimer?.Dispose(); _texturePollingTimer = null; } _disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public void CleanupInvalidEntries() { foreach (KeyValuePair item in _textureAccessTime.ToList()) { if (DateTime.Now - item.Value > CLEANUP_INTERVAL) { _translatedImages.TryRemove(item.Key, out var _); _textureAccessTime.TryRemove(item.Key, out var _); } } string[] array = _untranslatedImages.Keys.ToArray(); foreach (string key in array) { if (!_keyToFileName.ContainsKey(key)) { _untranslatedImages.TryRemove(key, out var _); } } foreach (string item2 in _keyToFileName.Where(delegate(KeyValuePair kv) { KeyValuePair keyValuePair2 = kv; return !File.Exists(keyValuePair2.Value); }).Select(delegate(KeyValuePair kv) { KeyValuePair keyValuePair = kv; return keyValuePair.Key; }).ToList()) { _keyToFileName.TryRemove(item2, out var _); _translatedImages.TryRemove(item2, out var _); _textureAccessTime.TryRemove(item2, out var _); } } public void PeriodicCleanup() { if (DateTime.Now - _lastCleanupTime > CLEANUP_INTERVAL) { CleanupInvalidEntries(); _lastCleanupTime = DateTime.Now; } } public void PreloadCommonTextures() { try { foreach (string item in _textureAccessTime.OrderByDescending((KeyValuePair kvp) => kvp.Value).Take(10).Select(delegate(KeyValuePair kvp) { KeyValuePair keyValuePair = kvp; return keyValuePair.Key; }) .ToList()) { if (!_translatedImages.ContainsKey(item) && _keyToFileName.TryGetValue(item, out var value) && File.Exists(value)) { RegisterImageFromFile(value); } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "Error during texture preloading"); } } public void UpdateTextureStatistics(string key) { _textureAccessTime.AddOrUpdate(key, DateTime.Now, (string k, DateTime v) => DateTime.Now); } } internal class TextureTranslationInfo { private static Dictionary NameToHash = new Dictionary(); private static readonly Encoding UTF8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private string _key; private byte[] _originalData; private bool _initialized; private TextureFormat _textureFormat; public static HashSet DuplicateTextureNames = new HashSet(); public WeakReference Original { get; private set; } public Texture2D Translated { get; private set; } public Sprite TranslatedSprite { get; set; } public bool IsTranslated { get; set; } public bool IsDumped { get; set; } public bool UsingReplacedTexture { get; set; } public void Initialize(Texture2D texture) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _initialized = true; _textureFormat = texture.format; SetOriginal(texture); } } public void SetOriginal(Texture2D texture) { Original = WeakReference.Create(texture); } public void SetTranslated(Texture2D texture) { Translated = texture; } public void CreateTranslatedTexture(byte[] newData, TranslateExtensions.ImageFormat format) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Translated == (Object)null) { Texture2D target = Original.Target; Texture2D val = CreateEmptyTexture2D(_textureFormat); val.LoadImageEx(newData, format, target); SetTranslated(val); ExtensionDataHelper.SetExtensionData((object)val, this); UsingReplacedTexture = true; } } public void CreateOriginalTexture() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!((WeakReference)(object)Original).IsAlive && _originalData != null) { Texture2D val = CreateEmptyTexture2D(_textureFormat); val.LoadImageEx(_originalData, TranslateExtensions.ImageFormat.PNG, null); SetOriginal(val); } } public string GetKey() { if ((Object)(object)Original.Target == (Object)null) { return null; } SetupHashAndData(Original.Target); return _key; } public byte[] GetOriginalData() { SetupHashAndData(Original.Target); return _originalData; } public byte[] GetOrCreateOriginalData() { SetupHashAndData(Original.Target); if (_originalData != null) { return _originalData; } return Original.Target.GetTextureData().Data; } public static void AddDuplicateName(string name) { DuplicateTextureNames.Add(name); } private TextureDataResult SetupKeyForNameWithFallback(string name, Texture2D texture) { if (TranslatePlugin.disableDuplicateTextureCheck.Value) { _key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); return null; } if (!string.IsNullOrEmpty(TranslatePlugin.ignoredTextureNames.Value)) { string[] source = TranslatePlugin.ignoredTextureNames.Value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); if (source.Contains(name)) { _key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); return null; } } bool flag = false; string value = null; TextureDataResult textureData = texture.GetTextureData(); string text = TextureTranslationCache.HashHelper.Compute(textureData.Data); if (NameToHash.TryGetValue(name, out value)) { if (value != text) { XuaLogger.AutoTranslator.Warn("Detected duplicate image name: " + name); flag = true; AddDuplicateName(name); } } else { NameToHash[name] = text; } _key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); if (flag) { string key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); TranslateConfig.cache.RenameFileWithKey(name, key, value); } return textureData; } private void SetupHashAndData(Texture2D texture) { if (_key != null) { return; } string textureName = texture.GetTextureName(null); if (textureName != null) { TextureDataResult textureDataResult = SetupKeyForNameWithFallback(textureName, texture); if (textureDataResult != null) { _originalData = textureDataResult.Data; } } } public static Texture2D CreateEmptyTexture2D(TextureFormat format) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) TextureFormat val = (((int)format == 3) ? ((TextureFormat)3) : (((int)format == 10) ? ((TextureFormat)3) : (((int)format != 12) ? ((TextureFormat)5) : ((TextureFormat)5)))); return new Texture2D(2, 2, val, false); } public static Texture2D CreateEmptyTexture2D(Texture2D texture) { //IL_000d: 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_0019: Expected O, but got Unknown return new Texture2D(((Texture)texture).width, ((Texture)texture).height, texture.format, false); } } public class TranslatedImage { public interface ITranslatedImageSource { byte[] GetData(); } private static readonly Dictionary Formats = new Dictionary(StringComparer.OrdinalIgnoreCase) { { ".png", TranslateExtensions.ImageFormat.PNG }, { ".tga", TranslateExtensions.ImageFormat.TGA } }; private readonly ITranslatedImageSource _source; private WeakReference _weakData; private byte[] _data; public string FileName { get; } internal TranslateExtensions.ImageFormat ImageFormat { get; } private byte[] Data { get { if (_source == null) { return _data; } byte[] target = _weakData.Target; if (target != null) { return target; } return null; } set { if (_source == null) { _data = value; } else { _weakData = WeakReference.Create(value); } } } public TranslatedImage(string fileName, byte[] data, ITranslatedImageSource source) { _source = source; FileName = fileName; Data = data; ImageFormat = Formats[Path.GetExtension(fileName)]; } public byte[] GetData() { byte[] array = Data; if (array != null) { return array; } if (_source != null) { array = (Data = _source.GetData()); XuaLogger.AutoTranslator.Debug("Image loaded in GetData: " + FileName + "."); } return array; } } internal static class TranslateExtensions { public enum ImageFormat { PNG, TGA } private interface IPropertyMover { void MoveProperty(object source, object destination); } private class PropertyMover : IPropertyMover { private readonly Func _get; private readonly Action _set; public PropertyMover(PropertyInfo propertyInfo) { MethodInfo getMethod = propertyInfo.GetGetMethod(); MethodInfo setMethod = propertyInfo.GetSetMethod(); _get = (Func)ExpressionHelper.CreateTypedFastInvoke((MethodBase)getMethod); _set = (Action)ExpressionHelper.CreateTypedFastInvoke((MethodBase)setMethod); } public void MoveProperty(object source, object destination) { TPropertyType arg = _get((T)source); _set((T)destination, arg); } } private static readonly Dictionary Manipulators = new Dictionary(); private static readonly Type TextElementType = typeof(TextElement); public static FieldInfo m_ChunkOffset = typeof(StringBuilder).GetField("m_ChunkOffset", BindingFlags.Instance | BindingFlags.NonPublic); public static FieldInfo m_ChunkLength = typeof(StringBuilder).GetField("m_ChunkLength", BindingFlags.Instance | BindingFlags.NonPublic); public static MethodInfo FindChunkForIndex = typeof(StringBuilder).GetMethod("FindChunkForIndex", BindingFlags.Instance | BindingFlags.NonPublic); public static MethodInfo ReplaceAllInChunk = typeof(StringBuilder).GetMethod("ReplaceAllInChunk", BindingFlags.Instance | BindingFlags.NonPublic); public static MethodInfo StartsWith = typeof(StringBuilder).GetMethod("StartsWith", BindingFlags.Instance | BindingFlags.NonPublic); private static List TexturePropertyMovers; private static readonly string SetAllDirtyMethodName = "SetAllDirty"; private static readonly string TexturePropertyName = "texture"; private static readonly string MainTexturePropertyName = "mainTexture"; private static readonly string CapitalMainTexturePropertyName = "MainTexture"; private static readonly string MarkAsChangedMethodName = "MarkAsChanged"; public static FieldInfo m_AtlasPopulationMode = typeof(TMP_FontAsset).GetField("m_AtlasPopulationMode", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_SourceFontFile = typeof(TMP_FontAsset).GetField("m_SourceFontFile", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_FaceInfo = typeof(TMP_FontAsset).GetField("m_FaceInfo", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo s_MissingCharacterList = typeof(TMP_FontAsset).GetField("s_MissingCharacterList", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_MissingUnicodesFromFontFile = typeof(TMP_FontAsset).GetField("m_MissingUnicodesFromFontFile", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_CharacterLookupDictionary = typeof(TMP_FontAsset).GetField("m_CharacterLookupDictionary", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_CharacterTable = typeof(TMP_FontAsset).GetField("m_CharacterTable", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_CharactersToAddLookup = typeof(TMP_FontAsset).GetField("m_CharactersToAddLookup", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static Type GetUnityType(this object obj) { return obj.GetType(); } private static bool IsWordChar(char c) { if (!char.IsLetterOrDigit(c)) { return c == '_'; } return true; } public static bool Contains(this string s, string value, bool ignoreCase = false) { if (!ignoreCase) { return s.IndexOf(value, StringComparison.Ordinal) >= 0; } return s.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0; } public static string ToString(this char[] value, int startIndex, int count) { if (value == null) { throw new ArgumentNullException("value"); } if (startIndex < 0 || startIndex > value.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > value.Length) { throw new ArgumentOutOfRangeException("count"); } return new string(value, startIndex, count); } public static StringBuilder ReplaceFull(this StringBuilder builder, string oldValue, string newValue) { return builder.ReplaceFull(oldValue, newValue, 0, builder.Length); } private static StringBuilder ReplaceFull(this StringBuilder builder, string oldValue, string newValue, int startIndex, int count) { int length = builder.Length; if (startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex > length - count) { throw new ArgumentOutOfRangeException("count"); } if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue"); } if (newValue == null) { newValue = ""; } int length2 = newValue.Length; int length3 = oldValue.Length; int[] array = null; int num = 0; StringBuilder stringBuilder = (StringBuilder)FindChunkForIndex.Invoke(builder, new object[1] { startIndex }); int num2 = startIndex - (int)m_ChunkOffset.GetValue(builder); while (count > 0) { if ((num2 == 0 || !IsWordChar(stringBuilder[num2 - 1])) && (bool)StartsWith.Invoke(builder, new object[4] { stringBuilder, num2, count, oldValue })) { int num3 = num2 + oldValue.Length; if (num3 == length || !IsWordChar(stringBuilder[num3])) { if (array == null) { array = new int[5]; } else if (num >= array.Length) { int[] array2 = new int[array.Length * 3 / 2 + 4]; Array.Copy(array, array2, array.Length); array = array2; } array[num++] = num2; } num2 += oldValue.Length; count -= oldValue.Length; } else { num2++; count--; } if (num2 >= (int)m_ChunkLength.GetValue(stringBuilder) || count == 0) { int num4 = num2 + (int)m_ChunkOffset.GetValue(stringBuilder); ReplaceAllInChunk.Invoke(builder, new object[5] { array, num, stringBuilder, oldValue.Length, newValue }); num4 += (newValue.Length - oldValue.Length) * num; num = 0; stringBuilder = (StringBuilder)FindChunkForIndex.Invoke(builder, new object[1] { num4 }); num2 = num4 - (int)m_ChunkOffset.GetValue(stringBuilder); } } return builder; } public static StringBuilder ReplaceFullWords(this StringBuilder s, string oldWord, string newWord, bool ignoreCase = false) { if (s == null) { return null; } string input = s.ToString(); string pattern = "(?<=^|\\W)" + Regex.Escape(oldWord) + "(?=$|\\W)"; string value = Regex.Replace(input, pattern, newWord, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None); return s.Clear().Append(value); } public static string ReplaceFullWords(this string s, string oldWord, string newWord, bool ignoreCase = false) { if (s == null) { return null; } string pattern = "(?<=^|\\W)" + Regex.Escape(oldWord) + "(?=$|\\W)"; return Regex.Replace(s, pattern, newWord, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None); } public static bool EqualsIgnoreCase(this string value, string other) { return string.Equals(value, other, StringComparison.OrdinalIgnoreCase); } public static Texture2D GetTexture(this object ui) { if (ui == null) { return null; } SpriteRenderer val = default(SpriteRenderer); if (!ObjectExtensions.TryCastTo(ui, ref val)) { Type type = ui.GetType(); CachedProperty val2 = ReflectionCache.CachedProperty(type, MainTexturePropertyName); object obj; if ((obj = ((val2 != null) ? val2.Get(ui) : null)) == null) { CachedProperty val3 = ReflectionCache.CachedProperty(type, TexturePropertyName); if ((obj = ((val3 != null) ? val3.Get(ui) : null)) == null) { CachedProperty val4 = ReflectionCache.CachedProperty(type, CapitalMainTexturePropertyName); obj = ((val4 != null) ? val4.Get(ui) : null); } } return (Texture2D)((obj is Texture2D) ? obj : null); } Sprite sprite = val.sprite; if ((Object)(object)sprite == (Object)null) { return null; } return sprite.texture; } public static string GetTextureName(this object texture, string fallbackName) { Texture2D val = default(Texture2D); if (ObjectExtensions.TryCastTo(texture, ref val)) { string name = ((Object)val).name; if (!string.IsNullOrEmpty(name)) { return name; } } return fallbackName; } private static byte[] EncodeToPNGEx(Texture2D texture) { if (ImageConversion_Methods.EncodeToPNG != null) { return ImageConversion_Methods.EncodeToPNG(texture); } if (Texture2D_Methods.EncodeToPNG == null) { throw new NotSupportedException("No way to encode the texture to PNG."); } return Texture2D_Methods.EncodeToPNG(texture); } public static Sprite SetTexture(this object ui, Texture2D texture, Sprite sprite, bool isPrefixHooked) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown if (ui == null) { return null; } Texture2D texture2 = ui.GetTexture(); if (!UnityObjectReferenceComparer.Default.Equals((object)texture2, (object)texture)) { SpriteRenderer sr = null; if (ObjectExtensions.TryCastTo(ui, ref sr)) { if (isPrefixHooked) { return SafeCreateSprite(sr, sprite, texture); } return SafeSetSprite(sr, sprite, texture); } Type type = ui.GetType(); CachedProperty val = ReflectionCache.CachedProperty(type, MainTexturePropertyName); if (val != null) { val.Set(ui, (object)texture); } CachedProperty val2 = ReflectionCache.CachedProperty(type, TexturePropertyName); if (val2 != null) { val2.Set(ui, (object)texture); } CachedProperty val3 = ReflectionCache.CachedProperty(type, CapitalMainTexturePropertyName); if (val3 != null) { val3.Set(ui, (object)texture); } CachedProperty val4 = ReflectionCache.CachedProperty(type, "material"); object obj = ((val4 != null) ? val4.Get(ui) : null); if (obj != null) { CachedProperty val5 = ReflectionCache.CachedProperty(obj.GetType(), MainTexturePropertyName); if ((Object)(Texture2D)((val5 != null) ? val5.Get(obj) : null) == (Object)(object)texture2 && val5 != null) { val5.Set(obj, (object)texture); } } } return null; } public static Sprite SafeSetSprite(SpriteRenderer sr, Sprite sprite, Texture2D texture) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) return sr.sprite = Sprite.Create(texture, ((Object)(object)sprite != (Object)null) ? sprite.rect : sr.sprite.rect, Vector2.zero); } public static bool IsComponentActive(this object ui) { Component val = (Component)((ui is Component) ? ui : null); if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val)) { GameObject gameObject = val.gameObject; if (Object.op_Implicit((Object)(object)gameObject)) { Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); if ((Object)(object)val2 != (Object)null) { if (gameObject.activeInHierarchy) { return val2.enabled; } return false; } return gameObject.activeInHierarchy; } } return true; } public static TextureDataResult GetTextureData(this Texture2D texture) { //IL_003e: 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_0067: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; int width = ((Texture)texture).width; int height = ((Texture)texture).height; byte[] array = null; if (array == null) { RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)7, (RenderTextureReadWrite)0); GL.Clear(false, true, new Color(0f, 0f, 0f, 0f)); Graphics.Blit((Texture)(object)texture, temporary); RenderTexture active = RenderTexture.active; RenderTexture.active = temporary; Texture2D val = new Texture2D(width, height); val.ReadPixels(new Rect(0f, 0f, (float)((Texture)temporary).width, (float)((Texture)temporary).height), 0, 0); array = EncodeToPNGEx(val); Object.DestroyImmediate((Object)(object)val); RenderTexture.active = (((Object)(object)active == (Object)(object)temporary) ? null : active); RenderTexture.ReleaseTemporary(temporary); } float realtimeSinceStartup2 = Time.realtimeSinceStartup; return new TextureDataResult(array, nonReadable: false, realtimeSinceStartup2 - realtimeSinceStartup); } public static bool IsCompatible(this object texture, ImageFormat dataType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0015: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 TextureFormat format = ((Texture2D)texture).format; switch (dataType) { case ImageFormat.TGA: if ((int)format != 5 && (int)format != 4) { return (int)format == 3; } return true; default: return false; case ImageFormat.PNG: return true; } } public static bool IsKnownImageType(this object ui) { if (ui == null) { return false; } Type unityType = ui.GetUnityType(); Material val = default(Material); SpriteRenderer val2 = default(SpriteRenderer); if (!ObjectExtensions.TryCastTo(ui, ref val) && !ObjectExtensions.TryCastTo(ui, ref val2) && (UnityTypes.Image == null || !UnityTypes.Image.IsAssignableFrom(unityType)) && (UnityTypes.RawImage == null || !UnityTypes.RawImage.IsAssignableFrom(unityType)) && (UnityTypes.CubismRenderer == null || !UnityTypes.CubismRenderer.IsAssignableFrom(unityType))) { if (UnityTypes.UIWidget != null) { object objA = unityType; TypeContainer uILabel = UnityTypes.UILabel; if (!object.Equals(objA, (uILabel != null) ? uILabel.UnityType : null) && UnityTypes.UIWidget.IsAssignableFrom(unityType)) { return true; } } if ((UnityTypes.UIAtlas == null || !UnityTypes.UIAtlas.IsAssignableFrom(unityType)) && (UnityTypes.UITexture == null || !UnityTypes.UITexture.IsAssignableFrom(unityType))) { if (UnityTypes.UIPanel != null) { return UnityTypes.UIPanel.IsAssignableFrom(unityType); } return false; } } return true; } public static TextureTranslationInfo GetOrCreateTextureTranslationInfo(this Texture2D texture) { TextureTranslationInfo orCreateExtensionData = ExtensionDataHelper.GetOrCreateExtensionData((object)texture); orCreateExtensionData.Initialize(texture); return orCreateExtensionData; } public static ITextComponentManipulator GetTextManipulator(this object ui) { if (ui == null) { return null; } Type unityType = ui.GetUnityType(); if (!Manipulators.TryGetValue(unityType, out var value)) { value = ((UnityTypes.TextField != null && UnityTypes.TextField.IsAssignableFrom(unityType)) ? new FairyGUITextComponentManipulator() : ((UnityTypes.TextArea2D != null && UnityTypes.TextArea2D.IsAssignableFrom(unityType)) ? new TextArea2DComponentManipulator() : ((UnityTypes.UguiNovelText == null || !UnityTypes.UguiNovelText.IsAssignableFrom(unityType)) ? ((ITextComponentManipulator)new DefaultTextComponentManipulator(ui.GetType())) : ((ITextComponentManipulator)new UguiNovelTextComponentManipulator(ui.GetType()))))); Manipulators[unityType] = value; } return value; } private static Sprite SafeCreateSprite(Sprite sprite, Texture2D texture) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Rect rect = sprite.rect; float x = ((Rect)(ref rect)).x; rect = sprite.rect; return Sprite.Create(texture, new Rect(x, ((Rect)(ref rect)).y, (float)((Texture)texture).width, (float)((Texture)texture).height), sprite.pivot); } private static Sprite SafeCreateSprite(SpriteRenderer sr, Sprite sprite, Texture2D texture) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) return Sprite.Create(texture, ((Object)(object)sprite != (Object)null) ? sprite.rect : sr.sprite.rect, Vector2.zero); } public static void SetAllDirtyEx(this object ui) { if (ui != null) { Type unityType = ui.GetUnityType(); SpriteRenderer val = default(SpriteRenderer); if (UnityTypes.Graphic != null && UnityTypes.Graphic.IsAssignableFrom(unityType)) { ReflectionCache.CachedMethod(UnityTypes.Graphic.ClrType, SetAllDirtyMethodName).Invoke(ui); } else if (!ObjectExtensions.TryCastTo(ui, ref val)) { Type type = ui.GetType(); AccessToolsShim.Method(type, MarkAsChangedMethodName, Array.Empty())?.Invoke(ui, null); } } } public static bool ShouldIgnoreTextComponent(this object ui) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown Component val = (Component)((ui is Component) ? ui : null); if ((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val)) { return false; } GameObject gameObject = val.gameObject; Component firstComponentInSelfOrAncestor; if (UnityTypes.InputField != null) { GameObject gameObject2 = val.gameObject; TypeContainer inputField = UnityTypes.InputField; firstComponentInSelfOrAncestor = gameObject2.GetFirstComponentInSelfOrAncestor((inputField != null) ? inputField.UnityType : null); if ((Object)(object)firstComponentInSelfOrAncestor != (Object)null && InputField_Properties.Placeholder != null) { Component val2 = (Component)InputField_Properties.Placeholder.Get((object)firstComponentInSelfOrAncestor); return !UnityObjectReferenceComparer.Default.Equals((object)val2, (object)val); } } if (UnityTypes.TMP_InputField != null) { GameObject gameObject3 = val.gameObject; TypeContainer tMP_InputField = UnityTypes.TMP_InputField; firstComponentInSelfOrAncestor = gameObject3.GetFirstComponentInSelfOrAncestor((tMP_InputField != null) ? tMP_InputField.UnityType : null); if ((Object)(object)firstComponentInSelfOrAncestor != (Object)null && TMP_InputField_Properties.Placeholder != null) { Component val3 = (Component)TMP_InputField_Properties.Placeholder.Get((object)firstComponentInSelfOrAncestor); return !UnityObjectReferenceComparer.Default.Equals((object)val3, (object)val); } } GameObject go = gameObject; TypeContainer uIInput = UnityTypes.UIInput; firstComponentInSelfOrAncestor = go.GetFirstComponentInSelfOrAncestor((uIInput != null) ? uIInput.UnityType : null); return (Object)(object)firstComponentInSelfOrAncestor != (Object)null; } public static bool IsKnownTextType(this object ui) { if (ui == null) { return false; } Type unityType = ui.GetUnityType(); if ((UnityTypes.Text == null || !UnityTypes.Text.IsAssignableFrom(unityType)) && (UnityTypes.TextMeshPro == null || !UnityTypes.TextMeshPro.IsAssignableFrom(unityType)) && (UnityTypes.TextMeshProUGUI == null || !UnityTypes.TextMeshProUGUI.IsAssignableFrom(unityType)) && (UnityTypes.TextField == null || !UnityTypes.TextField.IsAssignableFrom(unityType)) && (UnityTypes.TextArea2D == null || !UnityTypes.TextArea2D.IsAssignableFrom(unityType)) && (UnityTypes.UguiNovelText == null || !UnityTypes.UguiNovelText.IsAssignableFrom(unityType)) && (UnityTypes.UILabel == null || !UnityTypes.UILabel.IsAssignableFrom(unityType)) && (UnityTypes.TextMesh == null || !UnityTypes.TextMesh.IsAssignableFrom(unityType))) { if (TextElementType != null) { return TextElementType.IsAssignableFrom(unityType); } return false; } return true; } public static Component GetFirstComponentInSelfOrAncestor(this GameObject go, Type type) { if (type == null) { return null; } GameObject val = go; while ((Object)(object)val != (Object)null) { Component component = val.GetComponent(type); if ((Object)(object)component != (Object)null) { return component; } Transform transform = val.transform; GameObject val2; if ((Object)(object)transform == (Object)null) { val2 = null; } else { Transform parent = transform.parent; val2 = (((Object)(object)parent != (Object)null) ? ((Component)parent).gameObject : null); } val = val2; } return null; } public static ImageTranslationInfo GetOrCreateImageTranslationInfo(this object obj, Texture2D originalTexture) { if (obj == null) { return null; } ImageTranslationInfo orCreateExtensionData = ExtensionDataHelper.GetOrCreateExtensionData(obj); if (orCreateExtensionData.Original == null) { orCreateExtensionData.Initialize(originalTexture); } return orCreateExtensionData; } public static void Load() { TexturePropertyMovers = new List(); LoadProperty("name"); LoadProperty("anisoLevel"); LoadProperty("filterMode"); LoadProperty("mipMapBias"); LoadProperty("wrapMode"); } private static void LoadProperty(string propertyName) { PropertyInfo property = typeof(TObject).GetProperty(propertyName); if (property != null && property.CanWrite && property.CanRead) { TexturePropertyMovers.Add(new PropertyMover(property)); } } public static void LoadImageEx(this Texture2D texture, byte[] data, ImageFormat format, Texture2D originalTexture) { TextureLoader.Load(texture, data, format); if (!((Object)(object)originalTexture != (Object)null)) { return; } foreach (IPropertyMover texturePropertyMover in TexturePropertyMovers) { texturePropertyMover.MoveProperty(originalTexture, texture); } } public static TextTranslationInfo GetOrCreateTextTranslationInfo(this object ui) { TextTranslationInfo orCreateExtensionData = ExtensionDataHelper.GetOrCreateExtensionData(ui); orCreateExtensionData.Init(ui); return orCreateExtensionData; } public static void SetText(this object ui, string text, TextTranslationInfo info) { if (ui != null) { info?.TextManipulator.SetText(ui, text); } } public static string GetText(this object ui, TextTranslationInfo info) { if (ui == null) { return null; } return info?.TextManipulator.GetText(ui); } public static void TryRemoveCharacter(this TMP_FontAsset fontAsset, uint unicode) { ((List)s_MissingCharacterList.GetValue(fontAsset)).Add(unicode); ((HashSet)m_MissingUnicodesFromFontFile.GetValue(fontAsset)).Add(unicode); ((HashSet)m_CharactersToAddLookup.GetValue(fontAsset)).Remove(unicode); ((Dictionary)m_CharacterLookupDictionary.GetValue(fontAsset)).Remove(unicode); ((List)m_CharacterTable.GetValue(fontAsset)).RemoveAll((TMP_Character character) => ((TMP_TextElement)character).unicode == unicode); } public static void TryRemoveCharacters(this TMP_FontAsset fontAsset, uint[] unicodes) { for (int i = 0; i < unicodes.Length; i++) { fontAsset.TryRemoveCharacter(unicodes[i]); } } public static bool IsCompatible(this Texture2D texture, ImageFormat dataType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 TextureFormat format = texture.format; switch (dataType) { case ImageFormat.TGA: if ((int)format != 5 && (int)format != 4) { return (int)format == 3; } return true; default: return false; case ImageFormat.PNG: return true; } } } public class TranslationEndpointManager { private readonly ConcurrentDictionary _unstartedJobs; private readonly ConcurrentDictionary _ongoingJobs; private readonly ConcurrentDictionary _failedTranslations; private readonly ConcurrentDictionary _translationCache; private readonly SemaphoreSlim _concurrencyLimiter; private readonly int _maxConcurrency; private readonly int _maxRetries; private readonly float _translationDelay; private int _availableBatchOperations = 5; public bool IsBusy => _ongoingJobs.Count >= _maxConcurrency; public bool HasUnstartedJob => !_unstartedJobs.IsEmpty; public int OngoingJobsCount => _ongoingJobs.Count; public int UnstartedJobsCount => _unstartedJobs.Count; public int AvailableBatchOperations { get { return _availableBatchOperations; } set { _availableBatchOperations = value; } } public TranslationManager Manager { get; set; } public Exception Error { get; set; } public int ConsecutiveErrors { get; set; } public TranslationEndpointManager(int maxConcurrency = 3, int maxRetries = 3, float translationDelay = 1f) { _unstartedJobs = new ConcurrentDictionary(); _ongoingJobs = new ConcurrentDictionary(); _failedTranslations = new ConcurrentDictionary(); _translationCache = new ConcurrentDictionary(); _concurrencyLimiter = new SemaphoreSlim(maxConcurrency, maxConcurrency); _maxConcurrency = maxConcurrency; _maxRetries = maxRetries; _translationDelay = translationDelay; } public bool TryGetTranslation(string key, out string value) { return _translationCache.TryGetValue(key, out value); } public void AddTranslationToCache(string key, string value) { if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) { _translationCache.TryAdd(key, value); } } public TranslationJob EnqueueTranslation(object ui, string key, object translationInfo, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool isTranslatable, bool allowFallback = true) { string jobKey = GetJobKey(key, config); if (_unstartedJobs.TryGetValue(jobKey, out var value)) { value.Associate(key, ui, translationInfo, normalText, config, saveResult: true, allowFallback); return null; } if (_ongoingJobs.TryGetValue(jobKey, out var value2)) { value2.Associate(key, ui, translationInfo, normalText, config, saveResult: true, allowFallback); return null; } TranslationJob translationJob = new TranslationJob(ui, key, saveResult: true, isTranslatable); translationJob.Associate(key, ui, translationInfo, normalText, config, saveResult: true, allowFallback); if (_unstartedJobs.TryAdd(jobKey, translationJob)) { Manager?.ScheduleUnstartedJobs(this); return translationJob; } return null; } public async Task HandleNextJob() { if (_unstartedJobs.IsEmpty) { return; } KeyValuePair keyValuePair = _unstartedJobs.FirstOrDefault(); if (keyValuePair.Value == null) { return; } string jobKey = keyValuePair.Key; TranslationJob job = keyValuePair.Value; if (!_unstartedJobs.TryRemove(jobKey, out job)) { return; } _ongoingJobs.TryAdd(jobKey, job); Manager?.OnJobStarted(this); try { await _concurrencyLimiter.WaitAsync(); await ProcessTranslationJob(job, jobKey); } finally { _concurrencyLimiter.Release(); _ongoingJobs.TryRemove(jobKey, out var _); Manager?.OnJobCompleted(this); if (_unstartedJobs.IsEmpty) { Manager?.UnscheduleUnstartedJobs(this); } } } private async Task ProcessTranslationJob(TranslationJob job, string jobKey) { try { if (!CanTranslate(job.OriginalText)) { job.State = TranslationJobState.Failed; job.ErrorMessage = "Translation failed due to too many previous failures."; Manager?.InvokeJobFailed(job); return; } string text = await Task.Run(() => TranslateText(job.OriginalText, job.NormalText, job.Config)); if (!string.IsNullOrEmpty(text) && !text.Equals(job.OriginalText)) { job.TranslatedText = text; job.State = TranslationJobState.Succeeded; string cacheKey = GetCacheKey(job.OriginalText, job.Config); AddTranslationToCache(cacheKey, text); Manager?.InvokeJobCompleted(job); } else if (string.IsNullOrEmpty(text) || text.Equals(job.OriginalText)) { job.State = TranslationJobState.Succeeded; job.TranslatedText = null; Manager?.InvokeJobCompleted(job); } else if (job.RetryCount < _maxRetries) { job.RetryCount++; _unstartedJobs.TryAdd(jobKey, job); await Task.Delay(TimeSpan.FromSeconds(_translationDelay)); Manager?.ScheduleUnstartedJobs(this); } else { job.State = TranslationJobState.Failed; job.ErrorMessage = "Max retries exceeded."; RegisterTranslationFailure(job.OriginalText); Manager?.InvokeJobFailed(job); } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Translation job failed: " + ex.Message)); if (job.RetryCount < _maxRetries) { job.RetryCount++; _unstartedJobs.TryAdd(jobKey, job); await Task.Delay(TimeSpan.FromSeconds(_translationDelay)); Manager?.ScheduleUnstartedJobs(this); } else { job.State = TranslationJobState.Failed; job.ErrorMessage = ex.Message; RegisterTranslationFailure(job.OriginalText); Manager?.InvokeJobFailed(job); } } } private bool CanTranslate(string untranslatedText) { if (_failedTranslations.TryGetValue(untranslatedText, out var value)) { return value < 3; } if (ShouldSkipTranslation(untranslatedText)) { return false; } return true; } private bool ShouldSkipTranslation(string text) { if (string.IsNullOrEmpty(text)) { return true; } return false; } private void RegisterTranslationFailure(string untranslatedText) { _failedTranslations.AddOrUpdate(untranslatedText, 1, (string key, byte value) => (byte)(value + 1)); TranslatePlugin.logger.LogWarning((object)$"Translation failure registered for text: '{untranslatedText}' (Total failures: {_failedTranslations[untranslatedText]})"); } private string TranslateText(string text, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config) { if (string.IsNullOrEmpty(text)) { return text; } string text2 = text; try { if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value && normalText.IsTranslatable(text, isToken: false)) { text2 = normalText.TryTranslate(text2); } return TranslateConfig.replaceByMap(text2, config); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Translation error for text '" + text + "': " + ex.Message)); return text; } } private string GetJobKey(string text, TranslateConfig.TranslateConfigFile config) { return (config?.ConfigFileName ?? "global") + ":" + text; } private string GetCacheKey(string text, TranslateConfig.TranslateConfigFile config) { return (config?.ConfigFileName ?? "global") + ":" + text; } public void ClearAllJobs() { List first = _unstartedJobs.Values.ToList(); List second = _ongoingJobs.Values.ToList(); _unstartedJobs.Clear(); _ongoingJobs.Clear(); foreach (TranslationJob item in first.Concat(second)) { item.State = TranslationJobState.Failed; item.ErrorMessage = "Translation failed because all jobs were cleared."; Manager?.InvokeJobFailed(item); } } } public class TranslationJob { public object UI { get; set; } public string OriginalText { get; set; } public string TranslatedText { get; set; } public bool SaveResult { get; private set; } public bool IsTranslatable { get; private set; } public TranslationJobState State { get; set; } public string ErrorMessage { get; set; } public List AssociatedUIs { get; set; } public object TranslationInfo { get; set; } public NormalTextTranslator NormalText { get; set; } public TranslateConfig.TranslateConfigFile Config { get; set; } public int RetryCount { get; set; } public bool AllowFallback { get; set; } = true; public bool SaveResultGlobally { get { return SaveResult; } private set { SaveResult = value; } } public List> Components { get; private set; } public HashSet> TranslationResults { get; private set; } public TranslationType TranslationType { get; set; } public TranslationEndpointManager Endpoint { get; internal set; } public UntranslatedTextInfo UntranslatedTextInfo { get; set; } public long StartVersion { get; set; } public bool ShouldPersistTranslation => (TranslationType & TranslationType.Full) == TranslationType.Full; public TranslationJob(object ui, string originalText, bool saveResult, bool isTranslatable) { UI = ui; OriginalText = originalText; SaveResult = saveResult; IsTranslatable = isTranslatable; State = TranslationJobState.Pending; AssociatedUIs = new List(); RetryCount = 0; Components = new List>(); TranslationResults = new HashSet>(); TranslationType = TranslationType.None; StartVersion = TextTranslate.ChangeTime; } public void Associate(string originalText, object ui, object info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool saveResult, bool allowFallback) { if (!AssociatedUIs.Contains(ui)) { AssociatedUIs.Add(ui); } TranslationInfo = info; NormalText = normalText; Config = config; AllowFallback = allowFallback; SaveResult |= saveResult; if (ui != null && !ui.IsSpammingComponent()) { UntranslatedText key = new UntranslatedText(originalText, isFromSpammingComponent: false, removeInternalWhitespace: false, whitespaceBetweenWords: false, enableTemplating: false, templateAllNumbersAway: false); Components.Add(new KeyAnd(key, ui)); } TranslationType |= TranslationType.Full; } } public class KeyAnd { public UntranslatedText Key { get; set; } public T Item { get; set; } public KeyAnd(UntranslatedText key, T item) { Key = key; Item = item; } } public enum TranslationJobState { Pending, Running, RunningOrQueued, Succeeded, Failed } public class TranslationManager { private readonly List _updateCallbacks; private readonly List _endpointsWithUnstartedJobs; private readonly Timer _processingTimer; public List Endpoints { get; private set; } public List ConfiguredEndpoints => Endpoints.Where((TranslationEndpointManager e) => e.Manager != null).ToList(); public TranslationEndpointManager PrimaryEndpoint { get; set; } public TranslationEndpointManager FallbackEndpoint { get; set; } public TranslationEndpointManager PassthroughEndpoint { get; private set; } public int OngoingJobsCount { get; set; } public int UnstartedJobsCount { get; set; } public int OngoingTranslations { get; set; } public int UnstartedTranslations { get; set; } public List AllEndpoints { get; private set; } public TranslationEndpointManager CurrentEndpoint { get; set; } public event Action JobCompleted; public event Action JobFailed; public TranslationManager() { _updateCallbacks = new List(); _endpointsWithUnstartedJobs = new List(); Endpoints = new List(); AllEndpoints = new List(); _processingTimer = new Timer(ProcessPendingJobs, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(100.0)); } public bool IsFallbackAvailableFor(TranslationEndpointManager endpoint) { if (endpoint != null && FallbackEndpoint != null && endpoint == CurrentEndpoint) { return FallbackEndpoint != endpoint; } return false; } public void InitializeEndpoints() { try { TranslatePlugin.logger.LogInfo((object)"TranslationManager endpoints initialized."); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while constructing endpoints: " + ex.Message)); } } public void CreateEndpoints(object httpSecurity) { TranslatePlugin.logger.LogInfo((object)"Creating translation endpoints."); } private void AddEndpoint(TranslationEndpointManager translationEndpointManager) { translationEndpointManager.Manager = this; AllEndpoints.Add(translationEndpointManager); if (translationEndpointManager.Error == null) { ConfiguredEndpoints.Add(translationEndpointManager); } if (PrimaryEndpoint == null) { PrimaryEndpoint = translationEndpointManager; } } public void Update() { int count = _updateCallbacks.Count; for (int i = 0; i < count; i++) { try { _updateCallbacks[i].Update(); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while calling update on " + _updateCallbacks[i].GetType().Name + ": " + ex.Message)); } } } public void KickoffTranslations() { List endpointsWithUnstartedJobs = _endpointsWithUnstartedJobs; for (int num = endpointsWithUnstartedJobs.Count - 1; num >= 0; num--) { TranslationEndpointManager endpoint = endpointsWithUnstartedJobs[num]; while (endpoint.HasUnstartedJob && !endpoint.IsBusy) { Task.Run(async delegate { try { await endpoint.HandleNextJob(); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error processing translation job: " + ex.Message)); } }); } } } public void ScheduleUnstartedJobs(TranslationEndpointManager endpoint) { lock (_endpointsWithUnstartedJobs) { if (!_endpointsWithUnstartedJobs.Contains(endpoint)) { _endpointsWithUnstartedJobs.Add(endpoint); } } } public void UnscheduleUnstartedJobs(TranslationEndpointManager endpoint) { lock (_endpointsWithUnstartedJobs) { _endpointsWithUnstartedJobs.Remove(endpoint); } } public void OnJobStarted(TranslationEndpointManager endpoint) { UnstartedJobsCount++; OngoingJobsCount--; } public void OnJobCompleted(TranslationEndpointManager endpoint) { OngoingJobsCount--; } public void InvokeJobCompleted(TranslationJob job) { this.JobCompleted?.Invoke(job); } public void InvokeJobFailed(TranslationJob job) { this.JobFailed?.Invoke(job); } public void ClearAllJobs() { foreach (TranslationEndpointManager configuredEndpoint in ConfiguredEndpoints) { configuredEndpoint.ClearAllJobs(); } } public void RebootAllEndpoints() { foreach (TranslationEndpointManager configuredEndpoint in ConfiguredEndpoints) { configuredEndpoint.ConsecutiveErrors = 0; } } public void RegisterEndpoint(TranslationEndpointManager translationEndpointManager) { translationEndpointManager.Manager = this; AllEndpoints.Add(translationEndpointManager); if (translationEndpointManager.Error == null) { ConfiguredEndpoints.Add(translationEndpointManager); } if (PrimaryEndpoint == null) { PrimaryEndpoint = translationEndpointManager; } } private void ProcessPendingJobs(object state) { KickoffTranslations(); } public void Dispose() { _processingTimer?.Dispose(); } } public interface IMonoBehaviour_Update { void Update(); } public class TranslationResult { public bool Succeeded => ErrorMessage == null; public string TranslatedText { get; } public string ErrorMessage { get; } internal TranslationResult(string translatedText, string errorMessage) { TranslatedText = translatedText; ErrorMessage = errorMessage; } } [Flags] public enum TranslationType { None = 0, Full = 1, Token = 2 } public class UntranslatedText { private bool? _isOnlyTemplate; public bool IsFromSpammingComponent { get; set; } public string LeadingWhitespace { get; } public string TrailingWhitespace { get; } public string Original_Text { get; } public string Original_Text_ExternallyTrimmed { get; } public string Original_Text_InternallyTrimmed { get; } public string Original_Text_FullyTrimmed { get; } public string TemplatedOriginal_Text { get; } public string TemplatedOriginal_Text_ExternallyTrimmed { get; } public string TemplatedOriginal_Text_InternallyTrimmed { get; } public string TemplatedOriginal_Text_FullyTrimmed { get; } public TemplatedString TemplatedText { get; } public bool IsTemplated => TemplatedText != null; public bool IsOnlyTemplate { get { if (!_isOnlyTemplate.HasValue) { _isOnlyTemplate = IsTemplated && !TemplatingHelper.ContainsUntemplatedCharacters(TemplatedOriginal_Text_ExternallyTrimmed); } return _isOnlyTemplate.Value; } } private static string PerformInternalTrimming(string text, bool whitespaceBetweenWords, ref StringBuilder builder) { if (builder != null) { builder.Length = 0; } else if (builder == null) { builder = new StringBuilder(64); } bool flag = false; int length = text.Length; int num = -1; int i; for (i = 0; i < length; i++) { char c = text[i]; if (c == '\n') { int num2 = i - 1; while (num2 >= 0 && char.IsWhiteSpace(text[num2])) { num2--; } int j; for (j = i + 1; j < length && char.IsWhiteSpace(text[j]); j++) { } num2++; j--; int num3 = j - num2; char c2 = '\0'; if (num3 > 0) { int num4 = 0; char c3 = text[num2]; bool flag2 = false; num2++; for (int k = num2; k <= j; k++) { char c4 = text[k]; if (c4 == c3) { if (!flag2) { num4++; builder.Append(c3); flag2 = true; } num4++; builder.Append(c4); c2 = c4; } else if (c3 == '\r' && c4 == '\n') { int index = k + 1; int index2 = k + 2; if (k + 2 > j) { continue; } char c5 = text[index]; char c6 = text[index2]; if (c5 == '\r' && c6 == '\n') { if (!flag2) { num4++; builder.Append('\r'); builder.Append('\n'); flag2 = true; } num4++; builder.Append('\r'); builder.Append('\n'); c2 = '\n'; k++; } } else { flag2 = false; c3 = c4; } } if (num4 - 1 != num3) { flag = true; } } else { flag = true; } if (whitespaceBetweenWords && !char.IsWhiteSpace(c2) && builder.Length > 0 && builder[builder.Length - 1] != ' ') { flag = true; builder.Append(' '); } i = j; num = -1; } else if (!char.IsWhiteSpace(c)) { if (num != -1) { for (int l = num; l < i; l++) { builder.Append(text[l]); } num = -1; } builder.Append(c); } else if (num == -1) { num = i; } } if (num != -1) { for (int m = num; m < i; m++) { builder.Append(text[m]); } } if (flag) { return builder.ToString(); } return text; } private static string SurroundWithWhitespace(string text, string leadingWhitespace, string trailingWhitespace, ref StringBuilder builder) { if (leadingWhitespace != null || trailingWhitespace != null) { if (builder != null) { builder.Length = 0; } else if (builder == null) { builder = new StringBuilder(64); } if (leadingWhitespace != null) { builder.Append(leadingWhitespace); } builder.Append(text); if (trailingWhitespace != null) { builder.Append(trailingWhitespace); } return builder.ToString(); } return text; } public UntranslatedText(string originalText, bool isFromSpammingComponent, bool removeInternalWhitespace, bool whitespaceBetweenWords, bool enableTemplating, bool templateAllNumbersAway) { IsFromSpammingComponent = isFromSpammingComponent; Original_Text = originalText; if (enableTemplating) { if (isFromSpammingComponent) { TemplatedText = originalText.TemplatizeByNumbers(); if (TemplatedText != null) { originalText = TemplatedText.Template; } } else { TemplatedText = (templateAllNumbersAway ? originalText.TemplatizeByReplacementsAndNumbers() : originalText.TemplatizeByReplacements()); if (TemplatedText != null) { originalText = TemplatedText.Template; } } } TemplatedOriginal_Text = originalText; bool isTemplated = IsTemplated; int i = 0; int num = 0; int num2 = 0; StringBuilder stringBuilder = null; for (; i < originalText.Length && char.IsWhiteSpace(originalText[i]); i++) { if (stringBuilder == null) { stringBuilder = new StringBuilder(64); } stringBuilder.Append(originalText[i]); } if (i != 0) { LeadingWhitespace = stringBuilder?.ToString(); } StringBuilder builder = stringBuilder; if (i != originalText.Length) { i = originalText.Length - 1; if (builder != null) { builder.Length = 0; } while (i > -1 && char.IsWhiteSpace(originalText[i])) { if (builder == null) { builder = new StringBuilder(64); } builder.Append(originalText[i]); i--; } num2 = i; if (num2 != originalText.Length - 1) { TrailingWhitespace = builder?.Reverse().ToString(); } } int num3 = ((LeadingWhitespace != null) ? LeadingWhitespace.Length : 0); int num4 = ((TrailingWhitespace != null) ? TrailingWhitespace.Length : 0); if (num3 > 0 || num4 > 0) { Original_Text_ExternallyTrimmed = Original_Text.Substring(num3, Original_Text.Length - num4 - num3); } else { Original_Text_ExternallyTrimmed = Original_Text; } if (isTemplated) { TemplatedOriginal_Text_ExternallyTrimmed = TemplatedOriginal_Text.Substring(num3, TemplatedOriginal_Text.Length - num4 - num3); } else { TemplatedOriginal_Text_ExternallyTrimmed = Original_Text_ExternallyTrimmed; } if (removeInternalWhitespace) { Original_Text_FullyTrimmed = PerformInternalTrimming(Original_Text_ExternallyTrimmed, whitespaceBetweenWords, ref builder); bool flag = (object)Original_Text_FullyTrimmed == Original_Text_ExternallyTrimmed; Original_Text_InternallyTrimmed = (flag ? Original_Text : SurroundWithWhitespace(Original_Text_FullyTrimmed, LeadingWhitespace, TrailingWhitespace, ref builder)); } else { Original_Text_FullyTrimmed = Original_Text_ExternallyTrimmed; Original_Text_InternallyTrimmed = Original_Text; } if (isTemplated) { if (removeInternalWhitespace) { TemplatedOriginal_Text_FullyTrimmed = PerformInternalTrimming(TemplatedOriginal_Text_ExternallyTrimmed, whitespaceBetweenWords, ref builder); bool flag2 = (object)TemplatedOriginal_Text_FullyTrimmed == TemplatedOriginal_Text_ExternallyTrimmed; TemplatedOriginal_Text_InternallyTrimmed = (flag2 ? TemplatedOriginal_Text : SurroundWithWhitespace(TemplatedOriginal_Text_FullyTrimmed, LeadingWhitespace, TrailingWhitespace, ref builder)); } else { TemplatedOriginal_Text_FullyTrimmed = TemplatedOriginal_Text_ExternallyTrimmed; TemplatedOriginal_Text_InternallyTrimmed = TemplatedOriginal_Text; } } else { TemplatedOriginal_Text_FullyTrimmed = Original_Text_FullyTrimmed; TemplatedOriginal_Text_InternallyTrimmed = Original_Text_InternallyTrimmed; } } public string Untemplate(string text) { if (TemplatedText != null) { return TemplatedText.Untemplate(text); } return text; } public string PrepareUntranslatedText(string text) { if (TemplatedText != null) { return TemplatedText.PrepareUntranslatedText(text); } return text; } public string FixTranslatedText(string text, bool useTranslatorFriendlyArgs) { if (TemplatedText != null) { return TemplatedText.FixTranslatedText(text, useTranslatorFriendlyArgs); } return text; } public override bool Equals(object obj) { if (obj is UntranslatedText untranslatedText) { return TemplatedOriginal_Text_InternallyTrimmed == untranslatedText.TemplatedOriginal_Text_InternallyTrimmed; } return false; } public override int GetHashCode() { return TemplatedOriginal_Text_InternallyTrimmed.GetHashCode(); } } public class UntranslatedTextInfo { public List ContextBefore { get; } public string UntranslatedText { get; internal set; } public List ContextAfter { get; } internal UntranslatedTextInfo(string untranslatedText) { UntranslatedText = untranslatedText; ContextBefore = new List(); ContextAfter = new List(); } internal UntranslatedTextInfo(string untranslatedText, List contextBefore, List contextAfter) { UntranslatedText = untranslatedText; ContextBefore = contextBefore; ContextAfter = contextAfter; } } } namespace GameTranslator.Patches.Translatons.Manipulator { internal class DefaultTextComponentManipulator : ITextComponentManipulator { private class TypeAndMethod { private FastReflectionDelegate _setterInvoker; public Type Type { get; } public MethodBase SetterMethod { get; } public FastReflectionDelegate SetterInvoker { get { FastReflectionDelegate result; if ((result = _setterInvoker) == null) { result = (_setterInvoker = CustomFastReflectionHelper.CreateFastDelegate(SetterMethod, true, true)); } return result; } } public TypeAndMethod(Type type, MethodBase method) { Type = type; SetterMethod = method; } } public static Action SetCurText = null; private static readonly string TextPropertyName = "text"; private readonly Type _type; private readonly CachedProperty _property; private static Dictionary _textSetters = new Dictionary(); public DefaultTextComponentManipulator(Type type) { _type = type; if (type.GetProperty(TextPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null) { _property = ReflectionCache.CachedProperty(type, TextPropertyName); } else { _property = ReflectionCache.CachedProperty(type, "Text"); } } public string GetText(object ui) { CachedProperty property = _property; return (string)((property != null) ? property.Get(ui) : null); } public void SetText(object ui, string text) { try { Type type = _type; if (UnityTypes.TextWindow != null) { TypeContainer textMeshPro = UnityTypes.TextMeshPro; if (textMeshPro != null && textMeshPro.ClrType.IsAssignableFrom(type)) { Object textWindow = Object.FindObjectOfType(UnityTypes.TextWindow.ClrType); if (textWindow != (Object)null) { BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo field = ((object)textWindow).GetType().GetField("TextMesh", flags); object obj = ((field != null) ? field.GetValue(textWindow) : null); if (obj != null && object.Equals(obj, ui)) { if (new StackTrace().GetFrames().Any((StackFrame x) => x.GetMethod().DeclaringType == UnityTypes.TextWindow.ClrType)) { object previousCurText = ((object)textWindow).GetType().GetField("curText", flags).GetValue(textWindow); ((object)textWindow).GetType().GetField("curText", flags).SetValue(textWindow, text); SetCurText = delegate(object textWindowInner) { object value3 = ((object)textWindow).GetType().GetField("curText", flags).GetValue(textWindow); if (object.Equals(text, value3)) { textWindowInner.GetType().GetMethod("FinishTyping", flags).Invoke(textWindowInner, null); textWindowInner.GetType().GetField("curText", flags).SetValue(textWindowInner, previousCurText); object value4 = textWindowInner.GetType().GetField("TextMesh", flags).GetValue(textWindowInner); object value5 = textWindowInner.GetType().GetField("Keyword", flags).GetValue(textWindowInner); value5.GetType().GetMethod("UpdateTextMesh", flags).Invoke(value5, new object[2] { value4, true }); } SetCurText = null; }; } else { CachedProperty val = ReflectionCache.CachedProperty(type, TextPropertyName); if (val != null) { val.Set(ui, (object)text); } object value = ((object)textWindow).GetType().GetField("curText", flags).GetValue(textWindow); ((object)textWindow).GetType().GetField("curText", flags).SetValue(textWindow, text); ((object)textWindow).GetType().GetMethod("FinishTyping", flags).Invoke(textWindow, null); ((object)textWindow).GetType().GetField("curText", flags).SetValue(textWindow, value); object value2 = ((object)textWindow).GetType().GetField("Keyword", flags).GetValue(textWindow); value2.GetType().GetMethod("UpdateTextMesh", flags).Invoke(value2, new object[2] { obj, true }); } return; } } } } CachedProperty property = _property; if (property != null) { property.Set(ui, (object)text); } CachedProperty val2 = ReflectionCache.CachedProperty(type, "maxVisibleCharacters"); if (val2 != null && val2.PropertyType == typeof(int)) { int num = (int)val2.Get(ui); if (0 < num && num < 99999) { val2.Set(ui, (object)99999); } } if (TextExpansion_Methods.SetMessageType != null && TextExpansion_Methods.SkipTypeWriter != null && UnityTypes.TextExpansion.ClrType.IsAssignableFrom(type)) { TextExpansion_Methods.SetMessageType.Invoke(ui, (object)1); TextExpansion_Methods.SkipTypeWriter.Invoke(ui); } } catch (IndexOutOfRangeException ex) { ManualLogSource val3 = Logger.CreateLogSource("GameTranslator"); val3.LogError((object)("IndexOutOfRangeException in DefaultTextComponentManipulator.SetText: " + ex.Message)); } catch (NullReferenceException) { } catch (Exception ex3) { ManualLogSource val4 = Logger.CreateLogSource("GameTranslator"); val4.LogError((object)("Exception in DefaultTextComponentManipulator.SetText: " + ex3.Message)); } } private static TypeAndMethod GetTextPropertySetterInParent(Type type) { Type baseType = type.BaseType; while (baseType != null) { if (_textSetters.TryGetValue(type, out var value)) { return value; } PropertyInfo property = baseType.GetProperty(TextPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(property != null) || !property.CanWrite) { baseType = baseType.BaseType; continue; } TypeAndMethod typeAndMethod = new TypeAndMethod(baseType, property.GetSetMethod()); _textSetters[baseType] = typeAndMethod; return typeAndMethod; } return null; } } internal class FairyGUITextComponentManipulator : ITextComponentManipulator { private readonly CachedField _html; private readonly CachedProperty _htmlText; private readonly CachedProperty _text; public FairyGUITextComponentManipulator() { _html = ReflectionCache.CachedField(UnityTypes.TextField.ClrType, "html") ?? ReflectionCache.CachedFieldByIndex(UnityTypes.TextField.ClrType, 3, typeof(bool), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _text = ReflectionCache.CachedProperty(UnityTypes.TextField.ClrType, "text"); _htmlText = ReflectionCache.CachedProperty(UnityTypes.TextField.ClrType, "htmlText"); } public string GetText(object ui) { if ((bool)_html.Get(ui)) { return (string)_htmlText.Get(ui); } return (string)_text.Get(ui); } public void SetText(object ui, string text) { if ((bool)_html.Get(ui)) { _htmlText.Set(ui, (object)text); } else { _text.Set(ui, (object)text); } } } internal interface ITextComponentManipulator { string GetText(object ui); void SetText(object ui, string text); } internal class TextArea2DComponentManipulator : ITextComponentManipulator { private readonly Action set_status; private readonly Action set_textData; private readonly Action set_nameText; private readonly Action set_isInputSendMessage; private readonly CachedProperty _text; private readonly CachedProperty _TextData; public TextArea2DComponentManipulator() { FieldInfo field = UnityTypes.AdvPage.ClrType.GetField("textData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); set_textData = CustomFastReflectionHelper.CreateFastFieldSetter(field); FieldInfo field2 = UnityTypes.AdvPage.ClrType.GetField("status", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); set_status = CustomFastReflectionHelper.CreateFastFieldSetter(field2); FieldInfo field3 = UnityTypes.AdvPage.ClrType.GetField("isInputSendMessage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); set_isInputSendMessage = CustomFastReflectionHelper.CreateFastFieldSetter(field3); FieldInfo field4 = UnityTypes.AdvPage.ClrType.GetField("nameText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); set_nameText = CustomFastReflectionHelper.CreateFastFieldSetter(field4); _text = ReflectionCache.CachedProperty(UnityTypes.TextArea2D.ClrType, "text"); _TextData = ReflectionCache.CachedProperty(UnityTypes.TextArea2D.ClrType, "TextData"); } public string GetText(object ui) { object obj = _TextData.Get(ui); if (obj != null) { return ExtensionDataHelper.GetExtensionData(obj); } return (string)_text.Get(ui); } public void SetText(object ui, string text) { if (UnityTypes.AdvUiMessageWindow != null && UnityTypes.AdvPage != null) { Object val = Object.FindObjectOfType(UnityTypes.AdvUiMessageWindow.UnityType); object objA = AdvUiMessageWindow_Fields.text.Get((object)val); object objA2 = AdvUiMessageWindow_Fields.nameText.Get((object)val); if (object.Equals(objA, ui)) { Object arg = Object.FindObjectOfType(UnityTypes.AdvPage.UnityType); object obj = Activator.CreateInstance(UnityTypes.TextData.ClrType, text); _TextData.Set(ui, obj); set_textData(arg, obj); set_status(arg, 0); set_isInputSendMessage(arg, arg2: false); return; } if (object.Equals(objA2, ui)) { Object arg2 = Object.FindObjectOfType(UnityTypes.AdvPage.UnityType); object obj2 = Activator.CreateInstance(UnityTypes.TextData.ClrType, text); _TextData.Set(ui, obj2); set_nameText(arg2, text); return; } } object obj3 = Activator.CreateInstance(UnityTypes.TextData.ClrType, text); _text.Set(ui, (object)text); _TextData.Set(ui, obj3); } } internal class UguiNovelTextComponentManipulator : ITextComponentManipulator { private static readonly string TextPropertyName = "text"; private readonly CachedProperty _property; private readonly Type _type; public UguiNovelTextComponentManipulator(Type type) { _type = type; _property = ReflectionCache.CachedProperty(type, TextPropertyName); } public string GetText(object ui) { return (string)_property.Get(ui); } public void SetText(object ui, string text) { _property.Set(ui, (object)text); UguiNovelText_Methods.SetAllDirty.Invoke(ui); object obj = UguiNovelText_Properties.TextGenerator.Get(ui); UguiNovelTextGenerator_Methods.Refresh.Invoke(obj); } } } namespace GameTranslator.Patches.InteractiveTerminalAPI { internal class InteractiveTerminalAPIPatch { private static bool _isInteractiveTerminalAPIAvailable; private static Assembly _interactiveTerminalAPIAssembly; private static Harmony _harmony; public static void Initialize(Harmony harmony) { try { _harmony = harmony; _isInteractiveTerminalAPIAvailable = IsInteractiveTerminalAPIAvailable(); if (_isInteractiveTerminalAPIAvailable) { TranslatePlugin.logger.LogInfo((object)"InteractiveTerminalAPI translation module initialized"); RegisterTranslationHandlers(); } else { TranslatePlugin.logger.LogInfo((object)"InteractiveTerminalAPI not found, translation module disabled"); } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Failed to initialize InteractiveTerminalAPI: " + ex.Message)); _isInteractiveTerminalAPIAvailable = false; } } private static bool IsInteractiveTerminalAPIAvailable() { try { _interactiveTerminalAPIAssembly = Assembly.Load("InteractiveTerminalAPI"); return _interactiveTerminalAPIAssembly != null; } catch (Exception) { return false; } } private static void RegisterTranslationHandlers() { try { if (_isInteractiveTerminalAPIAvailable && !(_interactiveTerminalAPIAssembly == null)) { PatchGetTextMethods(); } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Failed to register InteractiveTerminalAPI translation handlers: " + ex.Message)); } } private static void PatchGetTextMethods() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown string[] array = new string[2] { "InteractiveTerminalAPI.UI.Page.PageElement", "InteractiveTerminalAPI.UI.Screen.BoxedScreen" }; string[] array2 = array; foreach (string name in array2) { Type type = _interactiveTerminalAPIAssembly.GetType(name); if (!(type != null)) { continue; } MethodInfo method = type.GetMethod("GetText", BindingFlags.Instance | BindingFlags.Public); if (method != null) { try { _harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(InteractiveTerminalAPIPatch).GetMethod("GetTextPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); TranslatePlugin.logger.LogInfo((object)("Patched " + type.Name + ".GetText method")); } catch (Exception arg) { TranslatePlugin.logger.LogWarning((object)$"Failed to patch {type.Name}.GetText: {arg}"); } } } } private static void GetTextPostfix(ref string __result) { if (!string.IsNullOrEmpty(__result) && TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value) { __result = TranslateInteractiveText(__result); } } public static string TranslateInteractiveText(string text) { if (string.IsNullOrEmpty(text) || !TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value) { return text; } try { if (TranslatePlugin.showAvailableText.Value && TextTranslate.ShouldOutputDebug("InteractiveTerminalAPI:" + text)) { TranslatePlugin.LogInfo("[Debug] InteractiveTerminalAPI available text: '" + text + "'"); } if (TranslateConfig.interactiveTerminalAPI != null && TranslateConfig.interactiveTerminalAPI.shouldTranslate) { string text2 = TranslateConfig.replaceByMap(text, TranslateConfig.interactiveTerminalAPI); if (TranslatePlugin.showAvailableText.Value && TranslatePlugin.showOtherDebug.Value && TextTranslate.ShouldOutputDebug("InteractiveTerminalAPI_translated:" + text2)) { TranslatePlugin.LogInfo("[Debug] InteractiveTerminalAPI translated: '" + text2 + "'"); } return text2; } return text; } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error translating InteractiveTerminalAPI text: " + ex.Message)); return text; } } } } namespace GameTranslator.Patches.Hooks { [HarmonyPatch(typeof(GameObject))] internal class GameObjectHook { [HarmonyPostfix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Active(ref GameObject __instance, bool value) { if (!value) { return; } Component[] componentsInChildren = __instance.GetComponentsInChildren(UnityTypes.TextMesh.UnityType); foreach (Component ui in componentsInChildren) { if (ui.IsComponentActive()) { TextTranslate.Instance.Hook_TextChanged(ui); } } } [HarmonyPostfix] [HarmonyPatch("SetActive", new Type[] { typeof(bool) })] public static void SetActive(ref GameObject __instance, bool value) { if (!value) { return; } Component[] componentsInChildren = __instance.GetComponentsInChildren(UnityTypes.TextMesh.UnityType); foreach (Component ui in componentsInChildren) { if (ui.IsComponentActive()) { TextTranslate.Instance.Hook_TextChanged(ui); } } } } [HarmonyPatch(typeof(GUIContent))] internal class GuiContentHook { public static Dictionary keys = new Dictionary(); public static FieldInfo s_Text = typeof(GUIContent).GetField("s_Text", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo s_TextImage = typeof(GUIContent).GetField("s_TextImage", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo m_Text = typeof(GUIContent).GetField("m_Text", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Init(ref GUIContent __instance, ref string text, Texture image, string tooltip) { Hook_TextChanged(TextTranslate.Instance, __instance, ref text, null, TranslateConfig.gui); } [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Change(ref GUIContent __instance, ref string value) { Hook_TextChanged(TextTranslate.Instance, __instance, ref value, null, TranslateConfig.gui); } [HarmonyPrefix] [HarmonyPatch("Temp", new Type[] { typeof(string) })] public static void Temp(ref string t) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Hook_TextChanged(TextTranslate.Instance, (object)(GUIContent)s_Text.GetValue(typeof(GUIContent)), ref t, null, TranslateConfig.gui); } [HarmonyPrefix] [HarmonyPatch("Temp", new Type[] { typeof(string), typeof(string) })] public static void Temp(ref string t, string tooltip) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Hook_TextChanged(TextTranslate.Instance, (object)(GUIContent)s_Text.GetValue(typeof(GUIContent)), ref t, null, TranslateConfig.gui); } [HarmonyPrefix] [HarmonyPatch("Temp", new Type[] { typeof(string), typeof(Texture) })] public static void Temp(ref string t, Texture i) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Hook_TextChanged(TextTranslate.Instance, (object)(GUIContent)s_TextImage.GetValue(typeof(GUIContent)), ref t, null, TranslateConfig.gui); } [HarmonyPrefix] [HarmonyPatch("Temp", new Type[] { typeof(string[]) })] public static void Temp(ref string[] texts) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown for (int i = 0; i < texts.Length; i++) { Hook_TextChanged(TextTranslate.Instance, (object)(GUIContent)s_Text.GetValue(typeof(GUIContent)), ref texts[i], null, TranslateConfig.gui); } } internal static void Hook_TextChanged(TextTranslate textTranslate, object ui, ref string value, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config) { if (!TranslatePlugin.shouldTranslateGui.Value) { return; } if (value != null && value.Length <= TranslatePlugin.syncTranslationThreshold.Value) { TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = textTranslate.DiscoverComponent(ui, orCreateTextTranslationInfo); string text = textTranslate.TranslateImmediate(ui, value, orCreateTextTranslationInfo, normalText, config, ignoreComponentState); if (text != null) { value = text; } } else { TextTranslationInfo orCreateTextTranslationInfo2 = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState2 = textTranslate.DiscoverComponent(ui, orCreateTextTranslationInfo2); value = textTranslate.TranslateOrQueue(ui, value, orCreateTextTranslationInfo2, normalText, config, ignoreComponentState2); } } } [HarmonyPatch(typeof(TextMeshPro))] internal class TeshMeshProHook { [HarmonyPrefix] [HarmonyPatch("OnEnable")] public static void Change(ref TextMeshPro __instance) { TextTranslate.Instance.Hook_TextChanged(__instance); } } public static class TextMeshProHooks { public static readonly Type[] All = new Type[9] { typeof(TeshMeshProUGUIHook), typeof(TextMeshProHook), typeof(TMP_Text_text_Hook), typeof(TMP_Text_SetText_Hook1), typeof(TMP_Text_SetText_Hook2), typeof(TMP_Text_SetText_Hook3), typeof(TMP_Text_SetCharArray_Hook1), typeof(TMP_Text_SetCharArray_Hook2), typeof(TMP_Text_SetCharArray_Hook3) }; } [HarmonyPatch(typeof(TextMeshProUGUI))] internal class TeshMeshProUGUIHook { public static Dictionary keys = new Dictionary(); [HarmonyPostfix] [HarmonyPatch("OnEnable")] public static void OnEnablePostfix(TextMeshProUGUI __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TeshMeshProUGUIHook.OnEnable: " + ex.Message)); } } } public static bool ShouldTranslate(string text) { if (text.Length >= TranslateConfig.text.shouldTranslateMinLength - 4) { return text.Any(char.IsLetter); } return false; } } [HarmonyPatch(typeof(TextMeshPro))] internal class TextMeshProHook { [HarmonyPostfix] [HarmonyPatch("OnEnable")] public static void OnEnablePostfix(TextMeshPro __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TextMeshProHook.OnEnable: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_text_Hook { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetSetMethod(); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_text_Hook: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetText_Hook1 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetText", new Type[1] { typeof(string) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetText_Hook1: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetText_Hook2 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetText", new Type[2] { typeof(string), typeof(bool) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetText_Hook2: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetText_Hook3 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetText", new Type[4] { typeof(string), typeof(float), typeof(float), typeof(float) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetText_Hook3: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetCharArray_Hook1 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetCharArray", new Type[1] { typeof(char[]) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetCharArray_Hook1: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetCharArray_Hook2 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetCharArray", new Type[3] { typeof(char[]), typeof(int), typeof(int) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetCharArray_Hook2: " + ex.Message)); } } } } [HarmonyPatch] internal static class TMP_Text_SetCharArray_Hook3 { private static bool Prepare() { return typeof(TMP_Text) != null; } private static MethodBase TargetMethod() { return typeof(TMP_Text).GetMethod("SetCharArray", new Type[3] { typeof(int[]), typeof(int), typeof(int) }); } [HarmonyPostfix] private static void Postfix(TMP_Text __instance) { try { TextTranslate.Instance.Hook_TextChanged(__instance); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TMP_Text_SetCharArray_Hook3: " + ex.Message)); } } } } [HarmonyPatch(typeof(Texture2D))] internal class TextArea2DHook { } [HarmonyPatch(typeof(TextField))] internal class TextFieldHook { } [HarmonyPatch(typeof(Text))] internal class TextHook { public static FieldInfo m_Text = typeof(Text).GetField("m_Text", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); [HarmonyPrefix] [HarmonyPatch("OnEnable")] public static void Change(ref Text __instance) { TextTranslate.Instance.Hook_TextChanged(__instance); } [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Change(ref Text __instance, ref string value) { TextTranslate.Instance.Hook_TextChanged(__instance, ref value); } } [HarmonyPatch(typeof(TextMesh))] internal class TextMeshHook { [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Change(ref TextMesh __instance, ref string value) { TextTranslate.Instance.Hook_TextChanged(__instance, ref value); } } [HarmonyPatch(typeof(Texture2D))] internal class Texture2DHook { [MethodImpl(MethodImplOptions.NoInlining)] [HarmonyPostfix] [HarmonyPatch("LoadRawTextureData", new Type[] { typeof(byte[]) })] public static void LoadRawTextureData(Texture2D __instance, byte[] __0) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown try { if (TextureTranslate.ImageHooksEnabled && TranslatePlugin.changeTexture.Value && (Object)(object)__instance != (Object)null) { int num = (int)__instance.format; if (num != 1 && num != 9 && num != 63) { TextureTranslate.Instance.Hook_ImageChanged(ref __instance, isPrefixHooked: false); } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred in Texture2D.LoadRawTextureData hook."); } } } [HarmonyPatch(typeof(TMP_FontAsset))] internal class TMP_FontAssetHook { [HarmonyPostfix] [HarmonyPatch("Awake")] [HarmonyWrapSafe] public static void TMP_FontAsset_Awake(TMP_FontAsset __instance) { FontSupportChecker.RegisterFont(__instance); } } [HarmonyPatch(typeof(TMP_Text))] internal class TMP_TextHook { [HarmonyPrefix] [HarmonyPatch("SetTextInternal")] public static void SetTextInternal(ref TMP_Text __instance, ref string sourceText) { TextTranslate.Instance.Hook_TextChanged(__instance, ref sourceText); ReplaceUnsupportedCharacters(ref sourceText, __instance); } [HarmonyPrefix] [HarmonyPatch("SetText", new Type[] { typeof(string), typeof(bool) })] public static void SetText(ref TMP_Text __instance, ref string sourceText, bool syncTextInputBox) { TextTranslate.Instance.Hook_TextChanged(__instance, ref sourceText); ReplaceUnsupportedCharacters(ref sourceText, __instance); } [HarmonyPrefix] [HarmonyPatch("SetText", new Type[] { typeof(string), typeof(float), typeof(float), typeof(float), typeof(float), typeof(float), typeof(float), typeof(float), typeof(float) })] public static void SetText(ref TMP_Text __instance, ref string sourceText, float arg0, float arg1, float arg2, float arg3, float arg4, float arg5, float arg6, float arg7) { TextTranslate.Instance.Hook_TextChanged(__instance, ref sourceText); ReplaceUnsupportedCharacters(ref sourceText, __instance); } [HarmonyPrefix] [HarmonyPatch("SetText", new Type[] { typeof(StringBuilder), typeof(int), typeof(int) })] public static void SetText(ref TMP_Text __instance, ref StringBuilder sourceText, int start, int length) { string value = sourceText.ToString(); TextTranslate.Instance.Hook_TextChanged(__instance, ref value); ReplaceUnsupportedCharacters(ref value, __instance); sourceText = new StringBuilder(value); } [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Change(ref TMP_Text __instance, ref string value) { TextTranslate.Instance.Hook_TextChanged(__instance, ref value); ReplaceUnsupportedCharacters(ref value, __instance); } private static void ReplaceUnsupportedCharacters(ref string text, TMP_Text textComponent) { if (!string.IsNullOrEmpty(text) && TranslatePlugin.replaceUnsupportedCharacters.Value) { string text2 = FontSupportChecker.ReplaceUnsupportedCharacters(text); if (text2 != text) { text = text2; } } } } internal static class UIElementsHooks { public static readonly Type[] All = new Type[1] { typeof(TextElement_text_Hook) }; } [HarmonyPatch(typeof(TextElement))] internal class TextElement_text_Hook { [HarmonyPrefix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Change(ref TextElement __instance, ref string value) { TextTranslate.Instance.Hook_TextChanged(__instance, ref value); } } } namespace GameTranslator.Patches.Hooks.texture { internal static class CubismRenderer_MainTexture_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.CubismRenderer != null; } private static MethodBase TargetMethod(object instance) { PropertyInfo propertyInfo = AccessToolsShim.Property(UnityTypes.CubismRenderer.ClrType, "MainTexture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Prefix(Component __instance, ref Texture2D value) { TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref value, isPrefixHooked: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance, ref Texture2D value) { Prefix(__instance, ref value); _original(__instance, value); } } internal static class CubismRenderer_TryInitialize_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.CubismRenderer != null; } private static MethodBase TargetMethod(object instance) { return AccessToolsShim.Method(UnityTypes.CubismRenderer.ClrType, "TryInitialize", Array.Empty()); } public static void Prefix(Component __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: true, onEnable: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance) { Prefix(__instance); _original(__instance); } } internal static class Cursor_SetCursor_Hook { private static Action _original; private static bool Prepare(object instance) { return true; } private static MethodBase TargetMethod(object instance) { return AccessToolsShim.Method(typeof(Cursor), "SetCursor", new Type[3] { typeof(Texture2D), typeof(Vector2), typeof(CursorMode) }); } public static void Prefix(ref Texture2D texture) { TextureTranslate.Instance.Hook_ImageChanged(ref texture, isPrefixHooked: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Texture2D texture, Vector2 arg2, CursorMode arg3) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) Prefix(ref texture); _original(texture, arg2, arg3); } } internal static class DicingTextures_GetTexture_Hook { private static Func _original; private static bool Prepare(object instance) { return UnityTypes.DicingTextures != null; } private static MethodBase TargetMethod(object instance) { TypeContainer dicingTextures = UnityTypes.DicingTextures; return AccessToolsShim.Method((dicingTextures != null) ? dicingTextures.ClrType : null, "GetTexture", new Type[1] { typeof(string) }); } public static void Postfix(object __instance, ref Texture2D __result) { TextureTranslate.Instance.Hook_ImageChanged(ref __result, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static Texture2D MM_Detour(object __instance, string arg1) { Texture2D __result = _original(__instance, arg1); Postfix(__instance, ref __result); return __result; } } internal static class ImageHooks { public static readonly Type[] All = new Type[20] { typeof(MaskableGraphic_OnEnable_Hook), typeof(Image_sprite_Hook), typeof(Image_overrideSprite_Hook), typeof(Image_material_Hook), typeof(RawImage_texture_Hook), typeof(Cursor_SetCursor_Hook), typeof(Material_mainTexture_Hook), typeof(CubismRenderer_MainTexture_Hook), typeof(CubismRenderer_TryInitialize_Hook), typeof(UIAtlas_spriteMaterial_Hook), typeof(UISprite_OnInit_Hook), typeof(UISprite_material_Hook), typeof(UISprite_atlas_Hook), typeof(UI2DSprite_sprite2D_Hook), typeof(UI2DSprite_material_Hook), typeof(UITexture_mainTexture_Hook), typeof(UITexture_material_Hook), typeof(UIPanel_clipTexture_Hook), typeof(UIRect_OnInit_Hook), typeof(DicingTextures_GetTexture_Hook) }; public static readonly Type[] Sprite = new Type[1] { typeof(Sprite_texture_Hook) }; public static readonly Type[] SpriteRenderer = new Type[1] { typeof(SpriteRenderer_sprite_Hook) }; } internal static class Image_material_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.Image != null; } private static MethodBase TargetMethod(object instance) { TypeContainer image = UnityTypes.Image; PropertyInfo propertyInfo = AccessToolsShim.Property((image != null) ? image.ClrType : null, "material"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(Component __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance, Material value) { _original(__instance, value); Postfix(__instance); } } internal static class Image_overrideSprite_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.Image != null; } private static MethodBase TargetMethod(object instance) { TypeContainer image = UnityTypes.Image; PropertyInfo propertyInfo = AccessToolsShim.Property((image != null) ? image.ClrType : null, "overrideSprite"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(Component __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance, Sprite value) { _original(__instance, value); Postfix(__instance); } } internal static class Image_sprite_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.Image != null; } private static MethodBase TargetMethod(object instance) { TypeContainer image = UnityTypes.Image; PropertyInfo propertyInfo = AccessToolsShim.Property((image != null) ? image.ClrType : null, "sprite"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(Component __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance, Sprite value) { _original(__instance, value); Postfix(__instance); } } internal static class MaskableGraphic_OnEnable_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.MaskableGraphic != null; } private static MethodBase TargetMethod(object instance) { TypeContainer maskableGraphic = UnityTypes.MaskableGraphic; return AccessToolsShim.Method((maskableGraphic != null) ? maskableGraphic.ClrType : null, "OnEnable", Array.Empty()); } public static void Postfix(Component __instance) { Type unityType = ObjectExtensions.GetUnityType((object)__instance); if ((UnityTypes.Image != null && UnityTypes.Image.IsAssignableFrom(unityType)) || (UnityTypes.RawImage != null && UnityTypes.RawImage.IsAssignableFrom(unityType))) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false, onEnable: true); } } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance) { _original(__instance); Postfix(__instance); } } internal static class Material_mainTexture_Hook { private static Action _original; private static bool Prepare(object instance) { return true; } private static MethodBase TargetMethod(object instance) { PropertyInfo propertyInfo = AccessToolsShim.Property(typeof(Material), "mainTexture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Prefix(Material __instance, ref Texture value) { Texture2D texture = default(Texture2D); if (ObjectExtensions.TryCastTo((object)value, ref texture)) { TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: true); value = (Texture)(object)texture; } } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Material __instance, ref Texture value) { Prefix(__instance, ref value); _original(__instance, value); } } internal static class RawImage_texture_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.RawImage != null; } private static MethodBase TargetMethod(object instance) { TypeContainer rawImage = UnityTypes.RawImage; PropertyInfo propertyInfo = AccessToolsShim.Property((rawImage != null) ? rawImage.ClrType : null, "texture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Prefix(Component __instance, ref Texture value) { Texture2D texture = default(Texture2D); if (ObjectExtensions.TryCastTo((object)value, ref texture)) { TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: true); value = (Texture)(object)texture; } } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(Component __instance, Texture value) { Prefix(__instance, ref value); _original(__instance, value); } } internal static class SpriteRenderer_sprite_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.SpriteRenderer != null; } private static MethodBase TargetMethod(object instance) { TypeContainer spriteRenderer = UnityTypes.SpriteRenderer; PropertyInfo propertyInfo = AccessToolsShim.Property((spriteRenderer != null) ? spriteRenderer.ClrType : null, "sprite"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Prefix(SpriteRenderer __instance, ref Sprite value) { bool imageHooksEnabled = TextureTranslate.ImageHooksEnabled; TextureTranslate.ImageHooksEnabled = false; Texture2D texture; try { texture = value.texture; } finally { TextureTranslate.ImageHooksEnabled = imageHooksEnabled; } TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref value, ref texture, isPrefixHooked: true, onEnable: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(SpriteRenderer __instance, Sprite sprite) { Prefix(__instance, ref sprite); _original(__instance, sprite); } } internal static class Sprite_texture_Hook { private static Func _original; private static bool Prepare(object instance) { return UnityTypes.Sprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer sprite = UnityTypes.Sprite; PropertyInfo propertyInfo = AccessToolsShim.Property((sprite != null) ? sprite.ClrType : null, "texture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetGetMethod(); } private static void Postfix(ref Texture2D __result) { TextureTranslate.Instance.Hook_ImageChanged(ref __result, isPrefixHooked: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static Texture2D MM_Detour(Sprite __instance) { Texture2D __result = _original(__instance); Postfix(ref __result); return __result; } } internal static class UI2DSprite_material_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UI2DSprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uI2DSprite = UnityTypes.UI2DSprite; PropertyInfo propertyInfo = AccessToolsShim.Property((uI2DSprite != null) ? uI2DSprite.ClrType : null, "material"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } internal static class UI2DSprite_sprite2D_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UI2DSprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uI2DSprite = UnityTypes.UI2DSprite; PropertyInfo propertyInfo = AccessToolsShim.Property((uI2DSprite != null) ? uI2DSprite.ClrType : null, "sprite2D"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } internal static class UIAtlas_spriteMaterial_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UIAtlas != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uIAtlas = UnityTypes.UIAtlas; PropertyInfo propertyInfo = AccessToolsShim.Property((uIAtlas != null) ? uIAtlas.ClrType : null, "spriteMaterial"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, Material value) { _original(__instance, value); Postfix(__instance); } } internal static class UIPanel_clipTexture_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UIPanel != null; } private static MethodBase TargetMethod(object instance) { PropertyInfo propertyInfo = AccessToolsShim.Property(UnityTypes.UIPanel.ClrType, "clipTexture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } internal static class UIRect_OnInit_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UIRect != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uIRect = UnityTypes.UIRect; return AccessToolsShim.Method((uIRect != null) ? uIRect.ClrType : null, "OnInit", Array.Empty()); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false, onEnable: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance) { _original(__instance); Postfix(__instance); } } internal static class UISprite_atlas_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UISprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uISprite = UnityTypes.UISprite; PropertyInfo propertyInfo = AccessToolsShim.Property((uISprite != null) ? uISprite.ClrType : null, "atlas"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } internal static class UISprite_material_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UISprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uISprite = UnityTypes.UISprite; PropertyInfo propertyInfo = AccessToolsShim.Property((uISprite != null) ? uISprite.ClrType : null, "material"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, Material value) { _original(__instance, value); Postfix(__instance); } } internal static class UISprite_OnInit_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UISprite != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uISprite = UnityTypes.UISprite; return AccessToolsShim.Method((uISprite != null) ? uISprite.ClrType : null, "OnInit", Array.Empty()); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false, onEnable: true); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance) { _original(__instance); Postfix(__instance); } } internal static class UITexture_mainTexture_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UITexture != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uITexture = UnityTypes.UITexture; PropertyInfo propertyInfo = AccessToolsShim.Property((uITexture != null) ? uITexture.ClrType : null, "mainTexture"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } internal static class UITexture_material_Hook { private static Action _original; private static bool Prepare(object instance) { return UnityTypes.UITexture != null; } private static MethodBase TargetMethod(object instance) { TypeContainer uITexture = UnityTypes.UITexture; PropertyInfo propertyInfo = AccessToolsShim.Property((uITexture != null) ? uITexture.ClrType : null, "material"); if (!(propertyInfo != null)) { return null; } return propertyInfo.GetSetMethod(); } public static void Postfix(object __instance) { Texture2D texture = null; TextureTranslate.Instance.Hook_ImageChangedOnComponent(__instance, ref texture, isPrefixHooked: false); } private static void MM_Init(object detour) { _original = DetourExtensions.GenerateTrampolineEx>(detour); } private static void MM_Detour(object __instance, object value) { _original(__instance, value); Postfix(__instance); } } }