using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; 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; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Timers; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using On.RoR2; using On.RoR2.UI; using RoR2; using RoR2.ConVar; using RoR2.UI; using SimpleJSON; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("R2API.Language")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+98b9ba9d40257e1878ab962f3cd1841a2a995892")] [assembly: AssemblyProduct("R2API.Language")] [assembly: AssemblyTitle("R2API.Language")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace R2API { internal static class ConsoleCommands { [CompilerGenerated] private static class <>O { public static hook_InitConVarsCoroutine <0>__OnConsoleInit; } private static bool _registered; internal static void Init() { //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_001b: Expected O, but got Unknown object obj = <>O.<0>__OnConsoleInit; if (obj == null) { hook_InitConVarsCoroutine val = OnConsoleInit; <>O.<0>__OnConsoleInit = val; obj = (object)val; } Console.InitConVarsCoroutine += (hook_InitConVarsCoroutine)obj; } internal static void Cleanup() { //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_001b: Expected O, but got Unknown object obj = <>O.<0>__OnConsoleInit; if (obj == null) { hook_InitConVarsCoroutine val = OnConsoleInit; <>O.<0>__OnConsoleInit = val; obj = (object)val; } Console.InitConVarsCoroutine -= (hook_InitConVarsCoroutine)obj; } private static IEnumerator OnConsoleInit(orig_InitConVarsCoroutine orig, Console self) { yield return orig.Invoke(self); RegisterCommands(); } private static void RegisterCommands() { //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (_registered) { return; } _registered = true; Console instance = Console.instance; if ((Object)(object)instance == (Object)null) { return; } FieldInfo field = typeof(Console).GetField("concommandCatalog", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)"concommandCatalog field not found via reflection"); } return; } Type nestedType = typeof(Console).GetNestedType("ConCommand", BindingFlags.NonPublic); if (nestedType == null) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)"ConCommand nested type not found"); } return; } if (!(field.GetValue(instance) is IDictionary dictionary)) { ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)"concommandCatalog is null"); } return; } FieldInfo field2 = nestedType.GetField("flags"); FieldInfo field3 = nestedType.GetField("helpText"); FieldInfo field4 = nestedType.GetField("action", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field4 == null) { ManualLogSource logger4 = LanguagePlugin.Logger; if (logger4 != null) { logger4.LogWarning((object)"ConCommand.action field not found"); } return; } Type fieldType = field4.FieldType; int num = 0; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); for (int i = 0; i < types.Length; i++) { MethodInfo[] methods = types[i].GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { ConCommandAttribute customAttribute = ((MemberInfo)methodInfo).GetCustomAttribute(); if (customAttribute == null) { continue; } try { object obj = Activator.CreateInstance(nestedType); field2?.SetValue(obj, customAttribute.flags); field3?.SetValue(obj, customAttribute.helpText); field4.SetValue(obj, Delegate.CreateDelegate(fieldType, methodInfo)); dictionary[customAttribute.commandName.ToLower()] = obj; num++; } catch (Exception ex) { ManualLogSource logger5 = LanguagePlugin.Logger; if (logger5 != null) { logger5.LogWarning((object)("Falha ao registrar comando '" + customAttribute.commandName + "': " + ex.Message)); } } } } if (num > 0) { ManualLogSource logger6 = LanguagePlugin.Logger; if (logger6 != null) { logger6.LogInfo((object)$"Console commands registered ({num}): /reloadlang, /langdebug, /langstatus"); } } } [ConCommand(/*Could not decode attribute arguments.*/)] private static void CC_ReloadLang(ConCommandArgs args) { LanguageAPI.InvalidateDiskCache(); LanguageAPI.ReloadAllLanguageFiles(Paths.PluginPath); LanguagePlugin.ReloadPeleJsonFiles(); Debug.Log((object)"[R2API.Language] /reloadlang: todos os arquivos recarregados."); } [ConCommand(/*Could not decode attribute arguments.*/)] private static void CC_LangDebug(ConCommandArgs args) { if (!LanguagePlugin.EnableDebugMenu) { Debug.Log((object)"[R2API.Language] /langdebug: EnableDebugMenu=false no config."); return; } LanguageDebugUI.Toggle(); Debug.Log((object)"[R2API.Language] /langdebug: janela de debug alternada."); } [ConCommand(/*Could not decode attribute arguments.*/)] private static void CC_LangStatus(ConCommandArgs args) { string text = string.Join(", ", LanguagePlugin.GetRegisteredCustomLangs()); string text2 = string.Join(", ", LanguagePlugin.GetFallbackLangs()); string currentLanguageName = Language.currentLanguageName; int totalCustomTokens = LanguageAPI.GetTotalCustomTokens(); int languageFileCount = LanguageAPI.GetLanguageFileCount(); int peleJsonFileCount = LanguageAPI.GetPeleJsonFileCount(); string text3 = string.Join(", ", LanguageAPI.GetPeleLanguageFolders()); string text4 = (LanguageAPI.IsHotReloadEnabled ? "ativo" : "inativo"); Debug.Log((object)"[R2API.Language] === Status ==="); Debug.Log((object)(" Current language: " + currentLanguageName)); Debug.Log((object)(" Custom langs: " + text)); Debug.Log((object)(" Fallback (en): " + text2)); Debug.Log((object)(" Hot-Reload: " + text4)); Debug.Log((object)$" .language files: {languageFileCount}"); Debug.Log((object)$" PELE JSON files: {peleJsonFileCount}"); Debug.Log((object)(" PELE language folders: " + text3)); Debug.Log((object)$" API tokens: {totalCustomTokens}"); Debug.Log((object)"[R2API.Language] ==============="); } } internal static class CustomLanguageRegistration { private static readonly string[] CustomLangs = new string[3] { "la", "eo", "uk" }; private static readonly string[] CultureFallbackLangs = new string[2] { "la", "eo" }; internal static string[] GetRegisteredCustomLangs() { return (string[])CustomLangs.Clone(); } internal static string[] GetFallbackLangs() { return (string[])CultureFallbackLangs.Clone(); } internal static string FixCultureName(string name) { if (Array.IndexOf(CultureFallbackLangs, name) >= 0) { return "en"; } return name; } [HarmonyPostfix] [HarmonyPatch(typeof(Language), "FindLanguageByName")] private static void FindLanguageByNamePostfix(string __0, ref Language __result) { if (__result != null || Array.IndexOf(CustomLangs, __0) < 0) { return; } Dictionary languagesByName = GetLanguagesByName(); if (languagesByName == null) { return; } if (languagesByName.TryGetValue(__0, out var value)) { __result = value; return; } Language val = CreateLanguage(__0); if (val != null) { languagesByName[__0] = val; __result = val; LanguagePlugin.LogVerbose("Lazy-registrado: " + __0); } } private static Dictionary GetLanguagesByName() { return (Dictionary)(typeof(Language).GetField("languagesByName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null)); } private static Language CreateLanguage(string lang) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown try { ConstructorInfo constructor = typeof(Language).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); if (constructor == null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)("CreateLanguage: Construtor Language(string) nao encontrado para " + lang)); } return null; } Language val = (Language)constructor.Invoke(new object[1] { lang }); FieldInfo fieldInfo = typeof(Language).GetField("name", BindingFlags.Instance | BindingFlags.Public) ?? typeof(Language).GetField("_name", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(Language).GetField("name", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(Language).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (fieldInfo == null) { FieldInfo[] fields = typeof(Language).GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo2 in fields) { if (!(fieldInfo2.FieldType != typeof(string)) && (string)fieldInfo2.GetValue(val) == lang) { fieldInfo = fieldInfo2; break; } } } fieldInfo?.SetValue(val, lang); MethodInfo method = typeof(Language).GetMethod("SetFolders", BindingFlags.Instance | BindingFlags.NonPublic); string text = Path.Combine(Path.Combine(Paths.PluginPath, "PELE", "Language"), lang); if (Directory.Exists(text)) { method?.Invoke(val, new object[1] { new string[1] { text } }); } string text2 = ((fieldInfo != null) ? ((string)fieldInfo.GetValue(val)) : "(field not found)"); LanguagePlugin.LogVerbose("CreateLanguage: '" + lang + "' ok (nameField='" + text2 + "')"); return val; } catch (Exception ex) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogError((object)("CreateLanguage falhou para " + lang + ": " + (ex.InnerException?.Message ?? ex.Message))); } ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogError((object)(" Stack: " + (ex.InnerException?.StackTrace ?? ex.StackTrace))); } return null; } } [HarmonyPrefix] [HarmonyPatch(typeof(Language), "SetCurrentLanguage")] private static bool SetCurrentLanguagePrefix(string newCurrentLanguageName) { if (Array.IndexOf(CustomLangs, newCurrentLanguageName) < 0) { return true; } try { Language val = Language.FindLanguageByName(newCurrentLanguageName); if (val == null) { return true; } if (Language.currentLanguage != null && Language.currentLanguage != Language.english) { Language.currentLanguage.UnloadStrings(); } (typeof(Language).GetField("k__BackingField", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(Language).GetField("currentLanguageName", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(Language).GetField("_currentLanguageName", BindingFlags.Static | BindingFlags.NonPublic))?.SetValue(null, newCurrentLanguageName); (typeof(Language).GetField("k__BackingField", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(Language).GetField("currentLanguage", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(Language).GetField("_currentLanguage", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(Language).GetField("currentLanguage", BindingFlags.Static | BindingFlags.Public))?.SetValue(null, val); CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(FixCultureName(val.name)); val.LoadStrings(); (typeof(Language).GetField("onCurrentLanguageChanged", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Action)?.Invoke(); LanguagePlugin.LogVerbose("SetCurrentLanguage interceptado: '" + newCurrentLanguageName + "' (CultureInfo='" + CultureInfo.CurrentCulture.Name + "')"); } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)("SetCurrentLanguagePrefix falhou: " + (ex.InnerException?.Message ?? ex.Message))); } return true; } return false; } } internal static class CyrillicFontSupport { private static TMP_FontAsset _cachedEmbeddedFont; private static AssetBundle _cachedEmbeddedFontBundle; private static TMP_FontAsset _vanillaDefaultFont; private static TMP_FontAsset _cyrillicFont; private static TMP_FontAsset _bombardierDefaultFont; private static AssetBundle _cyrillicFontBundle; private static bool _cyrillicFontProbeDone; [HarmonyPostfix] [HarmonyPatch(typeof(HGTextMeshProUGUI), "OnCurrentLanguageChanged")] private static void OnLanguageChanged() { if (_vanillaDefaultFont == null) { _vanillaDefaultFont = HGTextMeshProUGUI.defaultLanguageFont; } HGTextMeshProUGUI.defaultLanguageFont = GetCustomFontForCurrentLanguage() ?? _vanillaDefaultFont; ApplyCurrentFontToActiveText(); } [HarmonyPostfix] [HarmonyPatch(typeof(TextMeshProUGUI), "LoadFontAsset")] private static void TextMeshProUGUI_LoadFontAsset_Postfix(TextMeshProUGUI __instance) { TMP_FontAsset cyrillicFont = GetCyrillicFont(); if ((Object)(object)cyrillicFont == (Object)null || (Object)(object)__instance == (Object)null) { return; } TMP_FontAsset font = ((TMP_Text)__instance).font; if (!((Object)(object)font == (Object)null)) { TMP_FontAsset val = _bombardierDefaultFont ?? _vanillaDefaultFont; if ((Object)(object)font == (Object)(object)val || ((Object)font).name.IndexOf("Bomb", StringComparison.OrdinalIgnoreCase) >= 0) { ((TMP_Text)__instance).font = cyrillicFont; } } } internal static TMP_FontAsset GetCyrillicFont() { if (_cyrillicFontProbeDone) { return _cyrillicFont; } _cyrillicFontProbeDone = true; _cyrillicFont = TryLoadCyrillicFontBundle() ?? TryGetExternalCyrillicFont() ?? TryLoadEmbeddedFont("ukrainianfont"); if ((Object)(object)_cyrillicFont != (Object)null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogInfo((object)"Fonte cirilica do PELE ativa."); } } else { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)"Nenhuma fonte cirilica encontrada; caracteres ucranianos podem aparecer como '?'."); } } return _cyrillicFont; } private static TMP_FontAsset GetCustomFontForCurrentLanguage() { string currentLanguageName = Language.currentLanguageName; if (currentLanguageName == null) { return null; } if (!"uk".Equals(currentLanguageName, StringComparison.OrdinalIgnoreCase)) { return null; } return GetCyrillicFont(); } private static TMP_FontAsset TryGetExternalCyrillicFont() { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("AnotherOneCyrillicFont.AnotherOneCyrillicFont", throwOnError: false); if (!(type == null)) { FieldInfo field = type.GetField("fontBomb", BindingFlags.Static | BindingFlags.Public); FieldInfo? field2 = type.GetField("fontBombDefault", BindingFlags.Static | BindingFlags.Public); object? obj = field?.GetValue(null); TMP_FontAsset val = (TMP_FontAsset)((obj is TMP_FontAsset) ? obj : null); object? obj2 = field2?.GetValue(null); TMP_FontAsset val2 = (TMP_FontAsset)((obj2 is TMP_FontAsset) ? obj2 : null); if ((Object)(object)val2 != (Object)null) { _bombardierDefaultFont = val2; } if ((Object)(object)val != (Object)null) { return val; } } } } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)("TryGetExternalCyrillicFont falhou: " + ex.Message)); } } return null; } private static TMP_FontAsset TryLoadCyrillicFontBundle() { try { string text = FindCyrillicFontBundlePath(); if (text == null) { return null; } _cyrillicFontBundle = AssetBundle.LoadFromFile(text); if ((Object)(object)_cyrillicFontBundle == (Object)null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)"TryLoadCyrillicFontBundle: AssetBundle retornou null."); } return null; } TMP_FontAsset obj = _cyrillicFontBundle.LoadAsset("Assets/CyrillicFont/tmpBombDropshadow.asset") ?? _cyrillicFontBundle.LoadAllAssets().FirstOrDefault(); if ((Object)(object)obj != (Object)null) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"Fonte cirilica carregada de bundle PELE."); } } return obj; } catch (Exception ex) { ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)("TryLoadCyrillicFontBundle falhou: " + ex.Message)); } return null; } } private static string FindCyrillicFontBundlePath() { string[] array = new string[2] { Path.Combine(Paths.PluginPath, "PELE", "Fonts", "cyrillicfont"), Path.Combine(Paths.PluginPath, "PELE", "cyrillicfont") }; foreach (string text in array) { if (File.Exists(text)) { return text; } } return Directory.GetFiles(Paths.PluginPath, "cyrillicfont", SearchOption.AllDirectories).FirstOrDefault(); } private static TMP_FontAsset TryLoadEmbeddedFont(string resourceName) { if ((Object)(object)_cachedEmbeddedFont != (Object)null) { return _cachedEmbeddedFont; } try { if ((Object)(object)_cachedEmbeddedFontBundle == (Object)null) { Stream stream = TryGetResourceStream(resourceName); if (stream == null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)("TryLoadEmbeddedFont: recurso embutido nao encontrado: " + resourceName)); } return null; } using (stream) { _cachedEmbeddedFontBundle = AssetBundle.LoadFromStream(stream); } if ((Object)(object)_cachedEmbeddedFontBundle == (Object)null) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("TryLoadEmbeddedFont: AssetBundle retornou null: " + resourceName)); } return null; } } TMP_FontAsset val = _cachedEmbeddedFontBundle.LoadAsset("Assets/PELE-Font.asset") ?? _cachedEmbeddedFontBundle.LoadAsset("PELE-Font") ?? _cachedEmbeddedFontBundle.LoadAsset("ukrainianfont") ?? _cachedEmbeddedFontBundle.LoadAllAssets().FirstOrDefault(); if ((Object)(object)val == (Object)null) { ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)("TryLoadEmbeddedFont: TMP_FontAsset nao encontrado no bundle '" + resourceName + "'")); } return null; } _cachedEmbeddedFont = val; ManualLogSource logger4 = LanguagePlugin.Logger; if (logger4 != null) { logger4.LogInfo((object)"Fonte cirilica embutida carregada."); } return _cachedEmbeddedFont; } catch (Exception ex) { ManualLogSource logger5 = LanguagePlugin.Logger; if (logger5 != null) { logger5.LogWarning((object)("TryLoadEmbeddedFont falhou: " + ex.Message)); } return null; } } private static void ApplyCurrentFontToActiveText() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) TMP_FontAsset defaultLanguageFont = HGTextMeshProUGUI.defaultLanguageFont; if ((Object)(object)defaultLanguageFont == (Object)null) { return; } HGTextMeshProUGUI[] array = Resources.FindObjectsOfTypeAll(); foreach (HGTextMeshProUGUI val in array) { if (!((Object)(object)val == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { ((TMP_Text)val).font = defaultLanguageFont; ((Graphic)val).SetAllDirty(); } } } } private static Stream TryGetResourceStream(string resourceName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); return executingAssembly.GetManifestResourceStream("R2API.Language." + resourceName) ?? executingAssembly.GetManifestResourceStream("PELE." + resourceName); } } public class DuplicateTokenDetector { public List FindDuplicateTokens(string directory) { string[] languageFiles; try { languageFiles = LanguageFileHelper.GetLanguageFiles(directory); } catch (DirectoryNotFoundException) { return new List(); } Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); string[] array = languageFiles; foreach (string text in array) { Dictionary> dictionary2; try { dictionary2 = LanguageFileHelper.ParseTokensFromFile(text); } catch { continue; } string fileName = Path.GetFileName(text); foreach (KeyValuePair> item in dictionary2) { string key = item.Key; foreach (string key3 in item.Value.Keys) { string key2 = key + ":" + key3; if (!dictionary.TryGetValue(key2, out var value)) { value = (dictionary[key2] = new List()); } if (!value.Contains(fileName, StringComparer.OrdinalIgnoreCase)) { value.Add(fileName); } } } } return (from d in dictionary.Where((KeyValuePair> kvp) => kvp.Value.Count > 1).Select(delegate(KeyValuePair> kvp) { string[] array2 = kvp.Key.Split(new char[1] { ':' }, 2); return new DuplicateEntry { Language = ((array2.Length > 1) ? array2[0] : ""), TokenName = ((array2.Length > 1) ? array2[1] : kvp.Key), Files = kvp.Value, Count = kvp.Value.Count }; }) orderby d.Count descending select d).ToList(); } public bool HasDuplicates(string tokenName, string directory) { return FindDuplicateTokens(directory).Any((DuplicateEntry d) => d.TokenName.Equals(tokenName, StringComparison.OrdinalIgnoreCase)); } } public class DuplicateEntry { public string TokenName { get; set; } public string Language { get; set; } public List Files { get; set; } public int Count { get; set; } } public static class LanguageAPI { public class LanguageOverlay { private readonly List _overlayTokenDatas; public readonly ReadOnlyCollection readOnlyOverlays; internal LanguageOverlay(List data) { _overlayTokenDatas = data; readOnlyOverlays = data.AsReadOnly(); Apply(); temporaryOverlays.Add(this); } private void Apply() { _rwLock.EnterWriteLock(); try { ApplyUnderLock(); } finally { _rwLock.ExitWriteLock(); } } private void ApplyUnderLock() { foreach (OverlayTokenData readOnlyOverlay in readOnlyOverlays) { if (!OverlayLanguage.TryGetValue(readOnlyOverlay.lang, out Dictionary value)) { value = new Dictionary(); OverlayLanguage[readOnlyOverlay.lang] = value; } value[readOnlyOverlay.key] = readOnlyOverlay.value; } } public void Remove() { SetHooks(); temporaryOverlays.Remove(this); _rwLock.EnterWriteLock(); try { OverlayLanguage.Clear(); foreach (LanguageOverlay temporaryOverlay in temporaryOverlays) { temporaryOverlay.ApplyUnderLock(); } } finally { _rwLock.ExitWriteLock(); } } } public struct OverlayTokenData { public string key; public string value; public string lang; internal OverlayTokenData(string _key, string _value, string _lang) { key = _key; value = _value; lang = _lang; } internal OverlayTokenData(string _key, string _value) { key = _key; value = _value; lang = "generic"; } } [CompilerGenerated] private static class <>O { public static hook_GetLocalizedStringByToken <0>__Language_GetLocalizedStringByToken; public static hook_TokenIsRegistered <1>__Language_TokenIsRegistered; public static Func <2>__GetFileName; } public const string PluginGUID = "com.bepis.r2api.language"; public const string PluginName = "R2API.Language (Jaosnake fork)"; public const string PluginVersion = "2.0.0"; private static readonly Dictionary> CustomLanguage = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> OverlayLanguage = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly List temporaryOverlays = new List(); private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private static LanguageHotReload _hotReload; private static Dictionary _peleTokenCountCache; private static DateTime _peleTokenCacheTime = DateTime.MinValue; private static int _cachedLanguageFileCount; private static int _cachedPeleJsonFileCount; private static string[] _cachedPeleFolders; private static Dictionary> _cachedModFiles; private static DateTime _diskCacheTime = DateTime.MinValue; private const string GenericLanguage = "generic"; private const string GenericLegacyName = "strings"; private static bool _hooksEnabled = false; public static bool Loaded => true; internal static ManualLogSource Log { get; set; } internal static bool IsHotReloadEnabled => _hotReload?.IsEnabled ?? false; internal static void SetHooks() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (!_hooksEnabled) { _hooksEnabled = true; LoadLanguageFilesFromPluginFolder(); object obj = <>O.<0>__Language_GetLocalizedStringByToken; if (obj == null) { hook_GetLocalizedStringByToken val = Language_GetLocalizedStringByToken; <>O.<0>__Language_GetLocalizedStringByToken = val; obj = (object)val; } Language.GetLocalizedStringByToken += (hook_GetLocalizedStringByToken)obj; object obj2 = <>O.<1>__Language_TokenIsRegistered; if (obj2 == null) { hook_TokenIsRegistered val2 = Language_TokenIsRegistered; <>O.<1>__Language_TokenIsRegistered = val2; obj2 = (object)val2; } Language.TokenIsRegistered += (hook_TokenIsRegistered)obj2; } } private static void LoadLanguageFilesFromPluginFolder() { string[] files = Directory.GetFiles(Paths.PluginPath, "*.language", SearchOption.AllDirectories); for (int i = 0; i < files.Length; i++) { AddPath(files[i]); } } internal static void UnsetHooks() { //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_001b: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown object obj = <>O.<0>__Language_GetLocalizedStringByToken; if (obj == null) { hook_GetLocalizedStringByToken val = Language_GetLocalizedStringByToken; <>O.<0>__Language_GetLocalizedStringByToken = val; obj = (object)val; } Language.GetLocalizedStringByToken -= (hook_GetLocalizedStringByToken)obj; object obj2 = <>O.<1>__Language_TokenIsRegistered; if (obj2 == null) { hook_TokenIsRegistered val2 = Language_TokenIsRegistered; <>O.<1>__Language_TokenIsRegistered = val2; obj2 = (object)val2; } Language.TokenIsRegistered -= (hook_TokenIsRegistered)obj2; _hooksEnabled = false; } internal static void DisposeLock() { } private static bool TryGetToken(Dictionary> dict, string lang, string token, out string value) { _rwLock.EnterReadLock(); try { if (dict.TryGetValue(lang, out Dictionary value2) && value2.TryGetValue(token, out value)) { return true; } if (dict.TryGetValue("generic", out Dictionary value3) && value3.TryGetValue(token, out value)) { return true; } value = null; return false; } finally { _rwLock.ExitReadLock(); } } private static bool TokenExists(Dictionary> dict, string lang, string token) { _rwLock.EnterReadLock(); try { Dictionary value; Dictionary value2; return (dict.TryGetValue(lang, out value) && value.ContainsKey(token)) || (dict.TryGetValue("generic", out value2) && value2.ContainsKey(token)); } finally { _rwLock.ExitReadLock(); } } private static bool Language_TokenIsRegistered(orig_TokenIsRegistered orig, Language self, string token) { string name = self.name; if (TokenExists(OverlayLanguage, name, token)) { return true; } if (TokenExists(CustomLanguage, name, token)) { return true; } return orig.Invoke(self, token); } private static string Language_GetLocalizedStringByToken(orig_GetLocalizedStringByToken orig, Language self, string token) { string name = self.name; if (TryGetToken(OverlayLanguage, name, token, out string value)) { return value; } if (TryGetToken(CustomLanguage, name, token, out value)) { return value; } return orig.Invoke(self, token); } private static Dictionary> LoadFile(string fileContent) { Dictionary> dictionary = new Dictionary>(); try { JSONNode val = JSON.Parse(fileContent); if (val == (object)null) { return null; } foreach (string key2 in val.Keys) { JSONNode val2 = val[key2]; if (val2 == (object)null) { continue; } string key = ((key2 == "strings") ? "generic" : key2); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new Dictionary()); } foreach (string key3 in val2.Keys) { value[key3] = val2[key3].Value; } } } catch (Exception ex) { Debug.LogFormat("Parsing error in language file, Error: {0}", new object[1] { ex }); return null; } if (dictionary.Count <= 0) { return null; } return dictionary; } public static void Add(string key, string value) { SetHooks(); Add(key, value, "generic"); } public static void Add(string key, string value, string language) { SetHooks(); if (key == null) { throw new ArgumentNullException("key"); } if (value == null) { throw new ArgumentNullException("value"); } if (language == null) { throw new ArgumentNullException("language"); } _rwLock.EnterWriteLock(); try { if (!CustomLanguage.TryGetValue(language, out Dictionary value2)) { value2 = new Dictionary(); CustomLanguage[language] = value2; } if (!value2.ContainsKey(key)) { value2[key] = value; } } finally { _rwLock.ExitWriteLock(); } } public static void AddPath(string path) { SetHooks(); if (path == null) { throw new ArgumentNullException("path"); } Add(File.ReadAllText(path)); } public static void Add(string file) { SetHooks(); if (file == null) { throw new ArgumentNullException("file"); } Dictionary> dictionary = LoadFile(file); if (dictionary != null) { Add(dictionary); } } public static void Add(Dictionary tokenDictionary) { SetHooks(); Add(tokenDictionary, "generic"); } public static void Add(Dictionary tokenDictionary, string language) { SetHooks(); if (tokenDictionary == null) { throw new ArgumentNullException("tokenDictionary"); } foreach (KeyValuePair item in tokenDictionary) { if (item.Value != null) { Add(item.Key, item.Value, language); } } } public static void Add(Dictionary> languageDictionary) { SetHooks(); if (languageDictionary == null) { throw new ArgumentNullException("languageDictionary"); } foreach (KeyValuePair> item in languageDictionary) { if (item.Value != null) { Add(item.Value, item.Key); } } } public static void AddOrUpdateToken(string key, string value) { SetHooks(); AddOrUpdateToken(key, value, "generic"); } public static void AddOrUpdateToken(string key, string value, string language) { SetHooks(); if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } if (value == null) { throw new ArgumentNullException("value"); } if (string.IsNullOrEmpty(language)) { language = "generic"; } _rwLock.EnterWriteLock(); try { if (!CustomLanguage.TryGetValue(language, out Dictionary value2)) { value2 = new Dictionary(); CustomLanguage[language] = value2; } value2[key] = value; } finally { _rwLock.ExitWriteLock(); } } public static void RemoveToken(string key, string language = null) { SetHooks(); if (string.IsNullOrEmpty(key)) { return; } string key2 = language ?? "generic"; _rwLock.EnterWriteLock(); try { if (CustomLanguage.TryGetValue(key2, out Dictionary value)) { value.Remove(key); } } finally { _rwLock.ExitWriteLock(); } } public static void ClearAllTokens() { SetHooks(); _rwLock.EnterWriteLock(); try { CustomLanguage.Clear(); } finally { _rwLock.ExitWriteLock(); } } public static void EnableHotReload(string directory) { SetHooks(); if (_hotReload == null) { _hotReload = LanguageHotReload.Instance; } _hotReload.Enable(directory); } public static void DisableHotReload() { _hotReload?.Disable(); } public static void ReloadAllLanguageFiles(string directory) { SetHooks(); ClearAllTokens(); string[] languageFiles = LanguageFileHelper.GetLanguageFiles(directory); foreach (string text in languageFiles) { try { foreach (KeyValuePair> item in LanguageFileHelper.ParseTokensFromFile(text)) { foreach (KeyValuePair item2 in item.Value) { AddOrUpdateToken(item2.Key, item2.Value, item.Key); } } } catch (Exception ex) { Debug.LogError((object)("[LanguageAPI] Erro ao recarregar " + text + ": " + ex.Message)); } } } public static ValidationResult ValidateLanguageFile(string filePath) { SetHooks(); return new LanguageFileValidator().ValidateFile(filePath); } public static List FindDuplicateTokens(string directory) { SetHooks(); return new DuplicateTokenDetector().FindDuplicateTokens(directory); } public static List FindUnusedTokens(string directory) { SetHooks(); return new TokenUsageAnalyzer().FindUnusedTokens(directory); } public static void CompileLanguageFiles(string sourceDirectory, string outputDirectory) { SetHooks(); new LanguageFileCompiler().CompileLanguageFiles(sourceDirectory, outputDirectory); } public static LanguageOverlay AddOverlay(string key, string value) { SetHooks(); return AddOverlay(key, value, "generic"); } public static LanguageOverlay AddOverlay(string key, string value, string lang) { SetHooks(); if (key == null) { throw new ArgumentNullException("key"); } if (value == null) { throw new ArgumentNullException("value"); } if (lang == null) { throw new ArgumentNullException("lang"); } return new LanguageOverlay(new List(1) { new OverlayTokenData(key, value, lang) }); } public static LanguageOverlay AddOverlayPath(string path) { SetHooks(); if (path == null) { throw new ArgumentNullException("path"); } return AddOverlay(File.ReadAllText(path)); } public static LanguageOverlay AddOverlay(string file) { SetHooks(); if (file == null) { throw new ArgumentNullException("file"); } Dictionary> dictionary = LoadFile(file); if (dictionary == null) { return null; } return AddOverlay(dictionary); } public static LanguageOverlay AddOverlay(Dictionary tokenDictionary) { SetHooks(); return AddOverlay(tokenDictionary, "generic"); } public static LanguageOverlay AddOverlay(Dictionary tokenDictionary, string language) { SetHooks(); if (tokenDictionary == null) { throw new ArgumentNullException("tokenDictionary"); } if (language == null) { throw new ArgumentNullException("language"); } List list = new List(tokenDictionary.Count); foreach (KeyValuePair item in tokenDictionary) { if (item.Value != null) { list.Add(new OverlayTokenData(item.Key, item.Value, language)); } } return new LanguageOverlay(list); } public static LanguageOverlay AddOverlay(Dictionary> languageDictionary) { SetHooks(); if (languageDictionary == null) { throw new ArgumentNullException("languageDictionary"); } List list = new List(); foreach (KeyValuePair> item in languageDictionary) { item.Deconstruct(out var key, out var value); string lang = key; Dictionary dictionary = value; if (dictionary == null) { continue; } foreach (KeyValuePair item2 in dictionary) { item2.Deconstruct(out key, out var value2); string key2 = key; string text = value2; if (text != null) { list.Add(new OverlayTokenData(key2, text, lang)); } } } return new LanguageOverlay(list); } internal static Dictionary GetCustomTokenCounts() { _rwLock.EnterReadLock(); try { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair> item in CustomLanguage) { dictionary[item.Key] = item.Value.Count; } foreach (KeyValuePair> item2 in OverlayLanguage) { dictionary[item2.Key] = dictionary.GetValueOrDefault(item2.Key) + item2.Value.Count; } return dictionary; } finally { _rwLock.ExitReadLock(); } } internal static int GetTotalCustomTokens() { _rwLock.EnterReadLock(); try { return CustomLanguage.Sum>>((KeyValuePair> kvp) => kvp.Value.Count) + OverlayLanguage.Sum>>((KeyValuePair> kvp) => kvp.Value.Count); } finally { _rwLock.ExitReadLock(); } } internal static IEnumerable GetCustomLanguages() { _rwLock.EnterReadLock(); try { return (from x in CustomLanguage.Keys.Union(OverlayLanguage.Keys).Distinct(StringComparer.OrdinalIgnoreCase) orderby x select x).ToList(); } finally { _rwLock.ExitReadLock(); } } internal static void InvalidateDiskCache() { _diskCacheTime = DateTime.MinValue; _peleTokenCacheTime = DateTime.MinValue; } internal static void RefreshDiskCache() { _diskCacheTime = DateTime.UtcNow; try { _cachedLanguageFileCount = Directory.GetFiles(Paths.PluginPath, "*.language", SearchOption.AllDirectories).Length; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("GetLanguageFileCount: " + ex.Message)); } _cachedLanguageFileCount = 0; } string path = Path.Combine(Paths.PluginPath, "PELE", "Language"); try { _cachedPeleJsonFileCount = (Directory.Exists(path) ? Directory.GetFiles(path, "*.json", SearchOption.AllDirectories).Length : 0); } catch (Exception ex2) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("GetPeleJsonFileCount: " + ex2.Message)); } _cachedPeleJsonFileCount = 0; } try { _cachedPeleFolders = (Directory.Exists(path) ? Directory.GetDirectories(path).Select(Path.GetFileName).ToArray() : Array.Empty()); } catch (Exception ex3) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)("GetPeleLanguageFolders: " + ex3.Message)); } _cachedPeleFolders = Array.Empty(); } try { _cachedModFiles = new Dictionary>(StringComparer.OrdinalIgnoreCase); string[] files = Directory.GetFiles(Paths.PluginPath, "*.language", SearchOption.AllDirectories); foreach (string text in files) { DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(text)); string key = directoryInfo.Parent?.Name ?? directoryInfo.Name; if (!_cachedModFiles.TryGetValue(key, out List value)) { value = new List(); _cachedModFiles[key] = value; } value.Add(text); } } catch (Exception ex4) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogWarning((object)("GetModFiles: " + ex4.Message)); } if (_cachedModFiles == null) { _cachedModFiles = new Dictionary>(); } } } internal static int GetLanguageFileCount() { if (_diskCacheTime < DateTime.UtcNow.AddSeconds(-5.0)) { RefreshDiskCache(); } return _cachedLanguageFileCount; } internal static int GetPeleJsonFileCount() { if (_diskCacheTime < DateTime.UtcNow.AddSeconds(-5.0)) { RefreshDiskCache(); } return _cachedPeleJsonFileCount; } internal static string[] GetPeleLanguageFolders() { if (_diskCacheTime < DateTime.UtcNow.AddSeconds(-5.0)) { RefreshDiskCache(); } return _cachedPeleFolders ?? Array.Empty(); } internal static Dictionary> GetModFileGroups() { if (_diskCacheTime < DateTime.UtcNow.AddSeconds(-5.0)) { RefreshDiskCache(); } return _cachedModFiles ?? new Dictionary>(); } internal static Dictionary GetPeleTokenCounts() { if (_peleTokenCacheTime > DateTime.UtcNow.AddSeconds(-5.0)) { return _peleTokenCountCache ?? new Dictionary(); } Dictionary dictionary = new Dictionary(); try { string path = Path.Combine(Paths.PluginPath, "PELE", "Language"); if (!Directory.Exists(path)) { return dictionary; } string[] directories = Directory.GetDirectories(path); foreach (string path2 in directories) { string fileName = Path.GetFileName(path2); int num = 0; string[] files = Directory.GetFiles(path2, "*.json"); foreach (string text in files) { try { JSONNode val = JSON.Parse(File.ReadAllText(text)); if (val != (object)null) { num += CountPeleJsonTokens(val); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Erro ao ler " + text + ": " + ex.Message)); } } } if (num > 0) { dictionary[fileName] = num; } } } catch (Exception ex2) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("GetPeleTokenCounts: " + ex2.Message)); } } _peleTokenCountCache = dictionary; _peleTokenCacheTime = DateTime.UtcNow; return dictionary; } private static int CountPeleJsonTokens(JSONNode json) { if (json == (object)null) { return 0; } JSONNode val = json["strings"]; if (val != (object)null && val.Count > 0) { return CountTokenObject(val); } return CountTokenObject(json, skipManifestKeys: true); } private static int CountTokenObject(JSONNode node, bool skipManifestKeys = false) { int num = 0; foreach (string key in node.Keys) { if ((!skipManifestKeys || !string.Equals(key, "language", StringComparison.OrdinalIgnoreCase)) && (!skipManifestKeys || !string.Equals(key, "strings", StringComparison.OrdinalIgnoreCase)) && !key.StartsWith("_", StringComparison.Ordinal) && node[key] != (object)null && node[key].Count == 0) { num++; } } return num; } } internal class LanguageDebugUI : MonoBehaviour { private static LanguageDebugUI _instance; private bool _visible; private Rect _windowRect; private int _activeTab; private Vector2 _scrollPos; private readonly string[] _tabs = new string[5] { "Linguas", "PELE", "Mods", "Status", "Duplicatas" }; private const string PrefsKeyX = "R2APILang_DebugX"; private const string PrefsKeyY = "R2APILang_DebugY"; private const string PrefsKeyW = "R2APILang_DebugW"; private const string PrefsKeyH = "R2APILang_DebugH"; private List _duplicates; private bool _duplicatesDirty = true; private bool _scanning; private readonly Queue _mainThreadQueue = new Queue(); private int _errorCount = -1; private static MethodInfo _setCurrentLanguageMethod; internal static void Create() { //IL_0026: 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: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("LanguageDebugUI", new Type[1] { typeof(LanguageDebugUI) }); Object.DontDestroyOnLoad((Object)val); _instance = val.GetComponent(); _setCurrentLanguageMethod = typeof(Language).GetMethod("SetCurrentLanguage", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } internal static void DestroyInstance() { if ((Object)(object)_instance != (Object)null) { Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; } } internal static void Toggle() { if ((Object)(object)_instance != (Object)null) { _instance._visible = !_instance._visible; } } private void Awake() { //IL_003d: 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) _windowRect = new Rect(PlayerPrefs.GetFloat("R2APILang_DebugX", 50f), PlayerPrefs.GetFloat("R2APILang_DebugY", 50f), PlayerPrefs.GetFloat("R2APILang_DebugW", 600f), PlayerPrefs.GetFloat("R2APILang_DebugH", 400f)); } private void Update() { if (Input.GetKeyDown((KeyCode)287)) { _visible = !_visible; } ProcessMainThreadQueue(); } private void OnGUI() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (_visible) { _windowRect = GUILayout.Window(999, _windowRect, new WindowFunction(DrawWindow), "R2API.Language Debug", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.MinWidth(400f), GUILayout.MinHeight(200f) }); } } private void DrawWindow(int _) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 _activeTab = GUILayout.Toolbar(_activeTab, _tabs, Array.Empty()); _scrollPos = GUILayout.BeginScrollView(_scrollPos, Array.Empty()); switch (_activeTab) { case 0: DrawLanguagesTab(); break; case 1: DrawPeleTab(); break; case 2: DrawModsTab(); break; case 3: DrawStatusTab(); break; case 4: DrawDuplicatesTab(); break; } GUILayout.EndScrollView(); if ((int)Event.current.type == 1) { SaveWindowRect(); } GUI.DragWindow(); } private void SaveWindowRect() { PlayerPrefs.SetFloat("R2APILang_DebugX", ((Rect)(ref _windowRect)).x); PlayerPrefs.SetFloat("R2APILang_DebugY", ((Rect)(ref _windowRect)).y); PlayerPrefs.SetFloat("R2APILang_DebugW", ((Rect)(ref _windowRect)).width); PlayerPrefs.SetFloat("R2APILang_DebugH", ((Rect)(ref _windowRect)).height); PlayerPrefs.Save(); } private void InvokeSetCurrentLanguage(string lang) { if (_setCurrentLanguageMethod == null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)"SetCurrentLanguage method not found"); } return; } try { _setCurrentLanguageMethod.Invoke(null, new object[1] { lang }); } catch (Exception ex) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogError((object)("Falha ao trocar lingua: " + (ex.InnerException?.Message ?? ex.Message))); } } } private void DrawLanguagesTab() { GUILayout.Label("Linguas registradas no jogo — clique Ativar para trocar:", GUI.skin.label, Array.Empty()); string currentLanguageName = Language.currentLanguageName; List availableLanguages = LanguageNames.GetAvailableLanguages(); if (availableLanguages.Count == 0) { GUILayout.Label(" (nenhuma)", Array.Empty()); } else { foreach (string item in availableLanguages) { string friendlyName = LanguageNames.GetFriendlyName(item); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(" " + item + " - " + friendlyName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); if (item == currentLanguageName) { GUILayout.Label("[ATIVO]", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); } else if (GUILayout.Button("Ativar", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { InvokeSetCurrentLanguage(item); } GUILayout.EndHorizontal(); } } GUILayout.Space(10f); GUILayout.Label("Tokens customizados por lingua (API):", GUI.skin.label, Array.Empty()); Dictionary customTokenCounts = LanguageAPI.GetCustomTokenCounts(); if (customTokenCounts.Count == 0) { GUILayout.Label(" (nenhum)", Array.Empty()); } else { foreach (KeyValuePair item2 in customTokenCounts.OrderBy((KeyValuePair x) => x.Key)) { GUILayout.Label($" {item2.Key}: {item2.Value} tokens", Array.Empty()); } } GUILayout.Space(5f); GUILayout.Label("Total de tokens via API: " + LanguageAPI.GetTotalCustomTokens(), Array.Empty()); } private void DrawPeleTab() { string[] peleLanguageFolders = LanguageAPI.GetPeleLanguageFolders(); if (peleLanguageFolders.Length == 0) { GUILayout.Label("Pasta PELE/Language/ nao encontrada.", Array.Empty()); return; } GUILayout.Label("Pastas de idioma encontradas:", GUI.skin.label, Array.Empty()); string[] array = peleLanguageFolders; foreach (string text in array) { GUILayout.Label(" " + text, Array.Empty()); } GUILayout.Space(5f); GUILayout.Label("Arquivos JSON: " + LanguageAPI.GetPeleJsonFileCount(), Array.Empty()); GUILayout.Space(5f); GUILayout.Label("Tokens por lingua (PELE JSONs):", GUI.skin.label, Array.Empty()); Dictionary peleTokenCounts = LanguageAPI.GetPeleTokenCounts(); if (peleTokenCounts.Count == 0) { GUILayout.Label(" (nenhum token)", Array.Empty()); return; } foreach (KeyValuePair item in peleTokenCounts.OrderBy((KeyValuePair x) => x.Key)) { GUILayout.Label($" {item.Key}: {item.Value} tokens", Array.Empty()); } } private void DrawModsTab() { GUILayout.Label("Mods com arquivos .language:", GUI.skin.label, Array.Empty()); Dictionary> modFileGroups = LanguageAPI.GetModFileGroups(); if (modFileGroups.Count == 0) { GUILayout.Label(" (nenhum encontrado)", Array.Empty()); return; } foreach (KeyValuePair> item in modFileGroups.OrderBy((KeyValuePair> x) => x.Key)) { GUILayout.Label($" {item.Key}: {item.Value.Count} arquivo(s)", Array.Empty()); foreach (string item2 in item.Value.Take(5)) { GUILayout.Label(" " + Path.GetFileName(item2), Array.Empty()); } if (item.Value.Count > 5) { GUILayout.Label($" ... e mais {item.Value.Count - 5}", Array.Empty()); } } } private void DrawStatusTab() { GUILayout.Label("Status do Plugin", GUI.skin.label, Array.Empty()); GUILayout.Label(" DLL: com.bepis.r2api.language v2.0.0", Array.Empty()); GUILayout.Label(" Hot-Reload: " + (LanguageAPI.IsHotReloadEnabled ? "ATIVO" : "INATIVO"), Array.Empty()); GUILayout.Space(5f); GUILayout.Label("Diretorios monitorados:", GUI.skin.label, Array.Empty()); GUILayout.Label(" *.language: " + Paths.PluginPath, Array.Empty()); GUILayout.Label(" *.json: " + Path.Combine(Paths.PluginPath, "PELE", "Language"), Array.Empty()); GUILayout.Space(5f); GUILayout.Label("Arquivos carregados:", GUI.skin.label, Array.Empty()); GUILayout.Label($" .language: {LanguageAPI.GetLanguageFileCount()}", Array.Empty()); GUILayout.Label($" .json: {LanguageAPI.GetPeleJsonFileCount()}", Array.Empty()); GUILayout.Label($" Tokens: {LanguageAPI.GetTotalCustomTokens()}", Array.Empty()); GUILayout.Label(" Erros de sintaxe: " + ((_errorCount >= 0) ? _errorCount.ToString() : "?"), Array.Empty()); GUILayout.Space(5f); GUILayout.Label("Custom langs:", GUI.skin.label, Array.Empty()); GUILayout.Label(" " + string.Join(", ", LanguagePlugin.GetRegisteredCustomLangs()), Array.Empty()); GUILayout.Label(" Com fallback: " + string.Join(", ", LanguagePlugin.GetFallbackLangs()), Array.Empty()); GUILayout.Space(10f); if (GUILayout.Button("Refresh cache", Array.Empty())) { LanguageAPI.InvalidateDiskCache(); LanguageAPI.RefreshDiskCache(); _errorCount = CountFileErrors(); _duplicatesDirty = true; } if (GUILayout.Button("Recarregar tudo (F5)", Array.Empty())) { LanguageAPI.InvalidateDiskCache(); LanguageAPI.ReloadAllLanguageFiles(Paths.PluginPath); LanguagePlugin.ReloadPeleJsonFiles(); _errorCount = CountFileErrors(); _duplicatesDirty = true; } GUILayout.Space(5f); GUILayout.Label("F6 - Alternar esta janela", GUI.skin.label, Array.Empty()); } private void DrawDuplicatesTab() { if (_duplicatesDirty && !_scanning) { _scanning = true; _duplicates = null; ThreadPool.QueueUserWorkItem(delegate { try { List result = new DuplicateTokenDetector().FindDuplicateTokens(Paths.PluginPath); _mainThreadQueue.Enqueue(delegate { _duplicates = result; _duplicatesDirty = false; _scanning = false; }); } catch (Exception ex) { Exception ex2 = ex; Exception ex3 = ex2; _mainThreadQueue.Enqueue(delegate { _duplicates = new List(); _duplicatesDirty = false; _scanning = false; Debug.LogError((object)("Erro ao escanear duplicatas: " + ex3.Message)); }); } }); } if (_scanning) { GUILayout.Label("Escaneando arquivos .language...", Array.Empty()); return; } if (_duplicates == null || _duplicates.Count == 0) { GUILayout.Label("Nenhum token duplicado encontrado.", Array.Empty()); return; } GUILayout.Label($"{_duplicates.Count} token(s) duplicado(s):", GUI.skin.label, Array.Empty()); GUILayout.Space(5f); int count = Math.Min(_duplicates.Count, 50); foreach (DuplicateEntry item in _duplicates.OrderByDescending((DuplicateEntry d) => d.Count).Take(count)) { string arg = (string.IsNullOrEmpty(item.Language) ? "" : ("[" + item.Language + "] ")); GUILayout.Label($" {arg}{item.TokenName} ({item.Count}x)", Array.Empty()); foreach (string file in item.Files) { GUILayout.Label(" " + file, Array.Empty()); } GUILayout.Space(3f); } if (_duplicates.Count > 50) { GUILayout.Label($" ... e mais {_duplicates.Count - 50} token(s)", Array.Empty()); } } private void ProcessMainThreadQueue() { while (_mainThreadQueue.Count > 0) { try { _mainThreadQueue.Dequeue()?.Invoke(); } catch (Exception ex) { Debug.LogError((object)("Error processing main thread queue: " + ex.Message)); } } } private static int CountFileErrors() { int num = 0; try { LanguageFileValidator languageFileValidator = new LanguageFileValidator(); string[] files = Directory.GetFiles(Paths.PluginPath, "*.language", SearchOption.AllDirectories); foreach (string filePath in files) { ValidationResult validationResult = languageFileValidator.ValidateFile(filePath); if (!validationResult.IsValid) { num += validationResult.Errors.Count; } } } catch { } return num; } } public class LanguageFileCompiler { public void CompileLanguageFiles(string sourceDir, string outputDir) { if (!Directory.Exists(sourceDir)) { throw new DirectoryNotFoundException("Origem nao encontrada: " + sourceDir); } if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } string[] languageFiles = LanguageFileHelper.GetLanguageFiles(sourceDir); List list = new List(); string[] array = languageFiles; foreach (string text in array) { try { string contents = CompileFile(text); string relativePath = Path.GetRelativePath(sourceDir, text); string path = Path.Combine(outputDir, relativePath + ".compiled"); string directoryName = Path.GetDirectoryName(path); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(path, contents, Encoding.UTF8); } catch (Exception ex) { list.Add(Path.GetFileName(text) + ": " + ex.Message); } } if (list.Count > 0) { throw new AggregateException("Erros na compilacao:", list.Select((string e) => new Exception(e))); } } private static string CompileFile(string filePath) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("// COMPILED"); stringBuilder.AppendLine("// " + Path.GetFileName(filePath)); stringBuilder.AppendLine(); foreach (KeyValuePair> item in LanguageFileHelper.ParseTokensFromFile(filePath)) { string key = item.Key; foreach (KeyValuePair item2 in item.Value) { stringBuilder.AppendLine(key + "." + item2.Key + "=" + item2.Value); } } return stringBuilder.ToString().TrimEnd(); } } internal static class LanguageFileHelper { public static string[] GetLanguageFiles(string directory) { if (!Directory.Exists(directory)) { throw new DirectoryNotFoundException("Diretorio nao encontrado: " + directory); } return Directory.GetFiles(directory, "*.language", SearchOption.AllDirectories); } public static Dictionary> ParseTokensFromFile(string filePath) { string text = File.ReadAllText(filePath).TrimStart(); if (text.StartsWith("{")) { return ParseJsonTokens(text); } return ParseSimpleTokens(text, DetectLanguageFromFileName(filePath)); } private static Dictionary> ParseJsonTokens(string content) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); JSONNode val = JSON.Parse(content); if (val == (object)null) { return dictionary; } foreach (string key2 in val.Keys) { JSONNode val2 = val[key2]; if (val2 == (object)null) { continue; } string key = (key2.Equals("strings", StringComparison.OrdinalIgnoreCase) ? "generic" : key2); Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (string key3 in val2.Keys) { dictionary2[key3] = val2[key3].Value; } dictionary[key] = dictionary2; } return dictionary; } private static Dictionary> ParseSimpleTokens(string content, string language) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] array = content.Split('\n'); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (string.IsNullOrWhiteSpace(text) || text.StartsWith("//") || text.StartsWith("@")) { continue; } int num = text.IndexOf('='); if (num >= 1) { string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); if (text3.StartsWith("\"") && text3.EndsWith("\"") && text3.Length >= 2) { text3 = text3.Substring(1, text3.Length - 2); } if (!string.IsNullOrEmpty(text2)) { dictionary[text2] = text3; } } } return new Dictionary>(StringComparer.OrdinalIgnoreCase) { { language, dictionary } }; } internal static string DetectLanguageFromFileName(string filePath) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath); if (fileNameWithoutExtension == null) { return "generic"; } if (fileNameWithoutExtension.EndsWith("_eo", StringComparison.OrdinalIgnoreCase)) { return "eo"; } if (fileNameWithoutExtension.EndsWith("_la", StringComparison.OrdinalIgnoreCase)) { return "la"; } if (fileNameWithoutExtension.EndsWith("_uk", StringComparison.OrdinalIgnoreCase)) { return "uk"; } if (fileNameWithoutExtension.EndsWith("_pt-BR", StringComparison.OrdinalIgnoreCase)) { return "pt-BR"; } if (fileNameWithoutExtension.EndsWith("_en", StringComparison.OrdinalIgnoreCase)) { return "en"; } if (fileNameWithoutExtension.EndsWith("_de", StringComparison.OrdinalIgnoreCase)) { return "de"; } if (fileNameWithoutExtension.EndsWith("_fr", StringComparison.OrdinalIgnoreCase)) { return "fr"; } if (fileNameWithoutExtension.EndsWith("_es", StringComparison.OrdinalIgnoreCase)) { return "es-ES"; } if (fileNameWithoutExtension.EndsWith("_ru", StringComparison.OrdinalIgnoreCase)) { return "ru"; } if (fileNameWithoutExtension.EndsWith("_ja", StringComparison.OrdinalIgnoreCase)) { return "ja"; } if (fileNameWithoutExtension.EndsWith("_ko", StringComparison.OrdinalIgnoreCase)) { return "ko"; } if (fileNameWithoutExtension.EndsWith("_zh", StringComparison.OrdinalIgnoreCase)) { return "zh-CN"; } if (fileNameWithoutExtension.EndsWith("_tr", StringComparison.OrdinalIgnoreCase)) { return "tr"; } if (fileNameWithoutExtension.EndsWith("_it", StringComparison.OrdinalIgnoreCase)) { return "it"; } return "generic"; } public static string[] GetCodeFiles(string directory) { if (!Directory.Exists(directory)) { return Array.Empty(); } return Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories); } } public class LanguageFileValidator { private static readonly Regex TokenPattern = new Regex("^[A-Za-z0-9_.]+\\s*=", RegexOptions.Compiled); private static readonly Regex CommentPattern = new Regex("^\\s*//", RegexOptions.Compiled); private static readonly Regex TokenNameRegex = new Regex("^[A-Za-z0-9_.]+$", RegexOptions.Compiled); public ValidationResult ValidateFile(string filePath) { if (!File.Exists(filePath)) { return ValidationResult.Error("Arquivo nao encontrado: " + filePath); } string[] array = File.ReadAllLines(filePath); List list = new List(); List list2 = new List(); for (int i = 0; i < array.Length; i++) { string text = array[i]; int num = i + 1; if (string.IsNullOrWhiteSpace(text) || CommentPattern.IsMatch(text)) { continue; } string text2 = text.Trim(); if (!(text2 == "{") && !(text2 == "}")) { if (!TokenPattern.IsMatch(text2)) { list.Add($"Linha {num}: Sintaxe invalida: '{text}'"); } if (HasPotentialIssues(text)) { list2.Add($"Linha {num}: Possivel problema de espacos: '{text}'"); } } } return new ValidationResult { IsValid = (list.Count == 0), Errors = list, Warnings = list2, FilePath = filePath }; } public bool ValidateTokenSyntax(string token) { if (string.IsNullOrWhiteSpace(token)) { return false; } int num = token.IndexOf('='); if (num < 1) { return false; } string text = token.Substring(0, num).Trim(); string value = token.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(value)) { return IsValidTokenName(text); } return false; } private static bool IsValidTokenName(string tokenName) { if (TokenNameRegex.IsMatch(tokenName)) { return !tokenName.StartsWith("//"); } return false; } private static bool HasPotentialIssues(string line) { if (!line.Contains(" ") && !line.StartsWith(" ")) { return line.EndsWith(" "); } return true; } } public class ValidationResult { public bool IsValid { get; set; } public List Errors { get; set; } = new List(); public List Warnings { get; set; } = new List(); public string FilePath { get; set; } public static ValidationResult Error(string message) { return new ValidationResult { IsValid = false, Errors = new List { message } }; } } public class LanguageHotReload : IDisposable { private static LanguageHotReload _instance; private FileSystemWatcher _languageWatcher; private FileSystemWatcher _jsonWatcher; private readonly Dictionary _lastModified = new Dictionary(); private readonly Queue _pending = new Queue(); private readonly object _pendingLock = new object(); private System.Timers.Timer _debounceTimer; private SynchronizationContext _unityContext; public static LanguageHotReload Instance => _instance ?? (_instance = new LanguageHotReload()); public bool IsEnabled { get; private set; } public string WatchDirectory { get; private set; } public event Action OnTokenReloaded; public event Action OnFileReloaded; public event Action> OnBatchReloaded; public event Action OnBeforeReload; public event Action OnAfterReload; public void Enable(string directory) { if (IsEnabled) { Disable(); } if (!Directory.Exists(directory)) { Debug.LogError((object)("[LanguageAPI] Diretorio nao encontrado: " + directory)); return; } _unityContext = SynchronizationContext.Current; WatchDirectory = directory; try { _languageWatcher = new FileSystemWatcher(directory) { Filter = "*.language", IncludeSubdirectories = true, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite) }; _languageWatcher.Changed += OnChanged; _languageWatcher.Created += OnChanged; _languageWatcher.Deleted += OnChanged; _languageWatcher.EnableRaisingEvents = true; string path = Path.Combine(directory, "PELE", "Language"); if (Directory.Exists(path)) { _jsonWatcher = new FileSystemWatcher(path) { Filter = "*.json", IncludeSubdirectories = true, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite) }; _jsonWatcher.Changed += OnChanged; _jsonWatcher.Created += OnChanged; _jsonWatcher.Deleted += OnChanged; _jsonWatcher.EnableRaisingEvents = true; } _debounceTimer = new System.Timers.Timer(500.0) { AutoReset = false }; _debounceTimer.Elapsed += delegate { ProcessPending(); }; IsEnabled = true; } catch (Exception ex) { Debug.LogError((object)("[LanguageAPI] Erro ao iniciar FileSystemWatcher: " + ex.Message)); } } public void Disable() { IsEnabled = false; if (_languageWatcher != null) { _languageWatcher.EnableRaisingEvents = false; _languageWatcher.Changed -= OnChanged; _languageWatcher.Created -= OnChanged; _languageWatcher.Deleted -= OnChanged; _languageWatcher.Dispose(); _languageWatcher = null; } if (_jsonWatcher != null) { _jsonWatcher.EnableRaisingEvents = false; _jsonWatcher.Changed -= OnChanged; _jsonWatcher.Created -= OnChanged; _jsonWatcher.Deleted -= OnChanged; _jsonWatcher.Dispose(); _jsonWatcher = null; } _debounceTimer?.Stop(); _debounceTimer?.Dispose(); _debounceTimer = null; lock (_pendingLock) { _pending.Clear(); _lastModified.Clear(); } } public void Dispose() { Disable(); } private void OnChanged(object sender, FileSystemEventArgs e) { lock (_pendingLock) { try { DateTime lastWriteTime = File.GetLastWriteTime(e.FullPath); if (_lastModified.TryGetValue(e.FullPath, out var value) && value == lastWriteTime) { return; } _lastModified[e.FullPath] = lastWriteTime; } catch { return; } if (!_pending.Contains(e.FullPath)) { _pending.Enqueue(e.FullPath); } _debounceTimer?.Stop(); _debounceTimer?.Start(); } } private void ProcessPending() { if (!IsEnabled) { return; } List files; lock (_pendingLock) { files = new List(_pending); _pending.Clear(); } if (files.Count == 0) { return; } SynchronizationContext unityContext = _unityContext; if (unityContext != null) { unityContext.Post(delegate { FireEvents(); }, null); } else { FireEvents(); } void FireEvents() { try { this.OnBeforeReload?.Invoke(); } catch (Exception ex) { Debug.LogError((object)("[LanguageAPI] OnBeforeReload: " + ex.Message)); } LanguageAPI.ClearAllTokens(); string[] languageFiles = LanguageFileHelper.GetLanguageFiles(WatchDirectory); foreach (string text in languageFiles) { try { foreach (KeyValuePair> item in LanguageFileHelper.ParseTokensFromFile(text)) { foreach (KeyValuePair item2 in item.Value) { LanguageAPI.AddOrUpdateToken(item2.Key, item2.Value, item.Key); try { this.OnTokenReloaded?.Invoke(item.Key, item2.Key); } catch (Exception ex2) { Debug.LogError((object)("[LanguageAPI] OnTokenReloaded: " + ex2.Message)); } } } try { this.OnFileReloaded?.Invoke(text); } catch (Exception ex3) { Debug.LogError((object)("[LanguageAPI] OnFileReloaded: " + ex3.Message)); } } catch (Exception ex4) { Debug.LogError((object)("[LanguageAPI] Erro ao recarregar " + text + ": " + ex4.Message)); } } try { this.OnAfterReload?.Invoke(); } catch (Exception ex5) { Debug.LogError((object)("[LanguageAPI] OnAfterReload: " + ex5.Message)); } try { this.OnBatchReloaded?.Invoke(files); } catch (Exception ex6) { Debug.LogError((object)("[LanguageAPI] OnBatchReloaded: " + ex6.Message)); } } } } internal static class LanguageNames { private static readonly Dictionary Map = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "en", "English" }, { "pt-BR", "Portuguese (Brazil)" }, { "es-ES", "Spanish (Spain)" }, { "de", "German" }, { "fr", "French" }, { "it", "Italian" }, { "ja", "Japanese" }, { "ko", "Korean" }, { "ru", "Russian" }, { "tr", "Turkish" }, { "uk", "Ukrainian" }, { "zh-CN", "Chinese (Simplified)" }, { "es-419", "Spanish (Latin America)" }, { "la", "Latin" }, { "eo", "Esperanto" } }; internal static string GetFriendlyName(string langCode) { if (!Map.TryGetValue(langCode, out string value)) { return langCode; } return value; } internal static List GetAvailableLanguages(bool includeCustom = true) { try { if (typeof(Language).GetField("languagesByName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) is Dictionary dictionary) { HashSet hashSet = new HashSet(dictionary.Keys, StringComparer.OrdinalIgnoreCase); if (includeCustom) { string[] array = new string[3] { "uk", "la", "eo" }; foreach (string text in array) { if (!hashSet.Contains(text)) { Language.FindLanguageByName(text); hashSet.Add(text); } } } return hashSet.OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToList(); } } catch { } return new List(); } } internal static class LanguagePauseMenu { private class LanguageDialogCloseWatcher : MonoBehaviour { private float _delay = 0.4f; public SimpleDialogBox dialog; private bool _closing; private void Update() { if (!_closing) { _delay -= Time.unscaledDeltaTime; if (!(_delay > 0f) && (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)331))) { _closing = true; CloseDialog(dialog); } } } } private class LanguageInputLegendController : MonoBehaviour { public MPEventSystem eventSystem; public InputBindingDisplayController applyGlyph; public InputBindingDisplayController cancelGlyph; private void Update() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 bool visible = (Object)(object)eventSystem != (Object)null && (int)eventSystem.currentInputSource == 1; SetGlyphVisible(applyGlyph, visible); SetGlyphVisible(cancelGlyph, visible); } private static void SetGlyphVisible(InputBindingDisplayController glyph, bool visible) { if (!((Object)(object)glyph == (Object)null)) { HGTextMeshProUGUI component = ((Component)glyph).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = visible; } Transform val = ((Component)glyph).transform.parent.Find("KeyFallback"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(!visible); } } } } [CompilerGenerated] private static class <>O { public static hook_Awake <0>__OnPauseAwake; } private static bool _hooksActive; private static MethodInfo _setCurrentLanguageMethod; internal static void Init() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if (!_hooksActive) { _hooksActive = true; _setCurrentLanguageMethod = typeof(Language).GetMethod("SetCurrentLanguage", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = <>O.<0>__OnPauseAwake; if (obj == null) { hook_Awake val = OnPauseAwake; <>O.<0>__OnPauseAwake = val; obj = (object)val; } PauseScreenController.Awake += (hook_Awake)obj; } } internal static void Cleanup() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (_hooksActive) { _hooksActive = false; object obj = <>O.<0>__OnPauseAwake; if (obj == null) { hook_Awake val = OnPauseAwake; <>O.<0>__OnPauseAwake = val; obj = (object)val; } PauseScreenController.Awake -= (hook_Awake)obj; } } private static void OnPauseAwake(orig_Awake orig, PauseScreenController self) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown orig.Invoke(self); try { RegisterUiTokens(); RectTransform mainPanel = self.mainPanel; if ((Object)(object)mainPanel == (Object)null || ((Transform)mainPanel).childCount == 0) { return; } Transform child = ((Transform)mainPanel).GetChild(0).GetChild(1); if (!((Object)(object)child == (Object)null)) { Transform obj = Object.Instantiate(child, ((Transform)mainPanel).GetChild(0)); ((Object)obj).name = "LanguageSwitcherButton"; LanguageTextMeshController componentInChildren = ((Component)obj).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.token = "PELE_LANGUAGE_BUTTON"; } HGTextMeshProUGUI componentInChildren2 = ((Component)obj).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).text = Language.GetString("PELE_LANGUAGE_BUTTON"); } HGButton component = ((Component)obj).GetComponent(); ((Button)component).onClick = new ButtonClickedEvent(); ((UnityEvent)((Button)component).onClick).AddListener((UnityAction)delegate { ShowLanguageDialog(self); }); ((Component)obj).transform.SetAsLastSibling(); } } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)("Falha no botao de idiomas: " + ex.Message)); } } } private static void RegisterUiTokens() { LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Language"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Idioma", "pt-BR"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Lingua", "la"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Lingvo", "eo"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Мова", "uk"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Sprache", "de"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Idioma", "es-ES"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Idioma", "es-419"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Langue", "fr"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Lingua", "it"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "言語", "ja"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "언어", "ko"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Язык", "ru"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "Dil", "tr"); LanguageAPI.AddOrUpdateToken("PELE_LANGUAGE_BUTTON", "语言", "zh-CN"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_TITLE", "Select Language"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_TITLE", "Selecionar idioma", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_TITLE", "Elige linguam", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_TITLE", "Elekti lingvon", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_TITLE", "Вибрати мову", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_DESC", "Choose a language below. The change applies immediately."); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_DESC", "Escolha um idioma abaixo. A troca e aplicada imediatamente.", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_DESC", "Elige linguam infra. Mutatio statim fit.", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_DESC", "Elektu lingvon sube. La sxangxo validas tuj.", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_DESC", "Оберіть мову нижче. Зміна застосовується одразу.", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL", "Cancel"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL", "Cancelar", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL", "Abrogare", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL", "Nuligi", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL", "Скасувати", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_APPLY_HINT", "Apply"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_APPLY_HINT", "Aplicar", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_APPLY_HINT", "Applicare", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_APPLY_HINT", "Apliki", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_APPLY_HINT", "Застосувати", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL_HINT", "Cancel"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL_HINT", "Cancelar", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL_HINT", "Abrogare", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL_HINT", "Nuligi", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_CANCEL_HINT", "Скасувати", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "Left click"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "Clique esquerdo", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "Clic sinistro", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "Maldekstra klako", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "Ліва кнопка", "uk"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_ESC_HINT", "Esc"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_ESC_HINT", "Esc", "pt-BR"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_ESC_HINT", "Esc", "la"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_ESC_HINT", "Esc", "eo"); LanguageAPI.AddOrUpdateToken("SWITCH_LANGUAGE_ESC_HINT", "Esc", "uk"); } private static void SetLanguage(string lang) { if (_setCurrentLanguageMethod == null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)"SetCurrentLanguage method not found via reflection"); } return; } try { if (LanguageConVar.instance != null) { ((BaseConVar)LanguageConVar.instance).SetString(lang); return; } _setCurrentLanguageMethod.Invoke(null, new object[1] { lang }); } catch (Exception ex) { string text = ex.InnerException?.Message ?? ex.Message; string text2 = ex.InnerException?.StackTrace ?? ex.StackTrace; ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogError((object)("Falha ao trocar lingua para '" + lang + "': " + text)); } ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogError((object)(" Stack: " + text2)); } } } private static void ShowLanguageDialog(PauseScreenController pause) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown RegisterUiTokens(); List availableLanguages = LanguageNames.GetAvailableLanguages(); foreach (string item in availableLanguages) { LanguageAPI.AddOrUpdateToken("LANG_SEL_" + item, LanguageNames.GetFriendlyName(item)); } EventSystem current2 = EventSystem.current; SimpleDialogBox val = SimpleDialogBox.Create((MPEventSystem)(object)((current2 is MPEventSystem) ? current2 : null)); if ((Object)(object)val == (Object)null) { return; } val.headerToken = new TokenParamsPair { token = "SWITCH_LANGUAGE_TITLE" }; val.descriptionToken = new TokenParamsPair { token = "SWITCH_LANGUAGE_DESC" }; foreach (string item2 in availableLanguages) { string captured = item2; val.AddActionButton((UnityAction)delegate { SetLanguage(captured); }, "LANG_SEL_" + item2, true, Array.Empty()); } (((Object)(object)val.rootObject != (Object)null) ? val.rootObject : ((Component)val).gameObject).AddComponent().dialog = val; MonoBehaviour component = ((Component)pause).GetComponent(); if ((Object)(object)component != (Object)null) { component.StartCoroutine(DelayedGridLayout(val)); } else { ApplyGridLayout(val); } } private static void CloseDialog(SimpleDialogBox dialog) { if (!((Object)(object)dialog == (Object)null)) { Object.Destroy((Object)(object)(((Object)(object)dialog.rootObject != (Object)null) ? dialog.rootObject : ((Component)dialog).gameObject)); } } private static IEnumerator DelayedGridLayout(SimpleDialogBox dialog) { yield return null; ApplyGridLayout(dialog); } private static void ApplyGridLayout(SimpleDialogBox dialog) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) try { RectTransform buttonContainer = dialog.buttonContainer; if ((Object)(object)buttonContainer == (Object)null) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)"ApplyGridLayout: buttonContainer e null"); } return; } LayoutGroup[] components = ((Component)buttonContainer).GetComponents(); foreach (LayoutGroup obj in components) { ((Behaviour)obj).enabled = false; Object.DestroyImmediate((Object)(object)obj, true); } GridLayoutGroup obj2 = ((Component)buttonContainer).gameObject.AddComponent(); obj2.constraint = (Constraint)1; obj2.constraintCount = 3; obj2.cellSize = new Vector2(200f, 40f); obj2.spacing = new Vector2(8f, 6f); obj2.startAxis = (Axis)0; ContentSizeFitter obj3 = ((Component)buttonContainer).GetComponent() ?? ((Component)buttonContainer).gameObject.AddComponent(); obj3.verticalFit = (FitMode)2; obj3.horizontalFit = (FitMode)2; AddInputLegend(dialog); LayoutRebuilder.ForceRebuildLayoutImmediate(buttonContainer); WrapGridNavigation(buttonContainer); LanguagePlugin.LogVerbose($"ApplyGridLayout: grid aplicado com {((Transform)buttonContainer).childCount} botoes"); } catch (Exception ex) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogError((object)("Falha ao aplicar grid layout: " + ex.Message)); } } } private static void WrapGridNavigation(RectTransform container) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) List list = new List(); MPButton[] componentsInChildren = ((Component)container).GetComponentsInChildren(); foreach (MPButton val in componentsInChildren) { if (!((Object)(object)((Component)val).GetComponentInParent() != (Object)null)) { list.Add(val); } } if (list.Count == 0) { return; } int num = Mathf.Min(3, list.Count); int num2 = (list.Count + num - 1) / num; for (int j = 0; j < list.Count; j++) { Navigation val2 = default(Navigation); ((Navigation)(ref val2)).mode = (Mode)4; Navigation navigation = val2; int num3 = j % num; int num4 = j / num; int num5 = ((num3 > 0) ? (j - 1) : (j + num - 1)); int num6 = ((num3 < num - 1) ? (j + 1) : (j - num3)); int num7 = ((num4 > 0) ? (j - num) : ((num2 - 1) * num + Mathf.Min(num3, list.Count - (num2 - 1) * num - 1))); int num8 = ((num4 < num2 - 1) ? Mathf.Min(j + num, list.Count - 1) : num3); if (num5 >= 0 && num5 < list.Count) { ((Navigation)(ref navigation)).selectOnLeft = (Selectable)(object)list[num5]; } if (num6 >= 0 && num6 < list.Count) { ((Navigation)(ref navigation)).selectOnRight = (Selectable)(object)list[num6]; } if (num7 >= 0 && num7 < list.Count) { ((Navigation)(ref navigation)).selectOnUp = (Selectable)(object)list[num7]; } if (num8 >= 0 && num8 < list.Count) { ((Navigation)(ref navigation)).selectOnDown = (Selectable)(object)list[num8]; } ((Selectable)list[j]).navigation = navigation; } } private static void AddInputLegend(SimpleDialogBox dialog) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown RectTransform val = dialog?.buttonContainer; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Transform)val).Find("PELELanguageInputLegend") != (Object)null)) { GameObject val2 = new GameObject("PELELanguageInputLegend", new Type[3] { typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(LanguageInputLegendController) }); RectTransform component = val2.GetComponent(); ((Transform)component).SetParent((Transform)(object)val, false); component.sizeDelta = new Vector2(616f, 42f); LayoutElement obj = val2.AddComponent(); obj.minWidth = 616f; obj.preferredWidth = 616f; obj.minHeight = 42f; obj.preferredHeight = 42f; obj.flexibleWidth = 0f; obj.flexibleHeight = 0f; HorizontalLayoutGroup component2 = val2.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 58f; ((LayoutGroup)component2).padding = new RectOffset(8, 0, 8, 0); ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false; LanguageInputLegendController component3 = val2.GetComponent(); ref MPEventSystem eventSystem = ref component3.eventSystem; EventSystem current = EventSystem.current; eventSystem = (MPEventSystem)(object)((current is MPEventSystem) ? current : null); component3.applyGlyph = AddLegendItem(component, "UISubmit", "SWITCH_LANGUAGE_MOUSE_APPLY_HINT", "SWITCH_LANGUAGE_APPLY_HINT"); component3.cancelGlyph = AddLegendItem(component, "UICancel", "SWITCH_LANGUAGE_ESC_HINT", "SWITCH_LANGUAGE_CANCEL_HINT"); } } private static InputBindingDisplayController AddLegendItem(RectTransform parent, string actionName, string keyToken, string textToken) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(actionName + "Legend", new Type[2] { typeof(RectTransform), typeof(HorizontalLayoutGroup) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); component.sizeDelta = new Vector2(270f, 30f); HorizontalLayoutGroup component2 = val.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 7f; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false; LayoutElement obj = val.AddComponent(); obj.minWidth = 270f; obj.preferredWidth = 270f; obj.minHeight = 30f; obj.preferredHeight = 30f; GameObject val2 = new GameObject("Glyph", new Type[1] { typeof(RectTransform) }); val2.SetActive(false); RectTransform component3 = val2.GetComponent(); ((Transform)component3).SetParent((Transform)(object)component, false); component3.sizeDelta = new Vector2(28f, 28f); HGTextMeshProUGUI obj2 = val2.AddComponent(); ((TMP_Text)obj2).fontSize = 18f; ((TMP_Text)obj2).alignment = (TextAlignmentOptions)514; InputBindingDisplayController val3 = val2.AddComponent(); val3.actionName = actionName; val3.useExplicitInputSource = true; val3.explicitInputSource = (InputSource)1; val2.SetActive(true); GameObject val4 = new GameObject("KeyFallback", new Type[1] { typeof(RectTransform) }); val4.SetActive(false); RectTransform component4 = val4.GetComponent(); ((Transform)component4).SetParent((Transform)(object)component, false); component4.sizeDelta = new Vector2(158f, 28f); HGTextMeshProUGUI obj3 = val4.AddComponent(); ((TMP_Text)obj3).fontSize = 14f; ((TMP_Text)obj3).alignment = (TextAlignmentOptions)4097; ((TMP_Text)obj3).enableWordWrapping = false; val4.AddComponent().token = keyToken; val4.SetActive(true); GameObject val5 = new GameObject("Label", new Type[1] { typeof(RectTransform) }); val5.SetActive(false); RectTransform component5 = val5.GetComponent(); ((Transform)component5).SetParent((Transform)(object)component, false); component5.sizeDelta = new Vector2(76f, 28f); HGTextMeshProUGUI obj4 = val5.AddComponent(); ((TMP_Text)obj4).fontSize = 14f; ((TMP_Text)obj4).alignment = (TextAlignmentOptions)4097; ((TMP_Text)obj4).enableWordWrapping = false; val5.AddComponent().token = textToken; val5.SetActive(true); return val3; } } [BepInPlugin("com.bepis.r2api.language", "R2API.Language (Jaosnake fork)", "2.0.0")] public sealed class LanguagePlugin : BaseUnityPlugin { private Harmony _harmony; private Action> _collectLanguageRootFoldersHandler; private static ConfigEntry _enableDebugMenu; private static ConfigEntry _enableVerboseLogging; private static ConfigEntry _enableHotReload; internal static ManualLogSource Logger { get; set; } internal static bool EnableDebugMenu => _enableDebugMenu?.Value ?? false; internal static bool EnableVerboseLogging => _enableVerboseLogging?.Value ?? false; internal static bool EnableHotReload => _enableHotReload?.Value ?? true; private void Awake() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; LanguageAPI.Log = Logger; BindConfig(); Logger.LogInfo((object)"R2API.Language (Jaosnake fork) inicializado!"); if (EnableDebugMenu) { LanguageDebugUI.Create(); } LanguagePauseMenu.Init(); ConsoleCommands.Init(); _collectLanguageRootFoldersHandler = delegate(List folders) { string text = Path.Combine(Paths.PluginPath, "PELE", "Language"); if (Directory.Exists(text)) { folders.Add(text); LogVerbose("Pasta PELE/Language registrada: " + text); } }; Language.collectLanguageRootFolders += _collectLanguageRootFoldersHandler; try { _harmony = new Harmony("jaosnake.r2api.language"); _harmony.PatchAll(typeof(LanguagePlugin)); _harmony.PatchAll(typeof(CustomLanguageRegistration)); _harmony.PatchAll(typeof(CyrillicFontSupport)); LogVerbose("Harmony patches aplicados"); } catch (Exception ex) { Logger.LogError((object)("Falha ao aplicar Harmony patches: " + ex.Message)); } } private void OnEnable() { LanguageAPI.SetHooks(); PeleStartupDiagnostics.LogOnce(); ReloadPeleJsonFiles(); Logger.LogInfo((object)("PELE JSONs carregados no startup (" + PeleJsonLoader.CountTokens() + " tokens)")); if (EnableHotReload) { LanguageAPI.EnableHotReload(Paths.PluginPath); LanguageHotReload.Instance.OnAfterReload += OnHotReloadCompleted; Logger.LogInfo((object)"Hot-Reload habilitado! Pressione F5 para recarregar manualmente."); } else { Logger.LogInfo((object)"Hot-Reload desabilitado por config."); } } private void OnDisable() { LanguageAPI.UnsetHooks(); LanguageAPI.DisableHotReload(); Language.collectLanguageRootFolders -= _collectLanguageRootFoldersHandler; LanguageHotReload.Instance.OnAfterReload -= OnHotReloadCompleted; } private void OnDestroy() { LanguageAPI.DisableHotReload(); LanguageAPI.DisposeLock(); Language.collectLanguageRootFolders -= _collectLanguageRootFoldersHandler; LanguageHotReload.Instance.OnAfterReload -= OnHotReloadCompleted; LanguageDebugUI.DestroyInstance(); LanguagePauseMenu.Cleanup(); ConsoleCommands.Cleanup(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { if (EnableHotReload && Input.GetKeyDown((KeyCode)286)) { Logger.LogInfo((object)"F5 pressionado! Recarregando todas as linguagens..."); LanguageAPI.ReloadAllLanguageFiles(Paths.PluginPath); ReloadPeleJsonFiles(); Logger.LogInfo((object)"Recarregamento concluido!"); } } private void OnHotReloadCompleted() { ReloadPeleJsonFiles(); } public static void ReloadPeleJsonFiles() { PeleJsonLoader.Reload(); } internal static string[] GetRegisteredCustomLangs() { return CustomLanguageRegistration.GetRegisteredCustomLangs(); } internal static string[] GetFallbackLangs() { return CustomLanguageRegistration.GetFallbackLangs(); } public static string FixCultureName(string name) { return CustomLanguageRegistration.FixCultureName(name); } internal static void LogVerbose(string message) { if (EnableVerboseLogging) { ManualLogSource logger = Logger; if (logger != null) { logger.LogInfo((object)message); } } } private void BindConfig() { _enableHotReload = ((BaseUnityPlugin)this).Config.Bind("PELE", "EnableHotReload", true, "Enable F5/manual and file watcher language reload."); _enableDebugMenu = ((BaseUnityPlugin)this).Config.Bind("PELE", "EnableDebugMenu", false, "Enable the F6 PELE debug window."); _enableVerboseLogging = ((BaseUnityPlugin)this).Config.Bind("PELE", "EnableVerboseLogging", false, "Enable extra diagnostic logs for PELE hooks and UI layout."); } } public class ModInstallationWizard { public void CreateModStructure(string modPath) { if (string.IsNullOrWhiteSpace(modPath)) { throw new ArgumentException("Caminho invalido", "modPath"); } Directory.CreateDirectory(modPath); string[] array = new string[3] { "Plugins", "Language", "Assets" }; foreach (string path in array) { Directory.CreateDirectory(Path.Combine(modPath, path)); } Debug.Log((object)("[LanguageAPI] Estrutura criada em: " + modPath)); } public void SetupLanguageFiles(string modPath) { string text = Path.Combine(modPath, "Language"); Directory.CreateDirectory(text); WriteIfAbsent(Path.Combine(text, "en.language"), "EXAMPLE_TOKEN = \"Example text\"\n"); WriteIfAbsent(Path.Combine(text, "pt-BR.language"), "EXAMPLE_TOKEN = \"Texto de exemplo\"\n"); Debug.Log((object)("[LanguageAPI] Arquivos de idioma criados em: " + text)); } public void GenerateBasicLanguageFile(string modName, string modPath) { if (string.IsNullOrWhiteSpace(modName)) { throw new ArgumentException("Nome do mod invalido", "modName"); } string text = Path.Combine(modPath, "Language"); Directory.CreateDirectory(text); string text2 = modName.Replace(" ", "_").ToUpperInvariant(); string text3 = Path.Combine(text, modName.Replace(" ", "_") + ".language"); File.WriteAllText(text3, "// " + modName + "\nMOD_" + text2 + "_NAME = \"" + modName + "\"\nMOD_" + text2 + "_DESCRIPTION = \"Descricao do " + modName + "\"\n"); Debug.Log((object)("[LanguageAPI] Arquivo gerado: " + text3)); } private static void WriteIfAbsent(string path, string content) { if (!File.Exists(path)) { File.WriteAllText(path, content); } } } internal static class PeleJsonLoader { internal static void Reload() { LanguageAPI.InvalidateDiskCache(); try { string path = Path.Combine(Paths.PluginPath, "PELE", "Language"); if (!Directory.Exists(path)) { return; } string[] directories = Directory.GetDirectories(path); foreach (string path2 in directories) { string fileName = Path.GetFileName(path2); if (string.IsNullOrEmpty(fileName)) { continue; } string[] files = Directory.GetFiles(path2, "*.json"); foreach (string text in files) { try { JSONNode val = JSON.Parse(File.ReadAllText(text)); if (!(val == (object)null)) { AddJsonTokens(val, fileName); } } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogError((object)("Erro ao recarregar " + text + ": " + ex.Message)); } } } } } catch (Exception ex2) { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogError((object)("Erro em PeleJsonLoader.Reload: " + ex2.Message)); } } } internal static int CountTokens() { try { string path = Path.Combine(Paths.PluginPath, "PELE", "Language"); if (!Directory.Exists(path)) { return 0; } int num = 0; string[] directories = Directory.GetDirectories(path); foreach (string path2 in directories) { if (string.IsNullOrEmpty(Path.GetFileName(path2))) { continue; } string[] files = Directory.GetFiles(path2, "*.json"); foreach (string path3 in files) { try { JSONNode val = JSON.Parse(File.ReadAllText(path3)); if (val != (object)null) { num += CountJsonTokens(val); } } catch { } } } return num; } catch { return 0; } } private static int AddJsonTokens(JSONNode json, string langCode) { if (json == (object)null) { return 0; } JSONNode val = json["strings"]; if (val != (object)null && val.Count > 0) { return AddTokenObject(val, langCode); } return AddTokenObject(json, langCode, skipManifestKeys: true); } private static int AddTokenObject(JSONNode node, string langCode, bool skipManifestKeys = false) { int num = 0; foreach (string key in node.Keys) { if ((skipManifestKeys && string.Equals(key, "language", StringComparison.OrdinalIgnoreCase)) || (skipManifestKeys && string.Equals(key, "strings", StringComparison.OrdinalIgnoreCase)) || key.StartsWith("_", StringComparison.Ordinal)) { continue; } JSONNode val = node[key]; if (!(val == (object)null) && val.Count <= 0) { string value = val.Value; if (value != null) { LanguageAPI.AddOrUpdateToken(key, value, langCode); num++; } } } return num; } private static int CountJsonTokens(JSONNode json) { JSONNode val = json["strings"]; if (val != (object)null && val.Count > 0) { return CountTokenObject(val); } return CountTokenObject(json, skipManifestKeys: true); } private static int CountTokenObject(JSONNode node, bool skipManifestKeys = false) { int num = 0; foreach (string key in node.Keys) { if ((!skipManifestKeys || !string.Equals(key, "language", StringComparison.OrdinalIgnoreCase)) && (!skipManifestKeys || !string.Equals(key, "strings", StringComparison.OrdinalIgnoreCase)) && !key.StartsWith("_", StringComparison.Ordinal) && !(node[key] == (object)null) && node[key].Count <= 0) { num++; } } return num; } } internal static class PeleStartupDiagnostics { private static readonly string[] RequiredCustomLanguages = new string[3] { "la", "eo", "uk" }; private static bool _logged; internal static void LogOnce() { if (_logged) { return; } _logged = true; try { LogPeleFolders(); LogCyrillicFontBundle(); LogDuplicateLanguageDlls(); } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)("PELE startup diagnostics falhou: " + ex.Message)); } } } private static void LogPeleFolders() { string text = Path.Combine(Paths.PluginPath, "PELE", "Language"); if (!Directory.Exists(text)) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)("PELE/Language nao encontrado: " + text)); } return; } ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("PELE/Language encontrado: " + text)); } List list = new List(); string[] requiredCustomLanguages = RequiredCustomLanguages; foreach (string text2 in requiredCustomLanguages) { string text3 = Path.Combine(text, text2); if (!Directory.Exists(text3)) { ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)("Idioma PELE ausente: " + text3)); } continue; } string text4 = Path.Combine(text3, "language.json"); if (!File.Exists(text4)) { ManualLogSource logger4 = LanguagePlugin.Logger; if (logger4 != null) { logger4.LogWarning((object)("language.json ausente para '" + text2 + "': " + text4)); } } string text5 = Path.Combine(text3, "icon.png"); if (!File.Exists(text5)) { ManualLogSource logger5 = LanguagePlugin.Logger; if (logger5 != null) { logger5.LogWarning((object)("icon.png ausente para '" + text2 + "': " + text5)); } } list.Add(text2 + "=" + CountJsonTokens(text3)); } if (list.Count > 0) { ManualLogSource logger6 = LanguagePlugin.Logger; if (logger6 != null) { logger6.LogInfo((object)("Tokens PELE por idioma: " + string.Join(", ", list))); } } } private static void LogCyrillicFontBundle() { if (File.Exists(Path.Combine(Paths.PluginPath, "PELE", "Fonts", "cyrillicfont"))) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogInfo((object)"PELE/Fonts/cyrillicfont encontrado."); } } else { ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)"PELE/Fonts/cyrillicfont ausente; ucraniano pode depender de fallback."); } } } private static void LogDuplicateLanguageDlls() { string[] files = Directory.GetFiles(Paths.PluginPath, "R2API.Language.dll", SearchOption.AllDirectories); if (files.Length == 1) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogInfo((object)"DLL R2API.Language unica detectada."); } return; } ManualLogSource logger2 = LanguagePlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)("Detectadas " + files.Length + " copias de R2API.Language.dll no perfil.")); } foreach (string item in files.OrderBy((string path) => path, StringComparer.OrdinalIgnoreCase)) { ManualLogSource logger3 = LanguagePlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)(" R2API.Language.dll: " + item)); } } } private static int CountJsonTokens(string langDir) { int num = 0; string[] files = Directory.GetFiles(langDir, "*.json"); foreach (string text in files) { try { JSONNode val = JSON.Parse(File.ReadAllText(text)); if (!(val == (object)null)) { JSONNode val2 = val["strings"]; num += ((val2 != (object)null && val2.Count > 0) ? CountTokenObject(val2) : CountTokenObject(val, skipManifestKeys: true)); } } catch (Exception ex) { ManualLogSource logger = LanguagePlugin.Logger; if (logger != null) { logger.LogWarning((object)("Falha ao contar tokens em " + text + ": " + ex.Message)); } } } return num; } private static int CountTokenObject(JSONNode node, bool skipManifestKeys = false) { int num = 0; foreach (string key in node.Keys) { if ((!skipManifestKeys || !string.Equals(key, "language", StringComparison.OrdinalIgnoreCase)) && (!skipManifestKeys || !string.Equals(key, "strings", StringComparison.OrdinalIgnoreCase)) && !key.StartsWith("_", StringComparison.Ordinal) && !(node[key] == (object)null) && node[key].Count <= 0) { num++; } } return num; } } public class TokenUsageAnalyzer { private static readonly Regex TokenRefRegex = new Regex("(?:Language\\.)?GetLocalizedStringByToken\\(\"([^\"]+)\"\\)", RegexOptions.Compiled); public List FindUnusedTokens(string directory) { HashSet allTokenNames = GetAllTokenNames(directory); HashSet tokensReferencedInCode = GetTokensReferencedInCode(directory); return allTokenNames.Except(tokensReferencedInCode, StringComparer.OrdinalIgnoreCase).ToList(); } public List FindBrokenReferences(string directory) { HashSet allTokenNames = GetAllTokenNames(directory); return GetTokensReferencedInCode(directory).Except(allTokenNames, StringComparer.OrdinalIgnoreCase).ToList(); } public Dictionary GetTokenUsageStats(string directory) { HashSet allTokenNames = GetAllTokenNames(directory); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (string item in allTokenNames) { dictionary[item] = 0; } string[] codeFiles = LanguageFileHelper.GetCodeFiles(directory); foreach (string path in codeFiles) { try { string input = File.ReadAllText(path); foreach (string item2 in allTokenNames) { int count = Regex.Matches(input, Regex.Escape(item2)).Count; if (count > 0) { dictionary[item2] += count; } } } catch { } } return dictionary; } private HashSet GetAllTokenNames(string directory) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); string[] languageFiles; try { languageFiles = LanguageFileHelper.GetLanguageFiles(directory); } catch { return hashSet; } string[] array = languageFiles; foreach (string filePath in array) { try { foreach (KeyValuePair> item in LanguageFileHelper.ParseTokensFromFile(filePath)) { foreach (string key in item.Value.Keys) { hashSet.Add(key); } } } catch { } } return hashSet; } private HashSet GetTokensReferencedInCode(string directory) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); string[] codeFiles = LanguageFileHelper.GetCodeFiles(directory); foreach (string path in codeFiles) { try { string input = File.ReadAllText(path); foreach (Match item in TokenRefRegex.Matches(input)) { if (item.Groups.Count > 1) { hashSet.Add(item.Groups[1].Value); } } } catch { } } return hashSet; } } }