Decompiled source of REPO Traducao ptBR v1.0.4

plugins/RepoTraducaoPTBR.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;

[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("Xuaun")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Tradução pt-BR do R.E.P.O. com toggles por categoria (via XUnity.AutoTranslator).")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+57f8097c8dd56d1aa10932d79709376950110c9f")]
[assembly: AssemblyProduct("RepoTraducaoPTBR")]
[assembly: AssemblyTitle("RepoTraducaoPTBR")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.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 RepoTraducaoPTBR
{
	internal static class AutoTranslatorBridge
	{
		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static Assembly? CoreAssembly => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "XUnity.AutoTranslator.Plugin.Core");

		private static Type? PluginType => CoreAssembly?.GetType("XUnity.AutoTranslator.Plugin.Core.AutoTranslationPlugin");

		private static Type? SettingsType => CoreAssembly?.GetType("XUnity.AutoTranslator.Plugin.Core.Configuration.Settings");

		private static object? Instance => PluginType?.GetField("Current", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);

		internal static bool IsLoaded => Instance != null;

		internal static string? ConfiguredEndpointId => SettingsType?.GetField("ServiceEndpoint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as string;

		internal static string? AutoTranslationsFilePath => SettingsType?.GetField("AutoTranslationsFilePath", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as string;

		internal static bool TryApplyConfig((string Section, string Key, string Value)[] entries, out bool changed, out string? error)
		{
			changed = false;
			error = null;
			try
			{
				object fieldOrProperty = GetFieldOrProperty(CoreAssembly?.GetType("XUnity.AutoTranslator.Plugin.Core.PluginEnvironment"), null, "Current");
				if (fieldOrProperty == null)
				{
					error = "PluginEnvironment.Current não encontrado";
					return false;
				}
				object obj = GetFieldOrProperty(fieldOrProperty.GetType(), fieldOrProperty, "Preferences") ?? CoreAssembly?.GetType("XUnity.AutoTranslator.Plugin.Core.IPluginEnvironment")?.GetProperty("Preferences", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(fieldOrProperty);
				if (obj == null)
				{
					error = "PluginEnvironment.Preferences não encontrado";
					return false;
				}
				PropertyInfo property = obj.GetType().GetProperty("Item", new Type[1] { typeof(string) });
				if (property == null)
				{
					error = "indexer do IniFile não encontrado";
					return false;
				}
				for (int i = 0; i < entries.Length; i++)
				{
					(string Section, string Key, string Value) tuple = entries[i];
					string item = tuple.Section;
					string item2 = tuple.Key;
					string item3 = tuple.Value;
					object value = property.GetValue(obj, new object[1] { item });
					object obj2 = (value?.GetType().GetProperty("Item", new Type[1] { typeof(string) }))?.GetValue(value, new object[1] { item2 });
					PropertyInfo propertyInfo = obj2?.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (propertyInfo == null)
					{
						error = "IniKey.Value não encontrado (" + item + "." + item2 + ")";
						return false;
					}
					if (!(propertyInfo.GetValue(obj2) as string == item3))
					{
						propertyInfo.SetValue(obj2, item3);
						changed = true;
					}
				}
				if (!changed)
				{
					return true;
				}
				MethodInfo methodInfo = SettingsType?.GetMethod("Configure", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (methodInfo == null)
				{
					error = "Settings.Configure não encontrado";
					return false;
				}
				methodInfo.Invoke(null, null);
				return true;
			}
			catch (Exception ex)
			{
				error = ex.InnerException?.Message ?? ex.Message;
				return false;
			}
		}

		internal static bool TryGetCurrentEndpointId(out string? id)
		{
			id = null;
			try
			{
				string error;
				object translationManager = GetTranslationManager(out error);
				object endpointManager = translationManager?.GetType().GetProperty("CurrentEndpoint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(translationManager);
				id = EndpointIdOf(endpointManager);
				return id != null;
			}
			catch
			{
				return false;
			}
		}

		internal static bool TrySetEndpoint(string endpointId, out string? error)
		{
			error = null;
			try
			{
				object instance = Instance;
				if (instance == null)
				{
					error = "AutoTranslator ainda não carregou";
					return false;
				}
				object translationManager = GetTranslationManager(out error);
				if (translationManager == null)
				{
					return false;
				}
				if (!(translationManager.GetType().GetProperty("AllEndpoints", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(translationManager) is IEnumerable enumerable))
				{
					error = "AllEndpoints não encontrado";
					return false;
				}
				object obj = null;
				foreach (object item in enumerable)
				{
					if (string.Equals(EndpointIdOf(item), endpointId, StringComparison.OrdinalIgnoreCase))
					{
						obj = item;
						break;
					}
				}
				if (obj == null)
				{
					error = "endpoint '" + endpointId + "' não existe";
					return false;
				}
				if (obj.GetType().GetProperty("Error", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is Exception ex)
				{
					error = "endpoint '" + endpointId + "' falhou ao inicializar: " + ex.Message;
					return false;
				}
				MethodInfo method = PluginType.GetMethod("OnEndpointSelected", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					error = "OnEndpointSelected não encontrado";
					return false;
				}
				method.Invoke(instance, new object[1] { obj });
				return true;
			}
			catch (Exception ex2)
			{
				error = ex2.InnerException?.Message ?? ex2.Message;
				return false;
			}
		}

		internal static void EnsureAutoGenFile(string? fallbackPath = null)
		{
			try
			{
				string text = AutoTranslationsFilePath ?? fallbackPath;
				if (!string.IsNullOrEmpty(text) && !File.Exists(text))
				{
					Directory.CreateDirectory(Path.GetDirectoryName(text));
					File.WriteAllText(text, string.Empty);
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogDebug("EnsureAutoGenFile ignorado: " + ex.Message);
			}
		}

		internal static bool TryReloadTranslations(out string? error)
		{
			error = null;
			try
			{
				object instance = Instance;
				if (instance == null)
				{
					error = "AutoTranslator ainda não carregou";
					return false;
				}
				MethodInfo method = PluginType.GetMethod("ReloadTranslations", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method == null)
				{
					error = "ReloadTranslations não encontrado";
					return false;
				}
				EnsureAutoGenFile();
				method.Invoke(instance, null);
				return true;
			}
			catch (Exception ex)
			{
				error = ex.InnerException?.Message ?? ex.Message;
				return false;
			}
		}

		private static object? GetFieldOrProperty(Type? type, object? obj, string name)
		{
			if (type == null)
			{
				return null;
			}
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if ((object)field != null)
			{
				return field.GetValue(obj);
			}
			return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
		}

		private static object? GetTranslationManager(out string? error)
		{
			error = null;
			object instance = Instance;
			if (instance == null)
			{
				error = "AutoTranslator ainda não carregou";
				return null;
			}
			object? obj = PluginType.GetField("TranslationManager", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance);
			if (obj == null)
			{
				error = "TranslationManager não encontrado";
			}
			return obj;
		}

		private static string? EndpointIdOf(object? endpointManager)
		{
			if (endpointManager == null)
			{
				return null;
			}
			object obj = endpointManager.GetType().GetProperty("Endpoint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(endpointManager);
			return obj?.GetType().GetProperty("Id", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) as string;
		}
	}
	internal sealed class AutoTranslatorConfigurator
	{
		private static readonly (string Section, string Key, string Value)[] Defaults = new(string, string, string)[10]
		{
			("General", "Language", "pt"),
			("General", "FromLanguage", "en"),
			("Files", "Directory", "config\\Translation\\{Lang}\\Text"),
			("Files", "OutputFile", "config\\Translation\\{Lang}\\Text\\_AutoGeneratedTranslations.txt"),
			("Files", "SubstitutionFile", "config\\Translation\\{Lang}\\Text\\_Substitutions.txt"),
			("Files", "PreprocessorsFile", "config\\Translation\\{Lang}\\Text\\_Preprocessors.txt"),
			("Files", "PostprocessorsFile", "config\\Translation\\{Lang}\\Text\\_Postprocessors.txt"),
			("Texture", "TextureDirectory", "config\\Translation\\{Lang}\\Texture"),
			("ResourceRedirector", "PreferredStoragePath", "config\\Translation\\{Lang}\\RedirectedResources"),
			("Behaviour", "ReloadTranslationsOnFileChange", "True")
		};

		private readonly IniFile _ini;

		private readonly PluginConfig _config;

		private readonly string _autoGenPath;

		public AutoTranslatorConfigurator(IniFile ini, PluginConfig config, string autoGenPath)
		{
			_ini = ini;
			_config = config;
			_autoGenPath = autoGenPath;
		}

		public void ApplyConfig()
		{
			if (AutoTranslatorBridge.IsLoaded)
			{
				if (AutoTranslatorBridge.TryApplyConfig(Defaults, out bool changed, out string error))
				{
					if (changed)
					{
						BceConsole.LogInfo("Config do AutoTranslator corrigida em memória (idioma/pastas pt-BR).");
						AutoTranslatorBridge.TryReloadTranslations(out string _);
					}
					return;
				}
				BceConsole.LogWarning("Não consegui aplicar a config na memória do AutoTranslator (" + error + "); editando o ini (vale no próximo boot).");
			}
			(string, string, string)[] defaults = Defaults;
			for (int i = 0; i < defaults.Length; i++)
			{
				var (section, key, value) = defaults[i];
				_ini.Set(section, key, value);
			}
		}

		public void ApplyEndpoint(bool announce)
		{
			string text = (_config.AutoTranslateRest.Value ? "GoogleTranslateV2" : "Passthrough");
			if (!AutoTranslatorBridge.TryGetCurrentEndpointId(out string id))
			{
				id = AutoTranslatorBridge.ConfiguredEndpointId;
			}
			if (string.Equals(id, text, StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			if (AutoTranslatorBridge.IsLoaded)
			{
				ClearAutoGenCache();
				AutoTranslatorBridge.TryReloadTranslations(out string _);
				if (AutoTranslatorBridge.TrySetEndpoint(text, out string error2))
				{
					if (announce)
					{
						BceConsole.LogInfo("Endpoint trocado AO VIVO para " + text + " — sem precisar reiniciar.");
					}
					return;
				}
				BceConsole.LogWarning("Troca de endpoint em runtime falhou (" + error2 + "); usando o plano B (ini + restart).");
			}
			bool num = _ini.Set("Service", "Endpoint", text);
			if (num && !AutoTranslatorBridge.IsLoaded)
			{
				ClearAutoGenCache();
			}
			if (num && announce)
			{
				BceConsole.LogWarning("Endpoint de tradução definido para " + text + " — REINICIE O JOGO para aplicar.");
			}
		}

		private void ClearAutoGenCache()
		{
			try
			{
				string? path = AutoTranslatorBridge.AutoTranslationsFilePath ?? _autoGenPath;
				Directory.CreateDirectory(Path.GetDirectoryName(path));
				File.WriteAllText(path, string.Empty);
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Não consegui limpar o cache de traduções: " + ex.Message);
			}
		}
	}
	internal sealed class IniFile
	{
		private readonly string _path;

		public IniFile(string path)
		{
			_path = path;
		}

		public bool Set(string section, string key, string value)
		{
			try
			{
				List<string> list = (File.Exists(_path) ? File.ReadAllLines(_path).ToList() : new List<string>());
				int num = list.FindIndex((string l) => l.Trim() == "[" + section + "]");
				if (num < 0)
				{
					if (list.Count > 0)
					{
						if (list[list.Count - 1].Trim().Length > 0)
						{
							list.Add("");
						}
					}
					list.Add("[" + section + "]");
					list.Add(key + "=" + value);
					File.WriteAllLines(_path, list);
					return true;
				}
				int num2 = list.FindIndex(num + 1, (string l) => l.TrimStart().StartsWith("["));
				if (num2 < 0)
				{
					num2 = list.Count;
				}
				for (int num3 = num + 1; num3 < num2; num3++)
				{
					string text = list[num3];
					int num4 = text.IndexOf('=');
					if (num4 > 0 && text.Substring(0, num4).Trim().Equals(key, StringComparison.OrdinalIgnoreCase))
					{
						if (text.Substring(num4 + 1).Trim() == value)
						{
							return false;
						}
						list[num3] = key + "=" + value;
						File.WriteAllLines(_path, list);
						return true;
					}
				}
				list.Insert(num2, key + "=" + value);
				File.WriteAllLines(_path, list);
				return true;
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha ao editar o AutoTranslatorConfig.ini (" + section + "." + key + "): " + ex.Message);
				return false;
			}
		}
	}
	internal static class QotlCompat
	{
		internal static void Install(Harmony harmony)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("QueenOfTheLosers.Modifiers.TitleChange");
			if (!(type == null))
			{
				MethodInfo methodInfo = AccessTools.Method(type, "ReplaceTitle", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					BceConsole.LogWarning("QueenOfTheLosers presente mas TitleChange.ReplaceTitle não foi encontrado — compat de título desativada.");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(QotlCompat), "ReplaceTitlePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				BceConsole.LogDebug("Compat QueenOfTheLosers instalada (título da arena traduzido).");
			}
		}

		private static void ReplaceTitlePostfix(string? title, ref string? __result)
		{
			if (__result != null && !string.IsNullOrEmpty(title))
			{
				string text = title.ToUpperInvariant() switch
				{
					"QUEEN" => "RAINHA", 
					"KING" => "REI", 
					"MONARCH" => "MONARCA", 
					"RULER" => "SOBERANO", 
					_ => title, 
				};
				__result = Regex.Replace(__result, "\\bREI\\b", text.ToUpperInvariant());
				__result = Regex.Replace(__result, "\\bRei\\b", char.ToUpperInvariant(text[0]) + text.Substring(1).ToLowerInvariant());
			}
		}
	}
	internal sealed class PluginConfig
	{
		private readonly Dictionary<TranslationCategory, ConfigEntry<bool>> _toggles = new Dictionary<TranslationCategory, ConfigEntry<bool>>();

		public ConfigEntry<bool> AutoTranslateRest { get; }

		public ConfigEntry<bool> DumpCosmetics { get; }

		public IEnumerable<TranslationCategory> Categories => _toggles.Keys;

		public PluginConfig(ConfigFile config)
		{
			foreach (CategoryDef item in TranslationCategories.All)
			{
				_toggles[item.Id] = config.Bind<bool>("Categories", item.Key, true, item.Description);
			}
			AutoTranslateRest = config.Bind<bool>("Automatic Translation", "Translate the Rest (Automatic)", false, "Machine-translates (Google) anything the dictionaries don't cover. Applies instantly. When off, whatever is missing stays in English and is listed in _AutoGeneratedTranslations.txt.");
			DumpCosmetics = config.Bind<bool>("Tools", "Dump All Cosmetic Names", false, "Writes every cosmetic name currently loaded (vanilla + modded) that is MISSING from the dictionaries to config/Translation/pt/RTLC_dump_cosmeticos.txt, ready to translate. Use from the in-game mods menu, with all your cosmetic mods loaded.");
		}

		public ConfigEntry<bool> Toggle(TranslationCategory category)
		{
			return _toggles[category];
		}

		public bool IsEnabled(TranslationCategory category)
		{
			return _toggles[category].Value;
		}
	}
	internal enum TranslationCategory
	{
		Game,
		ModConfigs,
		MoreHeadBridge,
		VanillaCosmetics,
		XuaunCosmetics,
		ModdedCosmetics
	}
	internal sealed class CategoryDef
	{
		public TranslationCategory Id { get; }

		public string File { get; }

		public string Key { get; }

		public string Description { get; }

		public CategoryDef(TranslationCategory id, string file, string key, string description)
		{
			Id = id;
			File = file;
			Key = key;
			Description = description;
		}
	}
	internal static class TranslationCategories
	{
		public static readonly IReadOnlyList<CategoryDef> All = new CategoryDef[6]
		{
			new CategoryDef(TranslationCategory.Game, "Jogo.txt", "Translate Game", "R.E.P.O.'s own menus, HUD, settings and controls (native localization + dictionary)."),
			new CategoryDef(TranslationCategory.ModConfigs, "Config.txt", "Translate Mod Configs", "Mod configuration menus (REPOConfig, CustomGrabColor, Mimic…)."),
			new CategoryDef(TranslationCategory.MoreHeadBridge, "MoreHeadBridge.txt", "Translate MoreHead Bridge", "MoreHead Bridge interface (tabs, Customizer, Mini-Semibot…)."),
			new CategoryDef(TranslationCategory.VanillaCosmetics, "CosmeticosVanilla.txt", "Translate Vanilla Cosmetics", "Vanilla cosmetic names (they come from assetName; there is no official translation for them)."),
			new CategoryDef(TranslationCategory.XuaunCosmetics, "CosmeticosXuaun.txt", "Translate Xuaun Cosmetics", "Cosmetic names from Xuaun mods (XuaunCosmetics, RepoPride, FortniteSemibot, YoshyCarry, MonsterCosmetics)."),
			new CategoryDef(TranslationCategory.ModdedCosmetics, "CosmeticosModded.txt", "Translate Modded Cosmetics (Others)", "Cosmetic names from other mods (e.g.: MoreHead, BASICS, etc.).")
		};
	}
	internal static class CurrencyPatch
	{
		private static Func<bool> _enabled = null;

		private static readonly CultureInfo PtBr = new CultureInfo("pt-BR");

		private const string GlitchTaxWord = "DIN";

		private const string GlitchMoney = "R$";

		private static volatile string? _lastGlitch;

		private static readonly Regex MoneyRx = new Regex("(?<![\\w$=])\\$ ?(?=\\s*(?:<[^>]*>\\s*)*\\d)", RegexOptions.Compiled);

		private static readonly Regex ThousandRx = new Regex("((?:R\\$ |<sprite name=\\$+>)(?:\\s|</?[^>]*>)*[\\d.,]+(?:\\s|</?[^>]*>)*)[Kk](?![A-Za-z])", RegexOptions.Compiled);

		private static readonly Regex DialogDollarRx = new Regex("(?<![\\w=$])\\$", RegexOptions.Compiled);

		internal static void Install(Func<bool> enabled, Harmony harmony)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			_enabled = enabled;
			MethodInfo methodInfo = AccessTools.PropertySetter(AccessTools.TypeByName("TMPro.TMP_Text"), "text");
			if (methodInfo == null)
			{
				BceConsole.LogWarning("TMPro.TMP_Text.text não encontrado — troca de $ por R$ desativada (versão de TMP incompatível?).");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CurrencyPatch), "SymbolPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo2 = AccessTools.Method("SemiFunc:DollarGetString", new Type[1] { typeof(int) }, (Type[])null);
			if (methodInfo2 == null)
			{
				BceConsole.LogWarning("SemiFunc.DollarGetString não encontrado — separador de milhar pt-BR desativado.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CurrencyPatch), "DollarPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo3 = AccessTools.Method("SemiFunc:EmojiText", new Type[1] { typeof(string) }, (Type[])null);
			if (methodInfo3 == null)
			{
				BceConsole.LogWarning("SemiFunc.EmojiText não encontrado — 'R' antes do $ literal do diálogo desativado.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(CurrencyPatch), "EmojiPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			HarmonyMethod val = new HarmonyMethod(typeof(CurrencyPatch), "GlitchyPrefix", (Type[])null);
			string[] array = new string[3] { "ValueScreen", "TextScreen", "ExtractionPoint" };
			for (int i = 0; i < array.Length; i++)
			{
				MethodInfo methodInfo4 = AccessTools.Method(array[i] + ":GlitchyText", (Type[])null, (Type[])null);
				if (methodInfo4 != null)
				{
					harmony.Patch((MethodBase)methodInfo4, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private static void SymbolPrefix(ref string value)
		{
			if (_enabled() && value != null && value.IndexOf('$') >= 0 && (object)value != _lastGlitch)
			{
				value = MoneyRx.Replace(value, "R$$ ");
				if (value.IndexOf('K') >= 0 || value.IndexOf('k') >= 0)
				{
					value = ThousandRx.Replace(value, "$1 mil");
				}
			}
		}

		private static bool GlitchyPrefix(ref string __result)
		{
			if (!_enabled())
			{
				return true;
			}
			StringBuilder stringBuilder = new StringBuilder(12);
			int num = 0;
			while (num < 9)
			{
				if (num + "DIN".Length <= 9 && Random.Range(0, 4) == 0)
				{
					stringBuilder.Append("DIN");
					num += "DIN".Length;
				}
				else if (num + "R$".Length <= 9 && Random.Range(0, 3) == 0)
				{
					stringBuilder.Append("R$");
					num += "R$".Length;
				}
				else
				{
					stringBuilder.Append(Random.Range(0, 10));
					num++;
				}
			}
			__result = stringBuilder.ToString();
			_lastGlitch = __result;
			return false;
		}

		private static void DollarPostfix(int value, ref string __result)
		{
			if (_enabled())
			{
				__result = value.ToString("#,0", PtBr);
			}
		}

		private static void EmojiPostfix(ref string __result)
		{
			if (_enabled() && __result != null && __result.IndexOf('$') >= 0)
			{
				__result = DialogDollarRx.Replace(__result, "R$$");
			}
		}
	}
	internal static class CosmeticDumper
	{
		public static void Dump(string dictionariesDir)
		{
			try
			{
				Type type = AccessTools.TypeByName("CosmeticAsset");
				FieldInfo fieldInfo = type?.GetField("assetName");
				if (type == null || fieldInfo == null)
				{
					BceConsole.LogWarning("CosmeticAsset não encontrado — dump cancelado.");
					return;
				}
				SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
				Object[] array = Resources.FindObjectsOfTypeAll(type);
				foreach (Object obj in array)
				{
					if (fieldInfo.GetValue(obj) is string text && !string.IsNullOrWhiteSpace(text))
					{
						sortedSet.Add(text);
					}
				}
				HashSet<string> known = new HashSet<string>(StringComparer.Ordinal);
				string[] files = Directory.GetFiles(dictionariesDir, "*.txt");
				for (int i = 0; i < files.Length; i++)
				{
					string[] array2 = File.ReadAllLines(files[i]);
					foreach (string text2 in array2)
					{
						if (!text2.StartsWith("//") && !string.IsNullOrWhiteSpace(text2))
						{
							int num = text2.IndexOf('=');
							if (num > 0)
							{
								known.Add(text2.Substring(0, num).Replace("\\=", "="));
							}
						}
					}
				}
				List<string> list = sortedSet.Where((string n) => !known.Contains(n)).ToList();
				string text3 = Path.Combine(Paths.ConfigPath, "Translation", "pt", "RTLC_dump_cosmeticos.txt");
				Directory.CreateDirectory(Path.GetDirectoryName(text3));
				File.WriteAllLines(text3, new string[4]
				{
					"// Cosmetics carregados SEM tradução nos dicionários — gerado pelo botão Dump Cosmetic Names.",
					"// Preencha a parte direita e mova cada linha para o dicionário da categoria certa",
					$"// (plugins/.../Dictionaries/*.txt). Total carregado: {sortedSet.Count}; sem tradução: {list.Count}.",
					""
				}.Concat(list.Select((string n) => n + "=" + n)));
				BceConsole.LogInfo($"Dump de cosmetics: {sortedSet.Count} carregados, {list.Count} sem tradução → {text3}");
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha no dump de cosmetics: " + ex.Message);
			}
		}
	}
	internal sealed class DictionarySync
	{
		private const string ManagedPrefix = "RTLC_pt_";

		private readonly string _dictionariesDir;

		private readonly string _textDir;

		private readonly string _autoGenPath;

		private readonly PluginConfig _config;

		public DictionarySync(string dictionariesDir, string textDir, string autoGenPath, PluginConfig config)
		{
			_dictionariesDir = dictionariesDir;
			_textDir = textDir;
			_autoGenPath = autoGenPath;
			_config = config;
		}

		public void Sync(bool forceReload)
		{
			bool flag = false;
			try
			{
				Directory.CreateDirectory(_textDir);
				AutoTranslatorBridge.EnsureAutoGenFile(_autoGenPath);
				foreach (CategoryDef item in TranslationCategories.All)
				{
					string path = Path.Combine(_dictionariesDir, item.File);
					string path2 = Path.Combine(_textDir, "RTLC_pt_" + item.File);
					if (_config.IsEnabled(item.Id) && File.Exists(path))
					{
						string text = File.ReadAllText(path);
						if (!File.Exists(path2) || File.ReadAllText(path2) != text)
						{
							File.WriteAllText(path2, text);
							flag = true;
						}
					}
					else if (File.Exists(path2))
					{
						File.Delete(path2);
						flag = true;
					}
				}
				foreach (string item2 in TranslationCategories.All.Select((CategoryDef c) => c.File).Concat(new string[1] { "REPO_Cosmeticos.txt" }))
				{
					string path3 = Path.Combine(_textDir, item2);
					if (File.Exists(path3))
					{
						File.Delete(path3);
						flag = true;
					}
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha ao sincronizar dicionários: " + ex.Message);
			}
			if (flag && forceReload)
			{
				if (AutoTranslatorBridge.TryReloadTranslations(out string error))
				{
					BceConsole.LogInfo("Dicionários recarregados ao vivo.");
				}
				else
				{
					BceConsole.LogDebug("Reload ao vivo indisponível (" + error + "); o file-watcher do AutoTranslator aplica em ~1s.");
				}
			}
		}
	}
	internal static class GhostFolderCleanup
	{
		public static void Run()
		{
			try
			{
				string path = Path.Combine(Paths.BepInExRootPath, "Translation");
				if (!Directory.Exists(path))
				{
					return;
				}
				string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
				string[] array = files;
				for (int i = 0; i < array.Length; i++)
				{
					if (File.ReadAllText(array[i]).Trim().Length > 0)
					{
						return;
					}
				}
				array = files;
				for (int i = 0; i < array.Length; i++)
				{
					File.Delete(array[i]);
				}
				foreach (string item in from d in Directory.GetDirectories(path, "*", SearchOption.AllDirectories)
					orderby d.Length descending
					select d)
				{
					Directory.Delete(item);
				}
				Directory.Delete(path);
				BceConsole.LogDebug("Pasta residual BepInEx\\Translation (vazia) removida.");
			}
			catch (Exception ex)
			{
				BceConsole.LogDebug("Limpeza da pasta residual ignorada: " + ex.Message);
			}
		}
	}
	internal static class NativeLocalization
	{
		private static readonly string[] Tables = new string[3] { "HUD", "Menu", "Game" };

		private static string _tsvDir = null;

		private static Func<bool> _enabled = null;

		private static bool _installed;

		private static readonly Dictionary<(string Table, string Key), string?> _originals = new Dictionary<(string, string), string>();

		private static bool _applied;

		internal static void Install(string tsvDir, Func<bool> enabled, Harmony harmony)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			_tsvDir = tsvDir;
			_enabled = enabled;
			MethodInfo methodInfo = AccessTools.Method("LocalizationManager:LoadLocalizationOverrides", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				BceConsole.LogWarning("LocalizationManager.LoadLocalizationOverrides não encontrado — tradução da interface vanilla desativada (versão do jogo incompatível?).");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(NativeLocalization), "AfterVanillaOverrides", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
		}

		private static void AfterVanillaOverrides()
		{
			if (_enabled())
			{
				Apply();
			}
		}

		internal static void OnToggleChanged(bool enabled)
		{
			if (!_installed)
			{
				return;
			}
			try
			{
				if (LocalizationSettings.HasSettings)
				{
					if (enabled)
					{
						Apply();
					}
					else
					{
						Restore();
					}
					NotifyGame();
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha ao alternar a tradução: " + ex.Message);
			}
		}

		private static void Apply()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (_applied)
			{
				return;
			}
			int num = 0;
			try
			{
				string[] tables = Tables;
				foreach (string text in tables)
				{
					string path = Path.Combine(_tsvDir, text + ".tsv");
					if (!File.Exists(path))
					{
						continue;
					}
					StringTable table = ((LocalizedDatabase<StringTable, StringTableEntry>)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(text), LocalizationSettings.ProjectLocale);
					if ((Object)(object)table == (Object)null)
					{
						BceConsole.LogWarning("Tabela de localização '" + text + "' não encontrada — pulando.");
						continue;
					}
					string[] array = File.ReadAllLines(path);
					foreach (string text2 in array)
					{
						if (string.IsNullOrWhiteSpace(text2))
						{
							continue;
						}
						string[] array2 = text2.Split('\t');
						if (array2.Length == 2)
						{
							string text3 = array2[0];
							string text4 = array2[1].Replace("\\n", "\n");
							StringTableEntry entry = ((DetailedLocalizationTable<StringTableEntry>)(object)table).GetEntry(text3);
							if (entry != null)
							{
								_originals[(text, text3)] = entry.Value;
								entry.Value = text4;
							}
							else
							{
								_originals[(text, text3)] = null;
								((DetailedLocalizationTable<StringTableEntry>)(object)table).AddEntry(text3, text4);
							}
							num++;
						}
					}
				}
				_applied = true;
				BceConsole.LogInfo(string.Format("Interface vanilla traduzida ({0} textos nas tabelas {1}).", num, string.Join("/", Tables)));
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha ao aplicar a tradução: " + ex.Message);
			}
		}

		private static void Restore()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (!_applied)
			{
				return;
			}
			try
			{
				foreach (KeyValuePair<(string, string), string> original in _originals)
				{
					StringTable table = ((LocalizedDatabase<StringTable, StringTableEntry>)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(original.Key.Item1), LocalizationSettings.ProjectLocale);
					if ((Object)(object)table == (Object)null)
					{
						continue;
					}
					if (original.Value != null)
					{
						StringTableEntry entry = ((DetailedLocalizationTable<StringTableEntry>)(object)table).GetEntry(original.Key.Item2);
						if (entry != null)
						{
							entry.Value = original.Value;
						}
					}
					else
					{
						((DetailedLocalizationTable<StringTableEntry>)(object)table).RemoveEntry(original.Key.Item2);
					}
				}
				_originals.Clear();
				_applied = false;
				BceConsole.LogInfo("Tradução da interface vanilla desativada (textos originais restaurados).");
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("Falha ao restaurar os textos originais: " + ex.Message);
			}
		}

		private static void NotifyGame()
		{
			Type type = AccessTools.TypeByName("LocalizationManager");
			object obj = type?.GetField("instance")?.GetValue(null);
			if (obj != null)
			{
				AccessTools.Method(type, "NotifyLocalizationChanged", (Type[])null, (Type[])null)?.Invoke(obj, null);
			}
		}
	}
	internal static class BceConsole
	{
		private const string InfoPrefix = "[Info   :  REPO Tradução pt-BR] ";

		private const string WarnPrefix = "[Warning:  REPO Tradução pt-BR] ";

		private const string ErrorPrefix = "[Error  :  REPO Tradução pt-BR] ";

		private static readonly Action<string, ConsoleColor>? _writeLine;

		internal static bool IsAvailable => _writeLine != null;

		static BceConsole()
		{
			Type type = Type.GetType("BCE.console, BCE");
			if (type == null)
			{
				return;
			}
			try
			{
				MethodInfo method = type.GetMethod("WriteLine", new Type[2]
				{
					typeof(string),
					typeof(ConsoleColor)
				});
				if (method != null)
				{
					_writeLine = (Action<string, ConsoleColor>)Delegate.CreateDelegate(typeof(Action<string, ConsoleColor>), null, method);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("BceConsole: falha ao criar delegate (" + ex.Message + "). Saída BCE desativada — usando o logger do BepInEx."));
				}
			}
		}

		internal static void LogInfo(string msg)
		{
			LogInfo(msg, ConsoleColor.Green);
		}

		internal static void LogInfo(string msg, ConsoleColor color)
		{
			if (IsAvailable)
			{
				_writeLine("[Info   :  REPO Tradução pt-BR] " + msg, color);
				return;
			}
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)msg);
			}
		}

		internal static void LogWarning(string msg)
		{
			if (IsAvailable)
			{
				_writeLine("[Warning:  REPO Tradução pt-BR] " + msg, ConsoleColor.Yellow);
				return;
			}
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)msg);
			}
		}

		internal static void LogError(string msg)
		{
			if (IsAvailable)
			{
				_writeLine("[Error  :  REPO Tradução pt-BR] " + msg, ConsoleColor.Red);
				return;
			}
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogError((object)msg);
			}
		}

		internal static void LogDebug(string msg)
		{
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogDebug((object)msg);
			}
		}
	}
	internal static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "xuaun.repo.traducao.ptbr";

		public const string PLUGIN_NAME = "REPO Tradução pt-BR";

		public const string PLUGIN_VERSION = "1.0.4";
	}
	[BepInPlugin("xuaun.repo.traducao.ptbr", "REPO Tradução pt-BR", "1.0.4")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource? Log;

		private Harmony _harmony;

		private PluginConfig _config;

		private DictionarySync _dictionaries;

		private AutoTranslatorConfigurator _autoTranslator;

		private string _dictionariesDir;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			_dictionariesDir = Path.Combine(directoryName, "Dictionaries");
			string text = Path.Combine(Paths.ConfigPath, "Translation", "pt", "Text");
			string text2 = Path.Combine(text, "_AutoGeneratedTranslations.txt");
			string path = Path.Combine(Paths.ConfigPath, "AutoTranslatorConfig.ini");
			_config = new PluginConfig(((BaseUnityPlugin)this).Config);
			_dictionaries = new DictionarySync(_dictionariesDir, text, text2, _config);
			_autoTranslator = new AutoTranslatorConfigurator(new IniFile(path), _config, text2);
			WireConfigEvents();
			NativeLocalization.Install(Path.Combine(directoryName, "Localizations"), () => _config.IsEnabled(TranslationCategory.Game), _harmony);
			CurrencyPatch.Install(() => _config.IsEnabled(TranslationCategory.Game), _harmony);
			_dictionaries.Sync(forceReload: false);
			_autoTranslator.ApplyConfig();
			AutoTranslatorBridge.EnsureAutoGenFile(text2);
			_autoTranslator.ApplyEndpoint(announce: false);
			GhostFolderCleanup.Run();
			BceConsole.LogInfo("Tradução pt-BR pronta (dicionários sincronizados).");
		}

		private void Start()
		{
			QotlCompat.Install(_harmony);
		}

		private void WireConfigEvents()
		{
			foreach (TranslationCategory category in _config.Categories)
			{
				_config.Toggle(category).SettingChanged += delegate
				{
					_dictionaries.Sync(forceReload: true);
				};
			}
			_config.Toggle(TranslationCategory.Game).SettingChanged += delegate
			{
				NativeLocalization.OnToggleChanged(_config.IsEnabled(TranslationCategory.Game));
			};
			_config.AutoTranslateRest.SettingChanged += delegate
			{
				_autoTranslator.ApplyEndpoint(announce: true);
			};
			_config.DumpCosmetics.SettingChanged += delegate
			{
				if (_config.DumpCosmetics.Value)
				{
					CosmeticDumper.Dump(_dictionariesDir);
					_config.DumpCosmetics.Value = false;
				}
			};
		}
	}
}