Decompiled source of Unofficial Simplified Chinese Translation Pack v2.0.6
BepInEx/plugins/XGameTranslator.dll
Decompiled 10 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Timers; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Utils; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; using XGameTranslator.CustomGUI; using XGameTranslator.Events; using XGameTranslator.Extension; using XGameTranslator.Helper; using XGameTranslator.Hooks; using XGameTranslator.Interface; using XGameTranslator.Manager; using XGameTranslator.Model; using XGameTranslator.Temp; using XGameTranslator.Utilities; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("GameTranslator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("GameTranslator")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f1c544d0-229a-4aaa-9a69-b9b831450920")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace XGameTranslator { internal class PluginConfigFile { private static PluginConfigFile _instance = new PluginConfigFile(); private const string name = "TranslatorConfig.cfg"; private readonly ConfigEntry<string> _To; private readonly ConfigEntry<string> _Directory; private readonly ConfigEntry<string> _TextDirectory; public static HUDManager HUDinstance; public static string[] blackList = new string[6] { "/Environment/HangarShip/Player/PlayerNameCanvas/Text (TMP)", "/PlayerUsernameCanvas/Text (TMP)", "/Systems/UI/Canvas/IngamePlayerHUD/BottomLeftCorner/InputField (TMP)/Text Area/Text", "/Canvas/MenuContainer/LobbyList/ListPanel/Scroll View/Viewport/Content/LobbyListItem(Clone)/ServerName", "/Canvas/MenuContainer/LobbyList/ListPanel/Scroll View/Viewport/Content/LobbyListItemChallenge(Clone)/ServerName", "/Environment/HangarShip/Terminal/Canvas/MainContainer/Scroll View/Viewport/InputField (TMP)/Text Area/Text" }; public static string Directory { get { try { return Path.Combine(Paths.BepInExRootPath, Instance._Directory.Value); } catch { return null; } } set { Instance._Directory.Value = value; } } public static string TextDirectory { get { try { return Path.Combine(Paths.BepInExRootPath, Instance._TextDirectory.Value); } catch { return null; } } set { Instance._TextDirectory.Value = value; } } private static PluginConfigFile Instance { get { if (_instance == null) { _instance = new PluginConfigFile(); } return _instance; } } private PluginConfigFile() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "TranslatorConfig.cfg"); ConfigFile val = new ConfigFile(text, true); val.Bind<string>("Language", "From", "en", (ConfigDescription)null); _To = val.Bind<string>("Language", "To", "zh", (ConfigDescription)null); _Directory = val.Bind<string>("Files", "Directory", "config\\Translation", (ConfigDescription)null); _TextDirectory = val.Bind<string>("Files", "TranslationDirectory", "config\\Translation\\" + _To.Value + "\\Text", (ConfigDescription)null); } public static void LoadConfig() { } } internal class PluginConfig { private static readonly string dirPath = Path.Combine(Setting.RootPath, "Translation"); private const string name = ""; public static void Initialize() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown string text = Path.Combine(GetConfigPath(), ""); ConfigFile val = new ConfigFile(text, true); } public static string GetConfigPath() { try { return Paths.ConfigPath; } catch { XGTLogger.Error("ConfigPath Get Failed;"); return null; } } } internal class XGameTranslator : MonoBehaviour { internal static XGameTranslator Current; private bool _initialized = false; private bool _started = false; private bool _TextHookEnable = true; private bool isDisplay = false; private bool debounce = false; public PluginGUI pgui = new PluginGUI(); private Translator translator; private FileManager fileManager; public void Awake() { if (!_initialized) { _initialized = true; fileManager = new FileManager(PluginConfigFile.Directory, PluginConfigFile.TextDirectory); Current = this; FileHelper.Initialize(PluginConfigFile.TextDirectory, PluginConfigFile.Directory); HookingHelper.RunHook(); StartRoot(); } } public void Start() { if (!_started) { _started = true; Awake(); } } public void Update() { if (((ButtonControl)Keyboard.current.homeKey).wasPressedThisFrame && !debounce) { debounce = true; isDisplay = !isDisplay; Utils.SetTimeOut(200.0, delegate { debounce = false; }); } HandleSetting(); } private void HandleSetting() { if (Setting.EnableTranslator) { } if (Setting.EnableAutoTranslator) { } if (!Setting.OnlyReplaceFont) { } } public void OnGUI() { if (isDisplay) { pgui.DrawWindow(); } } public void StartRoot() { foreach (GameObject allRoot in GetAllRoots()) { ManualHook(allRoot); } } public void HookFileChange() { foreach (KeyValuePair<object, object> item in ActiveComponentHelper.GetAllRegisteredComponent()) { if (item.Value is TextModel textModel) { textModel.isTranslate = false; } } } public void ChangeFont(object ui, TextModel textModel) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (ui == null) { return; } Type type = ui.GetType(); if (UnityTypes.Text != null && UnityTypes.Text.IsAssignableFrom(type)) { CachedProperty font = UnityTypes.Text_Properties.Font; Font val = (Font)font.Get(ui); font.Set(ui, ResourceHelper.GetFont()); return; } CachedProperty cachedProperty = type.CachedProperty("font"); List<TMP_FontAsset> fallbackFontAssetTable = new List<TMP_FontAsset> { ResourceHelper.GetTMP_Font() }; TextMeshProUGUI val2 = (TextMeshProUGUI)((ui is TextMeshProUGUI) ? ui : null); if (val2 != null) { ((TMP_Text)val2).font.fallbackFontAssetTable = fallbackFontAssetTable; if (!((Object)((TMP_Text)val2).font).name.Contains("Variant")) { cachedProperty.Set(ui, ResourceHelper.GetTMP_Font()); } } else { cachedProperty.Set(ui, ResourceHelper.GetTMP_Font()); } } private void RecoveryFont(object ui, TextModel textModel) { if (ui == null || textModel.originfont == null) { return; } Type type = ui.GetType(); if (UnityTypes.Text != null && UnityTypes.Text.IsAssignableFrom(type)) { CachedProperty font = UnityTypes.Text_Properties.Font; if (font.Get(ui) != textModel.originfont) { font.Set(ui, textModel.originfont); } return; } CachedProperty cachedProperty = type.CachedProperty("font"); List<TMP_FontAsset> fallbackFontAssetTable = new List<TMP_FontAsset>(); TextMeshProUGUI val = (TextMeshProUGUI)((ui is TextMeshProUGUI) ? ui : null); if (val != null) { ((TMP_Text)val).font.fallbackFontAssetTable = fallbackFontAssetTable; if (!((Object)((TMP_Text)val).font).name.Contains("Variant") && cachedProperty.Get(ui) != textModel.originfont) { cachedProperty.Set(ui, textModel.originfont); } } else if (cachedProperty.Get(ui) != textModel.originfont) { cachedProperty.Set(ui, textModel.originfont); } } public void SetText(object ui, string text) { ui.SetText(text); } public void Hook_TextChanged(object ui, string caller) { if (!_TextHookEnable) { return; } string text = ui.GetText(); if (string.IsNullOrEmpty(text)) { return; } string path = ui.GetPath(); if (!NeedTranslate(text, path)) { return; } try { TextModel orCreateModel = ui.GetOrCreateModel<TextModel>(); if (text == orCreateModel.translate) { return; } orCreateModel.Load(ui); bool flag = DiscoverComponent(ui); if (Setting.EnableTranslator || Setting.OnlyReplaceFont) { ChangeFont(ui, orCreateModel); } else { RecoveryFont(ui, orCreateModel); } if (!Setting.EnableTranslator) { return; } if (text == orCreateModel.origin && text != orCreateModel.translate && !string.IsNullOrEmpty(orCreateModel.translate)) { ui.SetText(orCreateModel.translate); } if (((!(text != orCreateModel.origin) || !(text != orCreateModel.translate)) && orCreateModel.isTranslate) || !ContainWord(text) || !flag) { return; } if (TranslateManager.TryTranslate(text, out var result, fileManager.TranslationFile, path)) { orCreateModel.origin = text; orCreateModel.translate = result; if (text != result) { orCreateModel.isTranslate = true; } ui.SetText(result); } else if (Setting.Debug) { XGTLogger.Message("Not Matched"); } } catch (Exception e) { XGTLogger.Warn(e, "An unexpected error occurred."); } } public bool TryChangeText(string text, out string result, string path) { if (TranslateManager.TryTranslate(text, out result, fileManager.TerminalTransFile, path)) { return true; } return false; } private bool NeedTranslate(string text, string path) { foreach (string item in fileManager.BlackListFile.list) { if (path.Contains(item)) { return false; } } if (NotContainLetter(text)) { return false; } return true; } private bool NotContainLetter(string text) { string text2 = text.Trim(); foreach (char c in text2) { if (IsLetter(c)) { return false; } } return true; } public void TextChanged(object ui, string caller) { if (!_TextHookEnable) { return; } try { _TextHookEnable = false; TextModel orCreateModel = ui.GetOrCreateModel<TextModel>(); if (orCreateModel.EnableHook) { orCreateModel.EnableHook = false; string text = ui.GetText(); if (text == null) { return; } if (text.Length != 0) { if (Setting.EnableTranslator || Setting.OnlyReplaceFont) { ChangeFont(ui, orCreateModel); } else { RecoveryFont(ui, orCreateModel); } if (Setting.EnableTranslator) { bool flag = DiscoverComponent(ui); if (text == orCreateModel.origin && text != orCreateModel.translate) { ui.SetText(orCreateModel.translate); } try { if (((text != orCreateModel.origin && text != orCreateModel.translate) || !orCreateModel.isTranslate) && !ContainWord(text)) { } } catch (Exception ex) { XGTLogger.Error(ex.Message); XGTLogger.Warn($"{orCreateModel.uuid} ==== {orCreateModel} ==== {text} ==== {orCreateModel.origin} ==== {orCreateModel.translate} ==== {orCreateModel.isTranslate}"); } } } orCreateModel.EnableHook = true; } _TextHookEnable = true; } catch (Exception ex2) { XGTLogger.Error(ex2.Message + "=> Error While TextChange"); } } private bool ContainWord(string text) { for (int i = 0; i < text.Length; i++) { if (i - 1 > 0 && IsLetter(text[i]) && IsLetter(text[i - 1])) { return true; } } return false; } private bool IsLetter(char c) { return (c > '`' && c < '{') || (c > '@' && c < '['); } private bool DiscoverComponent(object ui) { return ui.IsComponentActive(); } private void ManualHook(GameObject obj) { if (!((Object)(object)obj != (Object)null)) { return; } Component[] components = obj.GetComponents<Component>(); Component[] array = components; foreach (Component component in array) { if (IsTextComponent(component)) { } } } private IEnumerable<GameObject> GetAllRoots() { GameObject[] objects = ComponentHelper.FindObjectsOfType<GameObject>(); GameObject[] array = objects; foreach (GameObject obj in array) { if ((Object)(object)obj.transform != (Object)null && (Object)(object)obj.transform.parent == (Object)null) { yield return obj; } } } private bool IsTextComponent(object component) { if (component == null) { return false; } Type type = component.GetType(); if (component is GUIContent) { return true; } if (typeof(Text).IsAssignableFrom(type)) { return true; } if (typeof(TextMesh).IsAssignableFrom(type)) { return true; } if (IsKnownTextMeshProType(type)) { return true; } return false; } public static bool IsKnownTextMeshProType(Type type) { if (UnityTypes.TMP_Text != null) { return UnityTypes.TMP_Text.IsAssignableFrom(type); } Type textMeshProUGUI = UnityTypes.TextMeshProUGUI; return ((object)textMeshProUGUI != null && textMeshProUGUI.IsAssignableFrom(type)) || (UnityTypes.TextMeshPro?.IsAssignableFrom(type) ?? false); } } public static class Loader { internal static Plugin env; internal static XGameTranslator Instance; internal static MonoBehaviour MonoInstance; public static void Load(Plugin environment) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown env = environment; if ((Object)(object)environment != (Object)null) { PluginConfigFile.LoadConfig(); Setting.Initialize(); } Instance = new XGameTranslator(); GameObject val = new GameObject("___GamesAutoTranslator") { hideFlags = (HideFlags)61 }; MonoInstance = (MonoBehaviour)(object)val.AddComponent<XGameTranslator>(); Object.DontDestroyOnLoad((Object)(object)val); } } internal class Package { internal const string GUID = "f1c544d0-229a-4aaa-9a69-b9b831450920"; internal const string PACKAGE_ID = "snowflake.plugin.gametranslator"; internal const string NAME = "GameTranslator"; internal const string VERSION = "1.0.0"; internal const string TITLE = "GameTranslator"; internal const string DESCRIPTION = ""; internal const string COPYRIGHT = "Copyright © 2023"; internal const string AUTHOR = "SnowFlake"; internal const string EMAIL = "[email protected]"; public const string CurrentGameVersion = "v49"; } public class Setting { public static string RootPath = GetPath(); private static Setting _instance; private readonly string SECTION = "Translator"; private readonly ConfigEntry<bool> _EnableTranslator; private readonly ConfigEntry<bool> _EnableAutoTranslator; private readonly ConfigEntry<bool> _EnableTextureReplace; private readonly ConfigEntry<bool> _OnlyReplaceFont; private readonly ConfigEntry<bool> _DisplayCredits; private readonly ConfigEntry<bool> _Debug; internal static bool Test = true; internal static bool EnableTranslator { get { return Instance._EnableTranslator.Value; } set { if (value) { Instance._OnlyReplaceFont.Value = false; } Instance._EnableTranslator.Value = value; } } internal static bool EnableTextureReplace { get { return Instance._EnableTextureReplace.Value; } set { Instance._EnableTextureReplace.Value = value; } } internal static bool EnableAutoTranslator { get { return Instance._EnableAutoTranslator.Value; } set { Instance._EnableAutoTranslator.Value = value; } } internal static bool OnlyReplaceFont { get { return Instance._OnlyReplaceFont.Value; } set { if (value) { Instance._EnableTranslator.Value = false; Instance._EnableTextureReplace.Value = false; Instance._EnableAutoTranslator.Value = false; } Instance._OnlyReplaceFont.Value = value; } } internal static bool DisplayCredits { get { return Instance._DisplayCredits.Value; } set { Instance._DisplayCredits.Value = value; } } internal static bool Debug { get { return Instance._Debug != null && Instance._Debug.Value; } set { if (Instance._Debug != null) { Instance._Debug.Value = value; } } } public static Setting Instance { get { if (_instance == null) { _instance = new Setting(); } return _instance; } } private Setting() { if (Object.op_Implicit((Object)(object)Loader.env)) { _EnableTranslator = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "enableTranslator", true, (ConfigDescription)null); _EnableTextureReplace = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "enableTextureReplace", true, (ConfigDescription)null); _EnableAutoTranslator = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "enableAutoTranslator", false, (ConfigDescription)null); _OnlyReplaceFont = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "onlyReplaceFont", false, (ConfigDescription)null); _DisplayCredits = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "displayCredits", true, (ConfigDescription)null); _Debug = ((BaseUnityPlugin)Loader.env).Config.Bind<bool>(SECTION, "debug", false, (ConfigDescription)null); _EnableTranslator.SettingChanged += SettingEvent.EventHandler; _OnlyReplaceFont.SettingChanged += SettingEvent.EventHandler; } } public static void Initialize() { Setting instance = Instance; if (instance._EnableTranslator.Value) { instance._OnlyReplaceFont.Value = !instance._EnableTranslator.Value; } if (instance._OnlyReplaceFont.Value) { instance._EnableTranslator.Value = !instance._OnlyReplaceFont.Value; instance._EnableTextureReplace.Value = !instance._EnableTextureReplace.Value; instance._EnableAutoTranslator.Value = !instance._OnlyReplaceFont.Value; } } private static string GetPath() { try { return Paths.BepInExRootPath; } catch { XGTLogger.Error("RootPath Get Failed;"); return null; } } public static void SetFontOnly() { Instance._EnableTranslator.Value = false; Instance._EnableTextureReplace.Value = false; Instance._EnableAutoTranslator.Value = false; } } public class TranslateManager { public static bool TryTranslate(string text, out string result, TranslationFile tf, string path) { if (text != null) { if (Setting.Debug) { XGTLogger.Message(path ?? ""); } try { if (tf.TryGetValueInDict(text, out result)) { return true; } if (tf.TryGetValueInRegexList(text, out result)) { return true; } if (tf.TryGetValueInSplitRegexList(text, out result)) { return true; } } catch (Exception ex) { XGTLogger.Error(ex.Message + "\n Error While translate"); } } result = null; return false; } public static bool TryTranslate(string text, out string result, TerminalTransFile tf, string path) { if (text != null) { if (!Setting.Debug) { XGTLogger.Message(path ?? ""); } try { if (tf.TryGetValueInDict(text, out result)) { string text2 = SymbolsFilter(text); if (text != text2) { result = text.Replace(text2, result); } return true; } if (tf.TryGetValueInRegexList(text, out result)) { return true; } if (tf.TryGetValueInSplitRegexList(text, out result)) { return true; } } catch (Exception ex) { XGTLogger.Error(ex.Message + "\n Error While translate"); } } result = null; return false; } private static string DivideWord(string text) { return ""; } private static string SymbolsFilter(string input) { char[] trimChars = new char[5] { '.', ' ', '!', ':', '?' }; return input.Trim().TrimEnd(trimChars); } } public class Translator { private readonly string dirPath = Path.Combine(Setting.RootPath, "Translation"); private List<string> translations = new List<string>(); private List<string[]> textList = new List<string[]>(); private List<string[]> regexList = new List<string[]>(); private Dictionary<string, string> dict = new Dictionary<string, string>(); private List<string> untrans = new List<string>(); private FileSystemWatcher watcher; public void Reset() { regexList.Clear(); textList.Clear(); translations.Clear(); } private bool CanTranslate(string text, string[] line) { if (CompleteConsistent(text, line[0])) { return true; } if (IsRegex(text, line[0])) { return true; } return false; } public bool hadTranslate(string text) { return translations.Contains(text); } private bool CompleteConsistent(string a, string b) { if (a == b) { return true; } if (Encoding.Unicode.GetBytes(a) == Encoding.Unicode.GetBytes(b)) { return true; } return false; } private string GetStringFromBytes(byte[] bytes) { string text = ""; foreach (byte b in bytes) { text += $"{b:X2}"; } return text; } private bool IsRegex(string text, string line) { return false; } public string translate(object ui, string text) { if (text != null) { try { string value = null; if (dict.TryGetValue(text, out value)) { return value; } foreach (string key in dict.Keys) { int num = levenshtein(text, key); if (num < 3) { XGTLogger.Message(string.Format("[{0}] With [{1}] Distance = {2}", key.Replace("\n", "\\n"), text.Replace("\n", "\\n"), num)); } } if (TryMatchWithRegex(text, out value)) { return value; } } catch (Exception ex) { XGTLogger.Error(ex.Message + "\n Error While translate"); } } WriteUntranslateFile(ui, text); return text; } private int levenshtein(string str1, string str2) { int[,] array = new int[str1.Length + 1, str2.Length + 1]; for (int i = 0; i <= str1.Length; i++) { array[i, 0] = i; } for (int j = 0; j <= str2.Length; j++) { array[0, j] = j; } for (int k = 1; k <= str1.Length; k++) { for (int l = 1; l <= str2.Length; l++) { int num = ((str1[k - 1] != str2[l - 1]) ? 1 : 0); array[k, l] = Math.Min(Math.Min(array[k - 1, l] + 1, array[k, l - 1] + 1), array[k - 1, l - 1] + num); } } return array[str1.Length, str2.Length]; } private void WriteUntranslateFile(object ui, string text) { string text2 = (text + "=" + text).Replace("\n", "\\n"); foreach (string untran in untrans) { if (untran == text2) { return; } } untrans.Add(text2); string path = Path.Combine(dirPath, GetFullFileName("untranslate")); using (StreamWriter streamWriter = new StreamWriter(path, append: true)) { streamWriter.WriteLine(text2); } using StreamWriter streamWriter2 = new StreamWriter(Path.Combine(dirPath, GetFullFileName("logs")), append: true); streamWriter2.WriteLine(text2); streamWriter2.WriteLine("Path : " + ui.GetPath()); } public bool TryGlobalMatchRegex(string text, out string result) { result = null; foreach (string[] regex2 in regexList) { Regex regex = new Regex(regex2[0]); MatchCollection matchCollection = regex.Matches(text); if (matchCollection.Count <= 0) { continue; } foreach (Match item in matchCollection) { if (!item.Success) { continue; } string[] array = new string[item.Groups.Count - 1]; for (int i = 1; i < item.Groups.Count; i++) { string value = item.Groups[i].Value; if (value != null && value.Length != 0) { XGTLogger.Message($"TryGlobalMatchRegex Param==>{item.Groups.Count}=>{item.Groups[i].Value}"); string value2 = null; if (dict.TryGetValue(value, out value2)) { array[i - 1] = value2; } else if (TryGlobalMatchRegex(value, out value2)) { array[i - 1] = value2; } else { array[i - 1] = value; } } else { array[i - 1] = ""; } } string obj = result; string value3 = item.Value; string format = regex2[1]; object[] args = array; result = obj + HandleSignal(regex, value3, string.Format(format, args)); } return true; } return false; } public bool TryMatchWithRegex(string text, out string result) { result = ""; foreach (string[] regex2 in regexList) { Regex regex = new Regex(regex2[0]); MatchCollection matchCollection = regex.Matches(text); if (matchCollection.Count <= 0) { continue; } if (Setting.Debug) { XGTLogger.Message($"*** Original Text[{text}] Matcher [{regex2[0]} => {matchCollection.Count}] ***"); } foreach (Match item in matchCollection) { if (!item.Success) { continue; } string text2 = ""; if (item.Groups.Count > 1) { string[] array = new string[item.Groups.Count - 1]; for (int i = 1; i < item.Groups.Count; i++) { try { string value = item.Groups[i].Value; if (value != null && value.Length != 0) { string value2 = null; if (TryGetValueInDict(value, out value2)) { array[i - 1] = value2; } else if (TryMatchWithRegex(value, out value2)) { array[i - 1] = value2; } else { array[i - 1] = value; } } else { array[i - 1] = ""; } if (Setting.Debug) { XGTLogger.Message(string.Format("Index[{0}]=> From [{1}] to [{2}] ***", i, value.Replace("\n", "\\n"), array[i - 1].Replace("\n", "\\n"))); } } catch (Exception ex) { if (Setting.Debug) { XGTLogger.Error($"*** Error [{ex.Message} index [{i}] ***"); } } } string value3 = item.Value; string format = regex2[1]; object[] args = array; text2 = HandleSignal(regex, value3, string.Format(format, args)); if (Setting.Debug) { string[] obj = new string[5] { "***Sub From [", item.Value, "] to [", null, null }; string format2 = regex2[1]; args = array; obj[3] = string.Format(format2, args); obj[4] = "] ***"; XGTLogger.Message(string.Concat(obj)); } } else { text2 = item.Value; } result += text.Replace(item.Value, text2); } if (Setting.Debug) { XGTLogger.Message("*** From [" + text.Replace("\n", "\\n") + "] to [" + result.Replace("\n", "\\n") + "] ***"); } return true; } return false; } public bool TryGetValueInDict(string key, out string value) { List<string> list = new List<string>(); string text = key; list.Add(key); if (key.Contains("\n")) { text = text.Replace("\n", ""); list.Add(text); } if (key.StartsWith(" ") || key.EndsWith(" ")) { text = text.Trim(); list.Add(text); } foreach (string item in list) { if (dict.TryGetValue(item, out value)) { value = key.Replace(item, value); return true; } } value = null; return false; } public bool TryMatchRegex(string text, out string result) { foreach (string[] regex2 in regexList) { Regex regex = new Regex(regex2[0]); Match match = regex.Match(text); if (!match.Success) { continue; } XGTLogger.Message($"*** Matcher [{regex2[0]} => {match.Groups.Count}] ***"); string[] array = new string[match.Groups.Count - 1]; for (int i = 1; i < match.Groups.Count; i++) { string value = match.Groups[i].Value; if (value != null && value.Length != 0) { string value2 = null; if (dict.TryGetValue(value, out value2)) { XGTLogger.Message("*** Dict=> [" + value + "=>" + array[i - 1] + "] ***"); array[i - 1] = value2; } else if (TryGlobalMatchRegex(value, out value2)) { array[i - 1] = value2; } else { array[i - 1] = value; } XGTLogger.Message("*** match.Groups [" + value + "=>" + array[i - 1] + "] ***"); } else { array[i - 1] = ""; } } XGTLogger.Message($"=========>MatchParamsLength: [{array.Length}]"); int num = 0; string[] array2 = array; foreach (string arg in array2) { XGTLogger.Message($"=========>Index:{num}=> [{arg}]"); num++; } XGTLogger.Message("* Original Text => " + text + " From[" + regex2[0] + "] to [" + regex2[1] + "] *"); string format = regex2[1]; object[] args = array; result = HandleSignal(regex, text, string.Format(format, args)); object[] obj = new object[4] { result, text, null, null }; string format2 = regex2[1]; args = array; obj[2] = string.Format(format2, args); obj[3] = match.Groups.Count; XGTLogger.Message(string.Format("* Result =>{0} => {1} => {2}=> {3} *", obj)); return true; } result = null; return false; } private string HandleSignal(Regex reg, string text, string replacement) { string text2 = "<dollar-signal/>"; replacement = replacement.Replace("$", text2); return reg.Replace(text, replacement).Replace(text2, "$"); } private bool isNumber(char c) { if (c > '/' && c < ':') { return true; } return false; } private bool isVersion(string version) { if (version.Length == 3 && (version[0] == 'v' || version[0] == 'V') && isNumber(version[1]) && isNumber(version[2])) { return true; } return false; } public bool IsTranslate(string text) { if (text == null) { return true; } foreach (string[] text2 in textList) { if (text.Contains(text2[1])) { return true; } } if (isVersion(text)) { return true; } return false; } public void LoadTranslateFile(string fileName) { XGTLogger.Message(Path.Combine(dirPath, GetFullFileName(fileName))); LoadFile(Path.Combine(dirPath, GetFullFileName(fileName))); } private string GetFullFileName(string fileName) { return fileName + ".txt"; } private void LoadUntranslateFile(string filename) { using StreamReader streamReader = new StreamReader(Path.Combine(dirPath, GetFullFileName(filename)), Encoding.UTF8); string item; while ((item = streamReader.ReadLine()) != null) { untrans.Add(item); } } private void LoadFile(string filePath) { try { Reset(); using StreamReader streamReader = new StreamReader(filePath, Encoding.UTF8); int num = 0; string text; while ((text = streamReader.ReadLine()) != null) { num++; if (text.Length == 0 || isAnnotation(text)) { continue; } text = text.Replace("\\n", "\n"); if (isRegex(ref text)) { string[] array = text.Split(new char[1] { '=' }); if (array.Length == 2) { array[0] = array[0].Replace("<equals />", "="); array[1] = array[1].Replace("<equals />", "="); regexList.Add(array); } else { XGTLogger.Warn($"Unexceptline:{num} ListSplitBy\"=\"Length:{array.Length} IndexZeroValue:{array[0]}"); } } else { string[] array2 = text.Split(new char[1] { '=' }); if (array2.Length == 2) { array2[0] = array2[0].Replace("<equals />", "="); array2[1] = array2[1].Replace("<equals />", "="); dict[array2[0]] = array2[1]; } else { XGTLogger.Warn($"Unexceptline:{num} ListSplitBy\"=\"Length:{array2.Length} IndexZeroValue:{array2[0]}"); } } } } catch (Exception ex) { XGTLogger.Error("An error occurred while reading the file: " + ex.Message); } } private bool isRegex(ref string text) { if (text.StartsWith("<regex>") && text.EndsWith("</regex>")) { text = text.Replace("<regex>", "").Replace("</regex>", ""); return true; } return false; } private bool isAnnotation(string text) { if (text.StartsWith("/*") && text.EndsWith("*/")) { return true; } return false; } } public static class XGTLogger { private static readonly string filePath = Path.Combine(Setting.RootPath, "Translation", "output.txt"); public static ManualLogSource Log = new ManualLogSource("Plugin.Core"); private static void WriteFile(string line) { using StreamWriter streamWriter = new StreamWriter(filePath, append: true, Encoding.UTF8, 4096); streamWriter.WriteLine(line); } public static void Setup() { Logger.Sources.Add((ILogSource)(object)Log); } public static void Message(string message) { Log.LogMessage((object)message); } public static void Message(object message) { Log.LogMessage(message); } public static void Info(string message) { Log.LogInfo((object)message); } public static void Error(string message) { Log.LogError((object)message); } public static void Warn(string message) { Log.LogWarning((object)message); } public static void Warn(Exception e, string message) { string text = e.Message + " " + message; Log.LogWarning((object)text); } } internal class ResourceHelper { private static Font translateFont; private static TMP_FontAsset translateTMP_Font; public static GUISkin skin; private static Dictionary<string, Texture2D> resourceTextures = new Dictionary<string, Texture2D>(); public static void Start() { } public static void LoadResource() { AssetBundle val = AssetBundle.LoadFromFile(Path.Combine(Paths.ConfigPath, "resources")); string text = "ReArial"; translateFont = val.LoadAsset<Font>(text); translateTMP_Font = val.LoadAsset<TMP_FontAsset>(text + " SDF"); skin = val.LoadAsset<GUISkin>("NEW GUISkin"); LoadTextures(val); XGTLogger.Info($"Loaded {translateFont}."); XGTLogger.Info($"Loaded {translateTMP_Font}."); if ((Object)(object)translateFont != (Object)null && (Object)(object)translateTMP_Font != (Object)null) { XGTLogger.Info("Loaded " + text + "."); } else { XGTLogger.Error("The font file is damaged. Please check the file."); } val.Unload(false); } private static void LoadTextures(AssetBundle asset) { Texture2D[] array = asset.LoadAllAssets<Texture2D>(); Texture2D[] array2 = array; foreach (Texture2D val in array2) { resourceTextures[((Object)val).name] = val; } } public static Texture2D TryGetTexture(string name) { if (resourceTextures.TryGetValue(name, out var value)) { return value; } XGTLogger.Warn("Unknow name, Get Texture Failed!"); return null; } public static bool TryGetTexture(string name, out Texture2D texture) { if (resourceTextures.TryGetValue(name, out var value)) { texture = value; return true; } XGTLogger.Warn("Unknow name, Get Texture Failed!"); texture = null; return false; } public static bool Contains(string name) { string[] source = new string[1] { "3270-HUDIngame - Variant" }; if (source.Contains(name)) { return true; } return false; } public static Font GetFont() { return translateFont; } public static TMP_FontAsset GetTMP_Font() { return translateTMP_Font; } } [BepInPlugin("f1c544d0-229a-4aaa-9a69-b9b831450920", "GameTranslator", "1.0.0")] public class Plugin : BaseUnityPlugin { public readonly Harmony harmony = new Harmony("f1c544d0-229a-4aaa-9a69-b9b831450920"); public void Awake() { XGTLogger.Setup(); ResourceHelper.LoadResource(); Loader.Load(this); } } internal static class Utils { public static void SetTimeOut(double interval, Action action) { Timer timer = new Timer(interval); timer.Elapsed += delegate { timer.Enabled = false; action(); }; timer.Enabled = true; } } } namespace XGameTranslator.Temp { internal class TerminalFileEND { private readonly string dirPath = Path.Combine(Setting.RootPath, "Terminal"); private readonly string translateFileName = "text.txt"; private readonly FileSystemWatcher watcher; private readonly List<string> translations = new List<string>(); private readonly List<string[]> textList = new List<string[]>(); private readonly List<string[]> regexList = new List<string[]>(); private readonly Dictionary<string, string> dict = new Dictionary<string, string>(); public void Reset() { regexList.Clear(); textList.Clear(); translations.Clear(); } public bool TryTranslate(string key, out string value) { if (dict.TryGetValue(key, out value)) { return true; } return false; } public TerminalFileEND() { if (Directory.Exists(dirPath)) { watcher = new FileSystemWatcher(dirPath); watcher.Changed += OnFileChanged; watcher.EnableRaisingEvents = true; LoadFile(); } } private void OnFileChanged(object sender, FileSystemEventArgs e) { if (e.ChangeType == WatcherChangeTypes.Changed) { XGTLogger.Message($"File {e.Name} has been {e.ChangeType.ToString().ToLower()} at {DateTime.Now}"); XGameTranslator.Current.HookFileChange(); LoadFile(); } } private void LoadFile() { string filePath = Path.Combine(dirPath, translateFileName); LoadFileInDict(filePath); } private void LoadFileInDict(string filePath) { try { Reset(); using StreamReader streamReader = new StreamReader(filePath, Encoding.UTF8); int num = 0; string text; while ((text = streamReader.ReadLine()) != null) { num++; if (text.Length == 0 || IsAnnotation(text)) { continue; } text = text.Replace("\\n", "\n"); if (IsRegex(ref text)) { string[] array = text.Split(new char[1] { '=' }); if (array.Length == 2) { array[0] = array[0].Replace("<equals />", "="); array[1] = array[1].Replace("<equals />", "="); regexList.Add(array); } else { XGTLogger.Warn($"Unexceptline:{num} ListSplitBy\"=\"Length:{array.Length} IndexZeroValue:{array[0]}"); } } else { string[] array2 = text.Split(new char[1] { '=' }); if (array2.Length == 2) { array2[0] = array2[0].Replace("<equals />", "="); array2[1] = array2[1].Replace("<equals />", "="); dict[array2[0]] = array2[1]; } else { XGTLogger.Warn($"Unexceptline:{num} ListSplitBy\"=\"Length:{array2.Length} IndexZeroValue:{array2[0]}"); } } } } catch (Exception ex) { XGTLogger.Error("An error occurred while reading the file: " + ex.Message); } } private bool IsRegex(ref string text) { if (text.StartsWith("<regex>") && text.EndsWith("</regex>")) { text = text.Replace("<regex>", "").Replace("</regex>", ""); return true; } return false; } private bool IsAnnotation(string text) { if (text.StartsWith("/*") && text.EndsWith("*/")) { return true; } return false; } } public class TerminalTranslateText { private bool firstUse = true; public string translate(string modifiedDisplayText, ref TerminalNode node) { string text = node.displayText.ToString(); if (Setting.DisplayCredits && (firstUse & (((Object)node).name == "OtherCommands"))) { modifiedDisplayText = modifiedDisplayText.Replace(".\n\n\n", "\n\n>TRANSLATE\n查看汉化人员名单\n\n"); } if (text.Contains("[translate]")) { credits(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[currentDay]")) { translateWelcome(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[numberOfItemsOnRoute]")) { translateMenu(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[planetTime]")) { translateCommandMoons(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[unlockablesSelectionList]")) { translateCommandStore(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[currentScannedEnemiesList]")) { translateCommandBestiary(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[storedUnlockablesList]")) { translateCommandStorage(ref modifiedDisplayText); return modifiedDisplayText; } if (text.Contains("[currentUnlockedLogsList]")) { translateCommandSigurd(ref modifiedDisplayText); return modifiedDisplayText; } if (modifiedDisplayText.Length > 180) { if (!translateDetailMoons(ref modifiedDisplayText)) { return modifiedDisplayText; } if (!translateDetailSigurd(ref modifiedDisplayText)) { return modifiedDisplayText; } return modifiedDisplayText; } translateCommandOther(ref modifiedDisplayText); translateMisc(ref modifiedDisplayText); translateDetailStore(ref modifiedDisplayText); return modifiedDisplayText; } public void credits(ref string raw) { raw = "\n\n\nHOME键打开设置.\n当前汉化版本:v1.1. 我们的目标是给玩家带来原汁原味的游戏体验.\n汉化致谢人员名单(以下排名不分先后):\n\n翻译&校对:\nRorschachD2\nvicvic(@炸飞Victor)\n\n中国地区员工QQ群: 245089284\n中转站QQ群: 451982030\n\nmod作者:吹到江心的孤雪\nB站教程视频链接:https://www.bilibili.com/video/BV1vN411L7Ey\n有任何疑问和建议欢迎前往视频评论区留言!\n本汉化包完全免费, 可加群自取最新版.\n请自觉抵制任何未标明汉化包出处或未获得本人授权的行为,共建和谐汉化环境!\n本人及本汉化组保留相关法律追究权利及最终解释权. \n\n\n"; } public void translateWelcome(ref string raw) { } public void translateMenu(ref string raw) { translateMisc(ref raw); } public void translateCommandMoons(ref string raw) { } public void translateCommandStore(ref string raw) { } public void translateCommandBestiary(ref string raw) { } public void translateCommandStorage(ref string raw) { translateCommandStore(ref raw); } public void translateCommandOther(ref string raw) { } public void translateCommandSigurd(ref string raw) { } public bool translateDetailMoons(ref string raw) { string text = raw; if (raw != text) { translateMisc(ref raw); } return raw == text; } public void translateDetailStore(ref string raw) { } public bool translateDetailBestiary(ref string raw) { string text = raw; return raw == text; } public bool translateDetailSigurd(ref string raw) { string text = raw; return raw == text; } public void translateMisc(ref string raw) { raw = raw.Replace("Do you want to route the autopilot to the Company building?", "您想让自动驾驶系统操控飞船前往公司大楼吗?").Replace("Returned the item from storage", "从终端存储区恢复了物品").Replace("You have requested to order the survival kit:", "您已申请订购生存工具包: ") .Replace("There was no action supplied with the word.", "未找到对应的命令") .Replace("This action was not compatible with this object", "此操作与该物品不兼容") .Replace("You could not afford these items", "您买不起这些物品") .Replace("Cancelled order", "订单已取消") .Replace("You have cancelled the order", "您已取消指令") .Replace("No data collected on wildlife. Scans are required", "尚未收集野生动物数据. 需要扫描") .Replace("There was no object supplied with the action, or your word was typed incorrectly or does not exist", "此操作未提供响应的对象, 或您的指令输入错误或不存在") .Replace("Unable to route the ship currently. It must be in orbit around a moon to route the autopilot", "目前无法为飞船设定航线. 它必须在环绕星球的轨道上才能为自动驾驶系统设定航线") .Replace("Use the main lever at the front desk to enter orbit", "请使用控制台的主控制杆进入轨道") .Replace("Please enjoy your flight", "请享受您的航程") .Replace("Please CONFIRM or DENY", "请输入 CONFIRM 确认 或 DENY 取消") .Replace("Good luck", "祝你好运") .Replace("eclipsed", "日食") .Replace("flooded", "洪水") .Replace("stormy", "暴风雨") .Replace("foggy", "大雾") .Replace("rainy", "多雨") .Replace("mild weather", "温和的天气") .Replace("DATA CORRUPTED OR OVERWRITTEN", "数据已损坏或被覆盖") .Replace("Toggling radar cam", "正在切换雷达监视器画面") .Replace("Switched radar to player", "已切换雷达监视器画面至目标") .Replace("Pinged radar booster", "已发送命令至雷达增幅器") .Replace("Our contractors enjoy fast, free shipping while on the job! Any purchased items will arrive hourly at your approximate location", "我们的员工在工作期间可享受快速、免费的送货服务! 任何购买的物品都将在1小时内到达您的附近地点") .Replace("Hold the cord to activate the loud horn", "握住绳索启动大喇叭") .Replace("Press the button to activate the teleporter. It will teleport whoever is currently being monitored on the ship's radar. You will not be able to keep any of your held items through the teleport. It takes about 10 seconds to recharge", "按下按钮启动传送器. 它将传送目前被飞船雷达监控的人. 在传送过程中, 您将无法保留所持有的任何物品. 充能需要大约 10 秒钟") .Replace("Press the button and step onto the inverse teleporter while it activates", "按下按钮, 在启动时站在逆向传送器上") .Replace("Use the light switch to enable cozy lights", "使用灯光开关开启温馨的灯带") .Replace("Press [B] to rearrange objects in your ship and [V] to confirm", "按 [B] 重新排列飞船中的物体, 按 [V] 确认") .Replace("which will make everyone feel at home", "踏入飞船就能体验到家一般的温暖") .Replace("This has already been unlocked for your ship", "您的飞船已解锁此功能") .Replace("* Walkie-talkies", "* 对讲机") .Replace("* Flashlights", "* 手电筒") .Replace("* Shovel", "* 铲子") .Replace("The delivery vehicle cannot hold more than 12 items", "送货仓单次不能装载超过 12 件物品. ") .Replace("The signal transmitter can be activated with the \"transmit\" command followed by any message under 10 letters", "可使用 \"transmit\" 指令激活信号发射器, 传输长度为10字母以内的信息") .Replace("at a time. Please pick up your items when they land", "请在货物着陆时拿走你的物品") .Replace("The ship cannot be leaving or landing", "飞船不能离开或着陆") .Replace("This item is not in stock", "这个物品没有库存") .Replace("This is locked from the demo version", "此物品在试玩版本中被锁定") .Replace("Entered broadcast code", "输入了广播代码") .Replace("Sent transmission", "信号已发送") .Replace("Flashed radar booster", "雷达增幅器已闪光") .Replace("Unable to route the ship currently. It must be in orbit around a moon to route the autopilot.", "当前无法设定飞船路线. 仅可在星球轨道中设定路线") .Replace("Use the main lever at the front desk to enter orbit", "请使用控制台中央的拉杆泊入轨道. ") .Replace("Reset credits to 200", "重置信用点为200") .Replace("Do you want to eject all crew members, including yourself? You must be in orbit around a moon", "您想要将包括您在内的所有队员弹射到太空中吗? 仅可在轨道中进行此操作. ") .Replace("The autopilot ship is already orbiting this moon", "飞船已泊入当前星球轨道") .Replace("The delivery vehicle connot hold more than 12 items at a time. Please pick up your items when they land", "运载火箭一次只能携带12件物品. 请在投放物品后迅速领取") .Replace("Purchased items on route", "购买物品运输中") .Replace("An error occured! Try again", "发生了一个错误, 请重试") .Replace("To purchase decorations, the ship cannot be landed", "若要购买装饰品,飞船不能着陆") .Replace("The most advanced map device, using light-detection and ranging to give you an overhead view of your surroundings", "最先进的地图设备, 利用光探测和测距技术为您提供周围环境的俯瞰图") .Replace("To order any item, use the word BUY", "要订购任何物品,请使用 \"BUY \"命令") .Replace("The survival kit includes these necessities in a convenient package", "求生包中包括以下必需品, 方便携带") .Replace("Teleporter // Price: $400", "Teleporter(传送器) // 售价: $400") .Replace("BG IG, A System-Act Ally", "BG IG, 系统操作助手") .Replace("Copyright (C) 2084-2108, Halden Electronics Inc", "版权所有 (C) 2084-2108, 豪登电子股份有限公司") .Replace("Courtesy of the Company", "公司授权使用") .Replace("Bios for FORTUNE-9 87.7/10MHZ SYSTEM", "FORTUNE-9 87.7/10MHZ 系统BIOS") .Replace("Courtesy of the Company", "FORTUNE-9 87.7/10MHZ 系统Bios\\n\\n现在日期是周二 3-7-2532\\n现在时间是 8:03:32.15\\n\\n请输入您最喜欢的动物l:\r\n") .Replace("Courtesy of the Company", "FORTUNE-9 87.7/10MHZ 系统Bios\\n\\n现在日期是周二 3-7-2532\\n现在时间是 8:03:32.15\\n\\n请输入您最喜欢的动物l:\r\n") .Replace("Courtesy of the Company", "FORTUNE-9 87.7/10MHZ 系统Bios\\n\\n现在日期是周二 3-7-2532\\n现在时间是 8:03:32.15\\n\\n请输入您最喜欢的动物l:\r\n") .Replace("Please enter favorite animal", "请输入您最喜欢的动物") .Replace("Please describe your role in a team dynamic", "请用一个词描述您在团队中所扮演的角色") .Replace("Welcome to the FORTUNE-9 OS", "欢迎使用 FORTUNE-9 系统") .Replace(" Courtesy of the Company", " 本系统由公司提供") .Replace("Type \"Help\" for a list of commands", "输入 \"Help\" 查看命令列表"); List<object> list = new List<object> { new { pattern = "Ordered (\\d+) (.+)\\. Your new balance is \\$(\\d+)\\.", zh = "预定了{0}个{1}, 您当前余额为 $ {2}." }, new { pattern = "You have requested to order (.+)\\. Amount: (\\d+)\\.", zh = "您已申请订购了{0} . 数量: {1}." }, new { pattern = "You have requested to order the (.+) ship upgrade\\.", zh = "您已申请订购了{0}飞船升级配件 ." }, new { pattern = "You have requested to order a (.+)\\.", zh = "您已申请订购了{0}." }, new { pattern = "You have requested to order the (.+)\\.", zh = "您已申请订购了{0}." }, new { pattern = "You have requested to order (.+)\\.", zh = "您已申请订购了{0}." }, new { pattern = "Amount: (\\d+)\\.", zh = "数量: {0} ." }, new { pattern = "Total cost of items: \\$(\\d+)\\.", zh = "所有物品共计: $ {0} ." }, new { pattern = "Total cost of item: \\$(\\d+)\\.", zh = "物品共计: $ {0} ." }, new { pattern = "Your balance is \\$(\\d+)\\. Total cost of these items is \\$(\\d+)", zh = "您的余额为 $ {0}, 这些物品总计需要$ {1}" }, new { pattern = "Routing autopilot to the (.+)", zh = "自动驾驶系统正在引导飞船前往{0}" }, new { pattern = "Routing autopilot to (.+)", zh = "自动驾驶系统正在引导飞船前往{0}" }, new { pattern = "Your new balance is \\$(\\d+)", zh = "您当前余额为 $ {0}" }, new { pattern = "The cost to route to (.+) is \\$(\\d+)\\. It is", zh = "前往 {0} 需要花费 $ {1}. 当前" }, new { pattern = "currently (.+) on this moon", zh = "此星球环境为 {0}" }, new { pattern = "There are (\\d+) objects outside the ship, totalling at an approximate value of \\$(\\d+)", zh = "船外共有 {0} 个物体, 总计价值约为$ {1}" }, new { pattern = "The Company is buying at (\\d+)%\\.", zh = "公司当前回收价格比例为 {0}%." }, new { pattern = "Ordered the (.+)!", zh = "订购了{0}" }, new { pattern = "Ordered (.+)!", zh = "订购了{0}" }, new { pattern = "The Company is buying your goods at (\\d+)\\%\\.", zh = "公司目前的废品回收价格比例为 {0}%." }, new { pattern = "Current time is (.+)", zh = "当前时间为 {0}" }, new { pattern = "Current date is (.+)", zh = "当前日期为 {0}" }, new { pattern = "((\\d+)% OFF!)", zh = "({0}% 折扣!)" }, new { pattern = "(\\d+) purchased items on route", zh = "{0} 个购买的物品正在派送中" } }; foreach (object item in list) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item); PropertyDescriptor propertyDescriptor = properties.Find("pattern", ignoreCase: true); PropertyDescriptor propertyDescriptor2 = properties.Find("zh", ignoreCase: true); Regex regex = new Regex(propertyDescriptor.GetValue(item).ToString()); Match match = regex.Match(raw); if (match.Success) { string[] array = new string[match.Groups.Count]; for (int i = 1; i < match.Groups.Count; i++) { string raw2 = match.Groups[i].Value; array[i - 1] = translateItemName(ref raw2); } string input = raw; string? format = propertyDescriptor2.GetValue(item).ToString(); object[] args = array; raw = regex.Replace(input, string.Format(format, args)); } } raw = raw.Replace("Mon", "星期一").Replace("Tue", "星期二").Replace("Wed", "星期三") .Replace("Thu", "星期四") .Replace("Fri", "星期五") .Replace("Sat", "星期六") .Replace("Sun", "星期日"); } private string translateItemName(ref string raw) { return raw.Replace("walkie-talkies", "对讲机").Replace("pro flashlights", "手电筒Pro").Replace("flashlights", "手电筒") .Replace("shovels", "铲子") .Replace("lock-pickers", "开锁器") .Replace("stun grenades", "震爆弹") .Replace("boom boxes", "音响") .Replace("TZP-Inhalants", "TZP吸入剂") .Replace("zap guns", "电击枪") .Replace("jetpacks", "喷气背包") .Replace("jetpack", "喷气背包") .Replace("extension ladders", "伸缩梯") .Replace("radar boosters", "雷达增幅器") .Replace("loud horn", "大喇叭") .Replace("inverse teleporter", "逆向传送器") .Replace("teleporter", "传送器") .Replace("romantic table", "浪漫之桌") .Replace("toilet", "冲水马桶") .Replace("shower", "淋浴器") .Replace("table", "桌子") .Replace("record player", "唱片机") .Replace("television", "电视") .Replace("jack-o-Lantern", "南瓜灯") .Replace("cozy lights", "温馨的灯带") .Replace("pajama suits", "睡衣套装") .Replace("green suits", "绿色套装") .Replace("hazard suits", "防辐射套装") .Replace("welcome mat", "迎宾踏垫") .Replace("spray paint cans", "喷漆罐") .Replace("spray paint", "喷漆罐") .Replace("goldfish", "金鱼") .Replace("plushie pajama man", "毛绒睡衣公仔") .Replace("signal translator", "信号发射器"); } } } namespace XGameTranslator.Patches { internal class DefaultPatcher { public static void DefaultPostfix() { } public static void DefaultPrefix() { } } [HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")] internal class TextMeshProUGUIPatch { private static void Postfix(Component __instance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown ChangeFont((TextMeshProUGUI)__instance); } private static void ChangeFont(TextMeshProUGUI __instance) { List<TMP_FontAsset> fallbackFontAssetTable = new List<TMP_FontAsset> { ResourceHelper.GetTMP_Font() }; ((TMP_Text)__instance).font.fallbackFontAssetTable = fallbackFontAssetTable; if (!((Object)((TMP_Text)__instance).font).name.Contains("Variant") && ((Object)((TMP_Text)__instance).font).name != ((Object)ResourceHelper.GetTMP_Font()).name) { ((TMP_Text)__instance).font = ResourceHelper.GetTMP_Font(); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal class TMP_TextPatch { private static void Postfix(Component __instance) { } } [HarmonyPatch(typeof(Text), "OnEnable")] internal class FontPatch { private static void Postfix(Text __instance) { if (((Object)__instance.font).name != ((Object)ResourceHelper.GetFont()).name) { __instance.font = ResourceHelper.GetFont(); } } } [HarmonyPatch(typeof(TextMeshProUGUI), "InternalUpdate")] internal class TMPFontFallbackPatch { private static void Postfix(TextMeshProUGUI __instance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0032: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((TMP_Text)__instance).font == (Object)(object)ResourceHelper.GetTMP_Font() && (int)((TMP_Text)__instance).overflowMode > 0 && ((TMP_Text)__instance).preferredWidth > 1f) { Bounds bounds = ((TMP_Text)__instance).bounds; if (((Bounds)(ref bounds)).extents == Vector3.zero) { ((TMP_Text)__instance).overflowMode = (TextOverflowModes)0; } } } } } namespace XGameTranslator.Utilities { internal class DictionaryComparer : IEqualityComparer<string> { private readonly char[] charList = new char[5] { '.', ' ', '!', ':', '?' }; public bool Equals(string x, string y) { return string.Equals(SymbolsFilter(x), SymbolsFilter(y), StringComparison.OrdinalIgnoreCase); } public int GetHashCode(string obj) { return SymbolsFilter(obj)?.ToLowerInvariant().GetHashCode() ?? 0; } private string SymbolsFilter(string input) { return input.Trim().TrimEnd(charList); } } internal static class CecilFastReflectionHelper { private static readonly Type[] DynamicMethodDelegateArgs = new Type[2] { typeof(object), typeof(object[]) }; public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Expected O, but got Unknown //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs); ILProcessor iLProcessor = val.GetILProcessor(); ParameterInfo[] parameters = method.GetParameters(); bool flag = true; if (!method.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); if (method.DeclaringType.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, method.DeclaringType); } } for (int i = 0; i < parameters.Length; i++) { Type type = parameters[i].ParameterType; bool isByRef = type.IsByRef; if (isByRef) { type = type.GetElementType(); } bool isValueType = type.IsValueType; if (isByRef && isValueType && !directBoxValueAccess) { iLProcessor.Emit(OpCodes.Ldarg_1); iLProcessor.Emit(OpCodes.Ldc_I4, i); } iLProcessor.Emit(OpCodes.Ldarg_1); iLProcessor.Emit(OpCodes.Ldc_I4, i); if (isByRef && !isValueType) { Extensions.Emit(iLProcessor, OpCodes.Ldelema, typeof(object)); continue; } iLProcessor.Emit(OpCodes.Ldelem_Ref); if (!isValueType) { continue; } if (!isByRef || !directBoxValueAccess) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, type); if (isByRef) { Extensions.Emit(iLProcessor, OpCodes.Box, type); iLProcessor.Emit(OpCodes.Dup); Extensions.Emit(iLProcessor, OpCodes.Unbox, type); if (flag) { flag = false; val.Definition.Body.Variables.Add(new VariableDefinition((TypeReference)new PinnedType((TypeReference)new PointerType(((MemberReference)val.Definition).Module.TypeSystem.Void)))); } iLProcessor.Emit(OpCodes.Stloc_0); iLProcessor.Emit(OpCodes.Stelem_Ref); iLProcessor.Emit(OpCodes.Ldloc_0); } } else { Extensions.Emit(iLProcessor, OpCodes.Unbox, type); } } if (method.IsConstructor) { Extensions.Emit(iLProcessor, OpCodes.Newobj, (MethodBase)(method as ConstructorInfo)); } else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall) { Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)(method as MethodInfo)); } else { Extensions.Emit(iLProcessor, OpCodes.Callvirt, (MethodBase)(method as MethodInfo)); } Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType); if (type2 != typeof(void)) { if (type2.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Box, type2); } } else { iLProcessor.Emit(OpCodes.Ldnull); } iLProcessor.Emit(OpCodes.Ret); return (FastReflectionDelegate)val.Generate().CreateDelegate(typeof(FastReflectionDelegate)); } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match return type."); } if (typeof(T) != typeof(object) && (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } string text = "FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">"; DynamicMethodDefinition val = new DynamicMethodDefinition(text, typeof(F), new Type[1] { typeof(T) }); ILProcessor iLProcessor = val.GetILProcessor(); if (!fieldInfo.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType); } Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo); if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType); } iLProcessor.Emit(OpCodes.Ret); return (Func<T, F>)val.Generate().CreateDelegate(typeof(Func<T, F>)); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) if (fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match argument type."); } if (typeof(T) != typeof(object) && (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } string text = "FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">"; DynamicMethodDefinition val = new DynamicMethodDefinition(text, (Type)null, new Type[2] { typeof(T), typeof(F) }); ILProcessor iLProcessor = val.GetILProcessor(); if (!fieldInfo.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType); } iLProcessor.Emit(OpCodes.Ldarg_1); if (!(fieldInfo.FieldType == typeof(F))) { if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { if (fieldInfo.FieldType.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, fieldInfo.FieldType); } else { Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType); } } else { Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.FieldType); } } Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo); iLProcessor.Emit(OpCodes.Ret); return (Action<T, F>)val.Generate().CreateDelegate(typeof(Action<T, F>)); } } public static class CustomFastReflectionHelper { private struct FastReflectionDelegateKey { public MethodBase Method { get; } public bool DirectBoxValueAccess { get; } public bool ForceNonVirtCall { get; } public FastReflectionDelegateKey(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { Method = method; DirectBoxValueAccess = directBoxValueAccess; ForceNonVirtCall = forceNonVirtCall; } public override bool Equals(object obj) { return obj is FastReflectionDelegateKey fastReflectionDelegateKey && EqualityComparer<MethodBase>.Default.Equals(Method, fastReflectionDelegateKey.Method) && DirectBoxValueAccess == fastReflectionDelegateKey.DirectBoxValueAccess && ForceNonVirtCall == fastReflectionDelegateKey.ForceNonVirtCall; } public override int GetHashCode() { int num = 1017116076; num = num * -1521134295 + EqualityComparer<MethodBase>.Default.GetHashCode(Method); num = num * -1521134295 + DirectBoxValueAccess.GetHashCode(); return num * -1521134295 + ForceNonVirtCall.GetHashCode(); } } private static readonly Dictionary<FastReflectionDelegateKey, FastReflectionDelegate> MethodCache = new Dictionary<FastReflectionDelegateKey, FastReflectionDelegate>(); private static Type DynamicMethodDefinition = FindTypeStrict("MonoMod.Utils.DynamicMethodDefinition, MonoMod.Utils"); private static Type FindTypeStrict(string name) { return Type.GetType(name, throwOnError: false); } public static FastReflectionDelegate CreateFastDelegate(this MethodBase method, bool directBoxValueAccess = true, bool forceNonVirtCall = false) { FastReflectionDelegateKey key = new FastReflectionDelegateKey(method, directBoxValueAccess, forceNonVirtCall); if (MethodCache.TryGetValue(key, out var value)) { return value; } value = ((!(DynamicMethodDefinition != null)) ? GetFastDelegateForSRE(method, directBoxValueAccess, forceNonVirtCall) : GetFastDelegateForCecil(method, directBoxValueAccess, forceNonVirtCall)); MethodCache.Add(key, value); return value; } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { if (DynamicMethodDefinition != null) { return CreateFastFieldGetterForCecil<T, F>(fieldInfo); } return CreateFastFieldGetterForSRE<T, F>(fieldInfo); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { if (DynamicMethodDefinition != null) { return CreateFastFieldSetterForCecil<T, F>(fieldInfo); } return CreateFastFieldSetterForSRE<T, F>(fieldInfo); } private static FastReflectionDelegate GetFastDelegateForCecil(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { try { return CecilFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e) { try { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e2) { XGTLogger.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (object target, object[] args) => method.Invoke(target, args); } } } private static Func<T, F> CreateFastFieldGetterForCecil<T, F>(FieldInfo fieldInfo) { try { return CecilFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e) { try { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e2) { XGTLogger.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (T target) => (F)fieldInfo.GetValue(target); } } } private static Action<T, F> CreateFastFieldSetterForCecil<T, F>(FieldInfo fieldInfo) { try { return CecilFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e) { try { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e2) { XGTLogger.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return delegate(T target, F value) { fieldInfo.SetValue(target, value); }; } } } private static FastReflectionDelegate GetFastDelegateForSRE(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { try { return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e) { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (object target, object[] args) => method.Invoke(target, args); } } private static Func<T, F> CreateFastFieldGetterForSRE<T, F>(FieldInfo fieldInfo) { try { return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e) { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (T target) => (F)fieldInfo.GetValue(target); } } private static Action<T, F> CreateFastFieldSetterForSRE<T, F>(FieldInfo fieldInfo) { try { return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e) { XGTLogger.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return delegate(T target, F value) { fieldInfo.SetValue(target, value); }; } } } public delegate object FastReflectionDelegate(object target, params object[] args); public static class ReflectionCache { private struct MemberLookupKey { public Type Type { get; set; } public string MemberName { get; set; } public MemberLookupKey(Type type, string memberName) { Type = type; MemberName = memberName; } public override bool Equals(object obj) { if (obj is MemberLookupKey memberLookupKey) { return Type == memberLookupKey.Type && MemberName == memberLookupKey.MemberName; } return false; } public override int GetHashCode() { return Type.GetHashCode() + MemberName.GetHashCode(); } } private static Dictionary<MemberLookupKey, CachedMethod> Methods = new Dictionary<MemberLookupKey, CachedMethod>(); private static Dictionary<MemberLookupKey, CachedProperty> Properties = new Dictionary<MemberLookupKey, CachedProperty>(); private static Dictionary<MemberLookupKey, CachedField> Fields = new Dictionary<MemberLookupKey, CachedField>(); public static CachedMethod CachedMethod(this Type type, string name) { return type.CachedMethod(name, (Type[])null); } public static CachedMethod CachedMethod(this Type type, string name, params Type[] types) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Methods.TryGetValue(key, out var value)) { Type type2 = type; MethodInfo methodInfo = null; while (methodInfo == null && type2 != null) { methodInfo = ((types != null && types.Length != 0) ? type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) : type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); type2 = type2.BaseType; } if (methodInfo != null) { value = new CachedMethod(methodInfo); } Methods[key] = value; } return value; } public static CachedProperty CachedProperty(this Type type, string name) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Properties.TryGetValue(key, out var value)) { Type type2 = type; PropertyInfo propertyInfo = null; while (propertyInfo == null && type2 != null) { propertyInfo = type2.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } if (propertyInfo != null) { value = new CachedProperty(propertyInfo); } Properties[key] = value; } return value; } public static CachedField CachedField(this Type type, string name) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Fields.TryGetValue(key, out var value)) { Type type2 = type; FieldInfo fieldInfo = null; while (fieldInfo == null && type2 != null) { fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } if (fieldInfo != null) { value = new CachedField(fieldInfo); } Fields[key] = value; } return value; } public static CachedField CachedFieldByIndex(this Type type, int index, Type fieldType, BindingFlags flags) { FieldInfo[] array = (from x in type.GetFields(flags) where x.FieldType == fieldType select x).ToArray(); if (index < array.Length) { FieldInfo fieldInfo = array[index]; return new CachedField(fieldInfo); } return null; } } public class CachedMethod { private static readonly object[] Args0 = new object[0]; private static readonly object[] Args1 = new object[1]; private static readonly object[] Args2 = new object[2]; private FastReflectionDelegate _invoke; internal CachedMethod(MethodInfo method) { _invoke = method.CreateFastDelegate(); } public object Invoke(object instance, object[] arguments) { return _invoke(instance, arguments); } public object Invoke(object instance) { return _invoke(instance, Args0); } public object Invoke(object instance, object arg1) { try { Args1[0] = arg1; return _invoke(instance, Args1); } finally { Args1[0] = null; } } public object Invoke(object instance, object arg1, object arg2) { try { Args2[0] = arg1; Args2[1] = arg2; return _invoke(instance, Args2); } finally { Args2[0] = null; Args2[1] = null; } } } public class CachedProperty { private static readonly object[] Args0 = new object[0]; private static readonly object[] Args1 = new object[1]; private FastReflectionDelegate _set; private FastReflectionDelegate _get; public Type PropertyType { get; } internal CachedProperty(PropertyInfo propertyInfo) { if (propertyInfo.CanRead) { _get = propertyInfo.GetGetMethod(nonPublic: true).CreateFastDelegate(); } if (propertyInfo.CanWrite) { _set = propertyInfo.GetSetMethod(nonPublic: true).CreateFastDelegate(); } PropertyType = propertyInfo.PropertyType; } public void Set(object instance, object[] arguments) { if (_set != null) { _set(instance, arguments); } } public void Set(object instance, object arg1) { if (_set == null) { return; } try { Args1[0] = arg1; _set(instance, Args1); } finally { Args1[0] = null; } } public object Get(object instance, object[] arguments) { if (_get == null) { return null; } return _get(instance, arguments); } public object Get(object instance) { if (_get == null) { return null; } return _get(instance, Args0); } } public class CachedField { private Func<object, object> _get; private Action<object, object> _set; public Type FieldType { get; } internal CachedField(FieldInfo fieldInfo) { _get = CustomFastReflectionHelper.CreateFastFieldGetter<object, object>(fieldInfo); _set = CustomFastReflectionHelper.CreateFastFieldSetter<object, object>(fieldInfo); FieldType = fieldInfo.FieldType; } public void Set(object instance, object value) { if (_set != null) { _set(instance, value); } } public object Get(object instance) { if (_get == null) { return null; } return _get(instance); } } internal class ReflectionEmitFastReflectionHelper { private static readonly Type[] DynamicMethodDelegateArgs = new Type[2] { typeof(object), typeof(object[]) }; public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall) { DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs, method.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); ParameterInfo[] parameters = method.GetParameters(); bool flag = true; if (!method.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); if (method.DeclaringType.IsValueType) { iLGenerator.Emit(OpCodes.Unbox_Any, method.DeclaringType); } } for (int i = 0; i < parameters.Length; i++) { Type type = parameters[i].ParameterType; bool isByRef = type.IsByRef; if (isByRef) { type = type.GetElementType(); } bool isValueType = type.IsValueType; if (isByRef && isValueType && !directBoxValueAccess) { iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Ldc_I4, i); } iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Ldc_I4, i); if (isByRef && !isValueType) { iLGenerator.Emit(OpCodes.Ldelema, typeof(object)); continue; } iLGenerator.Emit(OpCodes.Ldelem_Ref); if (!isValueType) { continue; } if (!isByRef || !directBoxValueAccess) { iLGenerator.Emit(OpCodes.Unbox_Any, type); if (isByRef) { iLGenerator.Emit(OpCodes.Box, type); iLGenerator.Emit(OpCodes.Dup); iLGenerator.Emit(OpCodes.Unbox, type); if (flag) { flag = false; throw new NotImplementedException("No idea how to implement this..."); } iLGenerator.Emit(OpCodes.Stloc_0); iLGenerator.Emit(OpCodes.Stelem_Ref); iLGenerator.Emit(OpCodes.Ldloc_0); } } else { iLGenerator.Emit(OpCodes.Unbox, type); } } if (method.IsConstructor) { iLGenerator.Emit(OpCodes.Newobj, method as ConstructorInfo); } else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall) { iLGenerator.Emit(OpCodes.Call, method as MethodInfo); } else { iLGenerator.Emit(OpCodes.Callvirt, method as MethodInfo); } Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType); if (type2 != typeof(void)) { if (type2.IsValueType) { iLGenerator.Emit(OpCodes.Box, type2); } } else { iLGenerator.Emit(OpCodes.Ldnull); } iLGenerator.Emit(OpCodes.Ret); return (FastReflectionDelegate)dynamicMethod.CreateDelegate(typeof(FastReflectionDelegate)); } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match return type."); } if (typeof(T) != typeof(object) && (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } string name = "FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">"; DynamicMethod dynamicMethod = new DynamicMethod(name, typeof(F), new Type[1] { typeof(T) }, fieldInfo.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); if (!fieldInfo.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); } iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo); if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType); } iLGenerator.Emit(OpCodes.Ret); return (Func<T, F>)dynamicMethod.CreateDelegate(typeof(Func<T, F>)); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match argument type."); } if (typeof(T) != typeof(object) && (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } string name = "FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">"; DynamicMethod dynamicMethod = new DynamicMethod(name, null, new Type[2] { typeof(T), typeof(F) }, fieldInfo.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); if (!fieldInfo.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); } iLGenerator.Emit(OpCodes.Ldarg_1); if (!(fieldInfo.FieldType == typeof(F))) { if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { if (fieldInfo.FieldType.IsValueType) { iLGenerator.Emit(OpCodes.Unbox, fieldInfo.FieldType); } else { iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType); } } else { iLGenerator.Emit(OpCodes.Castclass, fieldInfo.FieldType); } } iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo); iLGenerator.Emit(OpCodes.Ret); return (Action<T, F>)dynamicMethod.CreateDelegate(typeof(Action<T, F>)); } } internal class Tools { public static void GetObjectProperties(object obj, BindingFlags bindingAttr = BindingFlags.Public, int deep = 0) { if (deep > 5) { return; } string text = ""; string text2 = "**** "; for (int i = 0; i < deep; i++) { text2 += "**** "; text += "\t|"; } if (obj == null) { return; } Type type = obj.GetType(); if (deep == 0) { XGTLogger.Info($"-------------------- {type} Start --------------------"); } else { XGTLogger.Info($"{text2} {type} Start {text2}"); } PropertyInfo[] properties = type.GetProperties(bindingAttr); if (type.IsGenericType) { if (obj is ICollection collection) { { foreach (object item in collection) { GetObjectProperties(item, bindingAttr, deep + 1); } return; } } XGTLogger.Message(text + " 泛型集合为空"); return; } PropertyInfo[] array = properties; foreach (PropertyInfo propertyInfo in array) { string name = propertyInfo.Name; string name2 = propertyInfo.PropertyType.Name; object value = propertyInfo.GetValue(obj, null); if (!(name == "material") || name2 == "Material") { } if (propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType.Name.StartsWith("String")) { string arg = ((value == null) ? "空" : value.ToString()); XGTLogger.Message(text + $"属性名:{name}, 属性类型:{name2}, 属性值{arg}"); continue; } string text3 = ((value == null) ? "空" : value.ToString()); if (!text3.Contains("Circle") && !text3.Contains("LOD")) { XGTLogger.Message(text + $"<b>子类</b>, 属性名:{name}, 属性类型:{name2}, 属性值{text3}"); } else { XGTLogger.Message(text + $"<b>子类不包含Circle & LOD</b>, 属性名:{name}, 属性类型:{name2}, 属性值{text3}"); } if (name2.Contains("Transform") || name2.Contains("Matrix") || name2.Contains("Vector") || name2.Contains("Color")) { continue; } switch (name2) { case "Shader": if (text3.Contains("HDRP/Lit (UnityEngine.Shader)")) { continue; } break; case "Mesh": case "Material[]": continue; } if (!name2.Contains("LocalKeyword") && !name2.Contains("Camera") && !name2.Contains("[]") && !name2.Contains("Canvas") && !name.Contains("triangles") && !name.Contains("boneWeights") && !name.Contains("lightProbeProxyVolumeOverride") && !name.Contains("SyncRoot") && !name.Contains("enabledGlobalKeywords") && !name.Contains("globalKeywords") && !name.Contains("whiteTexture") && !name2.Contains("Texture2D") && !text3.Contains("Renderer") && !text3.Contains("Circle") && !name.Contains("gameObject")) { GetObjectProperties(value, bindingAttr, deep + 1); } } if (deep == 0) { XGTLogger.Info($"-------------------- {type} Ended --------------------"); } else { XGTLogger.Info($"{text2} {type} Ended {text2}"); } } public static void PrintObjField(object obj, BindingFlags bindingAttr = BindingFlags.Public, int deep = 0) { if (deep > 5) { return; } string text = ""; string text2 = "**** "; for (int i = 0; i < deep; i++) { text2 += "**** "; text += "\t|"; } if (obj == null) { return; } Type type = obj.GetType(); if (deep == 0) { XGTLogger.Info($"-------------------- Field {type} Start --------------------"); } else { XGTLogger.Info($"{text2} {type} Start {text2}"); } FieldInfo[] fields = type.GetFields(bindingAttr); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string name = fieldInfo.Name; string name2 = fieldInfo.FieldType.Name; object value = fieldInfo.GetValue(obj); string arg = ((value == null) ? "空" : value.ToString()); if (fieldInfo.FieldType.IsValueType || fieldInfo.FieldType.Name.StartsWith("String")) { XGTLogger.Message(text + $"属性名:{name}, 属性类型:{name2}, 属性值{arg}"); continue; } XGTLogger.Message(text + $"<b>子类</b>, 属性名:{name}, 属性类型:{name2}, 属性值{arg}"); XGTLogger.Message(text + $"属性名:{name}, 属性类型:{name2}, 属性值{arg}"); PrintObjField(value, bindingAttr, deep + 1); } if (deep == 0) { XGTLogger.Info($"-------------------- Field {type} Ended --------------------"); } else { XGTLogger.Info($"{text2} {type} Ended {text2}"); } } } } namespace XGameTranslator.Parsers { internal class StringParser { } } namespace XGameTranslator.Hooks { public class ConvertTest { public ConvertTest(Sprite sprite, string fileName) { Texture2D texture = SpriteToTexture2DConverter(sprite); SaveTexture2File(texture, fileName); } private Texture2D SpriteToTexture2DConverter(Sprite sprite) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) RenderTexture temporary = RenderTexture.GetTemporary(((Texture)sprite.texture).width, ((Texture)sprite.texture).height, 0, (RenderTextureFormat)7, (RenderTextureReadWrite)1); Graphics.Blit((Texture)(object)sprite.texture, temporary); RenderTexture active = RenderTexture.active; RenderTexture.active = temporary; Texture2D val = new Texture2D(((Texture)sprite.texture).width, ((Texture)sprite.texture).height); val.ReadPixels(new Rect(0f, 0f, (float)((Texture)temporary).width, (float)((Texture)temporary).height), 0, 0); val.Apply(); RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); return val; } private void SaveTexture2File(Texture2D texture, string fileName) { byte[] bytes = ImageConversion.EncodeToPNG(texture); string filePath = GetFilePath(Path.Combine(Setting.RootPath, "config", fileName ?? "")); File.WriteAllBytes(filePath, bytes); XGTLogger.Message("Writed [" + fileName + "]!"); } private string GetFilePath(string filePath, int index = 0) { string text = ".png"; if (File.Exists(filePath + text)) { index++; return GetFilePath($"{filePath}_{index}", index); } return filePath + text; } } internal class TerminalHooks { internal static class Terminal_Start_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Method(typeof(Terminal), "Start", (Type[])null, (Type[])null); } private static void Postfix(Terminal __instance) { } } internal static class Terminal_TextPostProcess_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Method(typeof(Terminal), "TextPostProcess", (Type[])null, (Type[])null); } private static string Postfix(string modifiedDisplayText, TerminalNode node) { if (Setting.EnableTranslator) { TerminalTranslateText terminalTranslateText = new TerminalTranslateText(); if (XGameTranslator.Current.TryChangeText(modifiedDisplayText, out var result, "")) { return terminalTranslateText.translate(result, ref node); } return terminalTranslateText.translate(modifiedDisplayText, ref node); } return modifiedDisplayText; } } internal static class Terminal_ParsePlayerSentence_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Method(typeof(Terminal), "ParsePlayerSentence", (Type[])null, (Type[])null); } private static void Postfix(ref Terminal __instance, ref TerminalNode __result) { if (Setting.DisplayCredits) { string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded); if (text.ToLower() == "translate") { __result.displayText = "[translate]"; } } } private static void Prefix() { } } public static readonly Type[] All = new Type[3] { typeof(Terminal_Start_Hook), typeof(Terminal_TextPostProcess_Hook), typeof(Terminal_ParsePlayerSentence_Hook) }; } internal class TextMeshProHooks { public static readonly Type[] All = new Type[2] { typeof(TeshMeshProUGUI_OnEnable_Hook), typeof(TMP_Text_text_Hook) }; } internal static class TMP_Text_text_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Property(typeof(TMP_Text), "text")?.GetSetMethod(); } private static void Postfix(ref Component __instance) { XGameTranslator.Current.Hook_TextChanged(__instance, "TMP_Text_text_Hook"); } private static void Prefix() { } } internal static class TeshMeshProUGUI_OnEnable_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Method(typeof(TextMeshProUGUI), "OnEnable", (Type[])null, (Type[])null); } private static void Postfix(ref Component __instance) { XGameTranslator.Current.Hook_TextChanged(__instance, "TeshMeshProUGUI_OnEnable_Hook"); } private static void Prefix() { } } internal class TexturesHooks { internal static class Material_mainTexture_Hook { private static bool Prepare(object instance) { return true; } private static MethodBase TargetMethod(object instance) { return AccessTools.Property(typeof(Material), "mainTexture")?.GetSetMethod(); } public static void Prefix(Material __instance, ref Texture value) { } } internal static class SkinnedMeshRenderer_sharedMaterial_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Property(typeof(SkinnedMeshRenderer), "material")?.GetSetMethod(); } private static void Postfix(Renderer __instance) { string[] array = new string[7] { "manual1", "manual2v2", "manual3v2", "manual4v2", "posters", "StickyNote", "TipsPoster2" }; if (!((Object)(object)__instance != (Object)null)) { return; } Material sharedMaterial = __instance.sharedMaterial; if (!((Object)(object)sharedMaterial.mainTexture != (Object)null)) { return; } string[] array2 = array; foreach (string value in array2) { if (((Object)sharedMaterial.mainTexture).name.Contains(value)) { XGTLogger.Message($"SkinnedMeshRenderer_sharedMaterial_Hook===>{__instance}"); } } } } internal static class Renderer_sharedMaterial_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Property(typeof(Renderer), "sharedMaterial")?.GetGetMethod(); } private static void Postfix(Renderer __instance) { string[] array = new string[7] { "manual1", "manual2v2", "manual3v2", "manual4v2", "posters", "StickyNote", "TipsPoster2" }; if (!((Object)(object)__instance != (Object)null)) { return; } Material sharedMaterial = __instance.sharedMaterial; if (!((Object)(object)sharedMaterial.mainTexture != (Object)null)) { return; } string[] array2 = array; foreach (string value in array2) { if (((Object)sharedMaterial.mainTexture).name.Contains(value)) { XGTLogger.Message(__instance); } } } } internal static class Sprite_texture_Hook { private static bool Prepare(object instance) { return UnityTypes.Sprite != null; } private static MethodBase TargetMethod(object instance) { return AccessTools.Property(UnityTypes.Sprite, "texture")?.GetGetMethod(); } private static void Postfix(ref object __instance, ref Texture2D __result) { if ((Object)(object)__result != (Object)null && ((Object)__result).name.Contains("RedUIPanelGlitchBWarningRadiation")) { Texture2D val = ResourceHelper.TryGetTexture(((Object)__result).name ?? ""); __result = val; } } } internal static class HUD_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Method(typeof(HUDManager), "Start", (Type[])null, (Type[])null); } public static void Postfix(HUDManager __instance) { PluginConfigFile.HUDinstance = __instance; } public static void RunTest() { PluginConfigFile.HUDinstance.radiationGraphicAnimator.Rebind(); PluginConfigFile.HUDinstance.radiationGraphicAnimator.SetTrigger("RadiationWarning"); XGTLogger.Message(((Object)PluginConfigFile.HUDinstance.radiationGraphicAnimator).name ?? ""); } private static void Prefix() { } } internal static class SpriteRenderer_sprite_Hook { private static MethodBase TargetMethod(object instance) { return AccessTools.Property(UnityTypes.SpriteRenderer, "sprite")?.GetSetMethod(); } public static void Prefix(SpriteRenderer __instance, ref Sprite value) { } } internal static class Image_sprite_Hook { private static bool Prepare(object instance) { return UnityTypes.Image != null; } private static MethodBase TargetMethod(object instance) { return AccessTools.Property(UnityTypes.Image, "sprite")?.GetSetMethod(); } public static void Postfix(Component __instance) { Type type = ((object)__instance).GetType(); if ((!(UnityTypes.Image != null) || !UnityTypes.Image.IsAssignableFrom(type)) && (!(UnityTypes.RawImage != null) || !UnityTypes.RawImage.IsAssignableFrom(type))) { return; } string[] array = new string[4] { "RedUIPanelGlitchBWarning", "RedUIPanelGlitchBWarningRadiationB", "RedUIPanelGlitchBWarningRadiationC", "RedUIPanelGlitchBWarningRadiationD" }; string[] array2 = new string[1] { "/Systems/UI/Canvas/IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease/Panel" }; string[] array3 = array2; foreach (string value in array3) { if (__instance.G