Decompiled source of REPO PTBR Contextual v1.0.1

BepInEx/plugins/RepoPTBRContextual/RepoPTBRContextual.dll

Decompiled 12 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
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: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 RepoPTBRContextual
{
	[BepInPlugin("local.repo.ptbrcontextual", "R.E.P.O. PT-BR Contextual", "1.0.1")]
	public sealed class RepoPTBRContextualPlugin : BaseUnityPlugin
	{
		private Harmony harmony;

		public void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			PTBRTranslator.Load(Paths.PluginPath);
			harmony = new Harmony("local.repo.ptbrcontextual");
			harmony.PatchAll(typeof(RepoPTBRContextualPlugin).Assembly);
			OverrideTools.ApplyOverridesToActiveLocales();
			((MonoBehaviour)this).StartCoroutine(ApplyTranslationWhenReady());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PT-BR contextual translation loaded.");
		}

		private IEnumerator ApplyTranslationWhenReady()
		{
			for (int attempt = 0; attempt < 12; attempt++)
			{
				yield return (object)new WaitForSecondsRealtime(1f);
				OverrideTools.ApplyOverridesToActiveLocales();
			}
		}

		public void OnDestroy()
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class PTBRTranslator
	{
		private static readonly Dictionary<string, string> Exact = new Dictionary<string, string>();

		private static readonly HashSet<string> Missing = new HashSet<string>();

		private static readonly object MissingLock = new object();

		private static string missingPath;

		private static string localizationPath;

		private static DateTime nextOverrideApplyAt = DateTime.MinValue;

		internal static string LocalizationPath => localizationPath;

		public static void Load(string pluginPath)
		{
			string path = Path.Combine(pluginPath, "RepoPTBRContextual");
			string path2 = Path.Combine(path, "runtime.tsv");
			localizationPath = Path.Combine(path, "Localizations");
			missingPath = Path.Combine(Paths.ConfigPath, "RepoPTBRContextual.missing.tsv");
			Exact.Clear();
			if (!File.Exists(path2))
			{
				LoadOfficialLocalizationPairs();
				return;
			}
			string[] array = File.ReadAllLines(path2, Encoding.UTF8);
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#", StringComparison.Ordinal))
				{
					string[] array2 = text.Split(new char[1] { '\t' }, 2);
					if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[0]))
					{
						Exact[array2[0].Replace("\\n", "\n")] = array2[1].Replace("\\n", "\n");
					}
				}
			}
			LoadOfficialLocalizationPairs();
		}

		public static bool ShouldApplyOverridesNow()
		{
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow < nextOverrideApplyAt)
			{
				return false;
			}
			nextOverrideApplyAt = utcNow.AddSeconds(1.0);
			return true;
		}

		public static string Translate(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return value;
			}
			string translated;
			if (value.StartsWith("(PT-BR) ", StringComparison.OrdinalIgnoreCase))
			{
				string text = value.Substring("(PT-BR) ".Length);
				if (TryTranslate(text, out translated))
				{
					return translated;
				}
				return text;
			}
			if (TryTranslate(value, out translated))
			{
				return translated;
			}
			LogMissing(value);
			return value;
		}

		private static bool TryTranslate(string value, out string translated)
		{
			if (Exact.TryGetValue(value, out translated))
			{
				return true;
			}
			string text = value.Trim();
			if (text.Length != value.Length && Exact.TryGetValue(text, out translated))
			{
				int length = value.Length - value.TrimStart().Length;
				int num = value.Length - value.TrimEnd().Length;
				translated = value.Substring(0, length) + translated + value.Substring(value.Length - num);
				return true;
			}
			return false;
		}

		private static void LoadOfficialLocalizationPairs()
		{
			try
			{
				string path = Path.Combine(Path.Combine(Application.streamingAssetsPath, "Localizations"), "Default");
				string[] array = new string[3] { "HUD.tsv", "Menu.tsv", "Game.tsv" };
				for (int i = 0; i < array.Length; i++)
				{
					Dictionary<string, string> dictionary = ReadTsv(Path.Combine(path, array[i]));
					Dictionary<string, string> dictionary2 = ReadTsv(Path.Combine(localizationPath, array[i]));
					foreach (KeyValuePair<string, string> item in dictionary)
					{
						if (dictionary2.TryGetValue(item.Key, out var value) && !Exact.ContainsKey(item.Value))
						{
							Exact[item.Value] = value;
							string key = item.Value.ToUpperInvariant();
							if (!Exact.ContainsKey(key))
							{
								Exact[key] = value;
							}
						}
					}
				}
			}
			catch
			{
			}
		}

		private static Dictionary<string, string> ReadTsv(string path)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			if (!File.Exists(path))
			{
				return dictionary;
			}
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					string[] array2 = text.Split(new char[1] { '\t' }, 2);
					if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[0]))
					{
						dictionary[array2[0]] = array2[1].Replace("\\n", "\n");
					}
				}
			}
			return dictionary;
		}

		private static void LogMissing(string value)
		{
			if (!LooksUserFacingEnglish(value))
			{
				return;
			}
			lock (MissingLock)
			{
				if (Missing.Add(value))
				{
					File.AppendAllText(missingPath, value.Replace("\r", "\\r").Replace("\n", "\\n") + "\t\r\n", Encoding.UTF8);
				}
			}
		}

		private static bool LooksUserFacingEnglish(string value)
		{
			string text = value.Trim();
			if (text.Length < 2 || text.Length > 180)
			{
				return false;
			}
			if (text.Contains("/") || text.Contains("\\") || text.Contains(".dll") || text.Contains(".asset"))
			{
				return false;
			}
			bool flag = false;
			bool flag2 = false;
			foreach (char c in text)
			{
				if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
				{
					flag = true;
				}
				else if (char.IsWhiteSpace(c) || ",.!?:;()[]{}'\"-".IndexOf(c) >= 0)
				{
					flag2 = true;
				}
			}
			if (!flag)
			{
				return false;
			}
			if (!flag2 && text.ToUpperInvariant() == text && text.Contains("."))
			{
				return false;
			}
			return true;
		}
	}
	internal static class LocaleTools
	{
		public static void ForcePtBr(object manager)
		{
			try
			{
				if (manager == null)
				{
					return;
				}
				Type type = manager.GetType();
				FieldInfo fieldInfo = AccessTools.Field(type, "AvailableLocales");
				MethodInfo methodInfo = AccessTools.Method(type, "SwitchLocale", (Type[])null, (Type[])null);
				IEnumerable enumerable = ((fieldInfo == null) ? null : (fieldInfo.GetValue(manager) as IEnumerable));
				if (enumerable == null || methodInfo == null)
				{
					return;
				}
				foreach (object item in enumerable)
				{
					if (string.Equals(GetLocaleCode(item), "pt-BR", StringComparison.OrdinalIgnoreCase))
					{
						methodInfo.Invoke(manager, new object[2] { item, true });
						break;
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[RepoPTBRContextual] Failed to apply localization overrides: " + ex));
			}
		}

		private static string GetLocaleCode(object localeAsset)
		{
			if (localeAsset == null)
			{
				return null;
			}
			FieldInfo fieldInfo = AccessTools.Field(localeAsset.GetType(), "LocaleReference");
			object obj = ((fieldInfo == null) ? null : fieldInfo.GetValue(localeAsset));
			if (obj == null)
			{
				return null;
			}
			object value = AccessTools.Property(obj.GetType(), "Identifier").GetValue(obj, null);
			if (value != null)
			{
				return (string)AccessTools.Property(value.GetType(), "Code").GetValue(value, null);
			}
			return null;
		}
	}
	internal static class OverrideTools
	{
		public static void ApplyOverridesToActiveLocales()
		{
			try
			{
				string localizationPath = PTBRTranslator.LocalizationPath;
				if (string.IsNullOrEmpty(localizationPath) || !Directory.Exists(localizationPath))
				{
					Debug.LogWarning((object)"[RepoPTBRContextual] Bundled localization tables were not found.");
					return;
				}
				string[] array = new string[3] { "HUD", "Menu", "Game" };
				List<Locale> targetLocales = GetTargetLocales();
				int num = 0;
				foreach (string text in array)
				{
					string path = Path.Combine(localizationPath, text + ".tsv");
					if (File.Exists(path))
					{
						List<KeyValuePair<string, string>> entries = ReadEntries(path);
						for (int j = 0; j < targetLocales.Count; j++)
						{
							num += ApplyTable(text, targetLocales[j], entries);
						}
					}
				}
				Debug.Log((object)("[RepoPTBRContextual] Applied " + num + " localization override entries to " + targetLocales.Count + " locale(s)."));
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[RepoPTBRContextual] Localization tables are not ready yet: " + ex.Message));
			}
		}

		private static List<Locale> GetTargetLocales()
		{
			List<Locale> list = new List<Locale>();
			AddLocale(list, LocalizationSettings.SelectedLocale);
			AddLocale(list, LocalizationSettings.ProjectLocale);
			try
			{
				if (LocalizationSettings.AvailableLocales != null)
				{
					foreach (Locale locale in LocalizationSettings.AvailableLocales.Locales)
					{
						if (IsPtBr(locale))
						{
							AddLocale(list, locale);
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static void AddLocale(List<Locale> locales, Locale locale)
		{
			if ((Object)(object)locale == (Object)null)
			{
				return;
			}
			for (int i = 0; i < locales.Count; i++)
			{
				if ((Object)(object)locales[i] == (Object)(object)locale)
				{
					return;
				}
			}
			locales.Add(locale);
		}

		private static bool IsPtBr(Locale locale)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)locale == (Object)null)
			{
				return false;
			}
			LocaleIdentifier identifier = locale.Identifier;
			return string.Equals(((LocaleIdentifier)(ref identifier)).Code, "pt-BR", StringComparison.OrdinalIgnoreCase);
		}

		private static List<KeyValuePair<string, string>> ReadEntries(string path)
		{
			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					string[] array2 = text.Split(new char[1] { '\t' }, 2);
					if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[0]))
					{
						list.Add(new KeyValuePair<string, string>(array2[0], array2[1].Replace("\\n", "\n")));
					}
				}
			}
			return list;
		}

		private static int ApplyTable(string tableName, Locale locale, List<KeyValuePair<string, string>> entries)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			StringTable table = ((LocalizedDatabase<StringTable, StringTableEntry>)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(tableName), locale);
			if ((Object)(object)table == (Object)null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < entries.Count; i++)
			{
				KeyValuePair<string, string> keyValuePair = entries[i];
				StringTableEntry entry = ((DetailedLocalizationTable<StringTableEntry>)(object)table).GetEntry(keyValuePair.Key);
				if (entry != null)
				{
					entry.Value = keyValuePair.Value;
				}
				else
				{
					((DetailedLocalizationTable<StringTableEntry>)(object)table).AddEntry(keyValuePair.Key, keyValuePair.Value);
				}
				num++;
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(LocalizationManager), "Start")]
	internal static class LocalizationManagerStartPatch
	{
		private static void Postfix(LocalizationManager __instance)
		{
			OverrideTools.ApplyOverridesToActiveLocales();
			MethodInfo methodInfo = AccessTools.Method(((object)__instance).GetType(), "NotifyLocalizationChanged", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(__instance, null);
			}
		}
	}
	[HarmonyPatch(typeof(LocalizationChangedEvent), "OnLocaleChange")]
	internal static class LocalizationChangedEventPatch
	{
		private static void Prefix()
		{
			if (PTBRTranslator.ShouldApplyOverridesNow())
			{
				OverrideTools.ApplyOverridesToActiveLocales();
			}
		}
	}
	[HarmonyPatch(typeof(TMP_Text), "set_text")]
	internal static class TMPTextPatch
	{
		private static void Prefix(ref string value)
		{
			value = PTBRTranslator.Translate(value);
		}
	}
	[HarmonyPatch]
	internal static class TMPSetTextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return from method in typeof(TMP_Text).GetMethods(BindingFlags.Instance | BindingFlags.Public)
				where method.Name == "SetText" && method.GetParameters().Length != 0 && method.GetParameters()[0].ParameterType == typeof(string)
				select method;
		}

		private static void Prefix(ref string sourceText)
		{
			sourceText = PTBRTranslator.Translate(sourceText);
		}
	}
	[HarmonyPatch(typeof(Text), "set_text")]
	internal static class UITextPatch
	{
		private static void Prefix(ref string value)
		{
			value = PTBRTranslator.Translate(value);
		}
	}
}