Decompiled source of ScheduleI PTBR v1.5.7

Plugins/ScheduleI_PTBR.dll

Decompiled 15 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.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using ScheduleI_PTBR;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Plugin), "ScheduleI_PTBR", "1.1.0", "LordValdomod", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ScheduleI_PTBR")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("ScheduleI_PTBR")]
[assembly: AssemblyTitle("ScheduleI_PTBR")]
[assembly: AssemblyVersion("1.1.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 ScheduleI_PTBR
{
	internal static class Dumper
	{
		private class Target
		{
			public Type Type;

			public object Il2CppType;

			public PropertyInfo Text;
		}

		private const int IntervalMs = 1000;

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static readonly List<Target> Targets = new List<Target>();

		private static HashSet<string> seen;

		private static HashSet<string> ignore;

		private static StreamWriter writer;

		private static MethodInfo findObjects;

		private static long nextScan;

		private static bool active;

		private static int errors;

		public static void Init(string path, HashSet<string> translatedValues)
		{
			ignore = translatedValues;
			seen = new HashSet<string>(StringComparer.Ordinal);
			if (File.Exists(path))
			{
				foreach (string item in File.ReadLines(path))
				{
					int num = TextFormat.FindSeparator(item);
					seen.Add((num >= 0) ? item.Substring(0, num) : item);
				}
			}
			writer = new StreamWriter(path, append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))
			{
				AutoFlush = true
			};
			Type type = FindType("UnityEngine.Object");
			string[] array = new string[2] { "Il2CppTMPro.TMP_Text", "UnityEngine.UI.Text" };
			for (int i = 0; i < array.Length; i++)
			{
				Type type2 = FindType(array[i]);
				PropertyInfo propertyInfo = type2?.GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
				if (type2 == null || propertyInfo == null)
				{
					continue;
				}
				object obj = ToIl2CppType(type2);
				if (obj != null)
				{
					if (findObjects == null && type != null)
					{
						findObjects = type.GetMethod("FindObjectsOfType", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { obj.GetType() }, null);
					}
					Targets.Add(new Target
					{
						Type = type2,
						Il2CppType = obj,
						Text = propertyInfo
					});
				}
			}
			active = findObjects != null && Targets.Count > 0;
			Plugin.Log.Msg(active ? $"Modo dump ativo ({Targets.Count} tipos de texto, varredura a cada {1000} ms). Arquivo: {path}" : "Modo dump não iniciado: tipos de texto do jogo não encontrados.");
		}

		private static object ToIl2CppType(Type t)
		{
			MethodInfo methodInfo = (from m in typeof(Il2CppObjectBase).Assembly.GetType("Il2CppInterop.Runtime.Il2CppType")?.GetMethods(BindingFlags.Static | BindingFlags.Public)
				where m.Name == "From" && m.GetParameters().FirstOrDefault()?.ParameterType == typeof(Type)
				orderby m.GetParameters().Length
				select m).FirstOrDefault();
			if (methodInfo == null)
			{
				return null;
			}
			object[] parameters = methodInfo.GetParameters().Select((ParameterInfo p, int i) => (i != 0) ? ((!(p.ParameterType == typeof(bool))) ? p.DefaultValue : ((object)false)) : t).ToArray();
			return methodInfo.Invoke(null, parameters);
		}

		public static void Tick()
		{
			if (!active || Clock.ElapsedMilliseconds < nextScan)
			{
				return;
			}
			nextScan = Clock.ElapsedMilliseconds + 1000;
			foreach (Target target in Targets)
			{
				try
				{
					if (!(findObjects.Invoke(null, new object[1] { target.Il2CppType }) is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item in enumerable)
					{
						Il2CppObjectBase val = (Il2CppObjectBase)((item is Il2CppObjectBase) ? item : null);
						if (val != null && !(val.Pointer == IntPtr.Zero))
						{
							object obj = Activator.CreateInstance(target.Type, val.Pointer);
							Record(target.Text.GetValue(obj) as string);
						}
					}
				}
				catch (Exception ex)
				{
					if (++errors <= 3)
					{
						Plugin.Log.Warning("Falha na varredura de " + target.Type.Name + ": " + (ex.InnerException?.Message ?? ex.Message));
					}
					if (errors >= 20)
					{
						active = false;
						Plugin.Log.Error("Modo dump desativado após falhas repetidas.");
					}
				}
			}
		}

		public static void Close()
		{
			try
			{
				writer?.Dispose();
			}
			catch
			{
			}
		}

		private static Type FindType(string fullName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType(fullName, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			string[] array = new string[3] { "UnityEngine.CoreModule", "Unity.TextMeshPro", "UnityEngine.UI" };
			foreach (string assemblyString in array)
			{
				try
				{
					Type type2 = Assembly.Load(assemblyString).GetType(fullName, throwOnError: false);
					if (type2 != null)
					{
						return type2;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static void Record(string text)
		{
			if (!string.IsNullOrWhiteSpace(text) && text.Length <= 4000 && text.Any(char.IsLetter))
			{
				string text2 = TextFormat.Escape(text);
				if (!ignore.Contains(text2) && seen.Add(text2))
				{
					writer.WriteLine(text2 + "=");
				}
			}
		}
	}
	internal static class FullScan
	{
		private const int MaxDepth = 4;

		private const int DelayMs = 15000;

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static readonly Dictionary<string, Type> TypeIndex = new Dictionary<string, Type>(StringComparer.Ordinal);

		private static readonly Dictionary<Type, List<PropertyInfo>> PropCache = new Dictionary<Type, List<PropertyInfo>>();

		private static readonly Dictionary<string, SortedSet<string>> Found = new Dictionary<string, SortedSet<string>>(StringComparer.Ordinal);

		private static HashSet<string> ignore;

		private static string outPath;

		private static long scanAt = -1L;

		private static int scans;

		private static MethodInfo findAll;

		private static Type unityObject;

		public static void Init(string path, HashSet<string> translatedValues)
		{
			outPath = path;
			ignore = translatedValues;
			unityObject = FindType("UnityEngine.Object");
			findAll = FindType("UnityEngine.Resources")?.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfTypeAll" && !m.IsGenericMethod && m.GetParameters().Length == 1);
			if (findAll == null || unityObject == null)
			{
				Plugin.Log.Error("Varredura completa indisponível: API da Unity não encontrada.");
				return;
			}
			if (File.Exists(path))
			{
				string section = "";
				foreach (string item in File.ReadLines(path))
				{
					if (item.StartsWith("//"))
					{
						section = item.Substring(2);
						continue;
					}
					int num = TextFormat.FindSeparator(item);
					if (num > 0)
					{
						Add(section, item.Substring(0, num), escaped: true);
					}
				}
			}
			((MelonEventBase<LemonAction<int, string>>)(object)MelonEvents.OnSceneWasInitialized).Subscribe((LemonAction<int, string>)delegate(int i, string name)
			{
				Schedule(name);
			}, 0, false);
			Plugin.Log.Msg("Varredura completa ativa. Roda 15 s depois de cada cena carregar. Arquivo: " + path);
		}

		private static void Schedule(string scene)
		{
			scanAt = Clock.ElapsedMilliseconds + 15000;
			Plugin.Log.Msg("Cena '" + scene + "' carregada, varredura completa em 15 s...");
		}

		public static void Tick()
		{
			if (scanAt >= 0 && Clock.ElapsedMilliseconds >= scanAt)
			{
				scanAt = -1L;
				Stopwatch stopwatch = Stopwatch.StartNew();
				int num = Found.Values.Sum((SortedSet<string> s) => s.Count);
				try
				{
					ScanTexts();
				}
				catch (Exception ex)
				{
					Plugin.Log.Warning("Falha nos textos de UI: " + ex.Message);
				}
				try
				{
					ScanObjects("UnityEngine.ScriptableObject");
				}
				catch (Exception ex2)
				{
					Plugin.Log.Warning("Falha nos dados do jogo: " + ex2.Message);
				}
				try
				{
					ScanObjects("UnityEngine.MonoBehaviour");
				}
				catch (Exception ex3)
				{
					Plugin.Log.Warning("Falha nos componentes: " + ex3.Message);
				}
				Save();
				scans++;
				int num2 = Found.Values.Sum((SortedSet<string> s) => s.Count);
				Plugin.Log.Msg($"Varredura #{scans} concluída em {stopwatch.ElapsedMilliseconds} ms: {num2 - num} textos novos, {num2} no total.");
			}
		}

		private static IEnumerable<Il2CppObjectBase> FindAll(string typeName)
		{
			Type type = FindType(typeName);
			if (type == null)
			{
				yield break;
			}
			object obj = ToIl2CppType(type);
			if (obj == null || !(findAll.Invoke(null, new object[1] { obj }) is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				Il2CppObjectBase val = (Il2CppObjectBase)((item is Il2CppObjectBase) ? item : null);
				if (val != null && val.Pointer != IntPtr.Zero)
				{
					yield return val;
				}
			}
		}

		private static void ScanTexts()
		{
			string[] array = new string[2] { "Il2CppTMPro.TMP_Text", "UnityEngine.UI.Text" };
			foreach (string text in array)
			{
				Type type = FindType(text);
				PropertyInfo propertyInfo = type?.GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
				if (propertyInfo == null)
				{
					continue;
				}
				foreach (Il2CppObjectBase item in FindAll(text))
				{
					try
					{
						Add("UI", propertyInfo.GetValue(Activator.CreateInstance(type, item.Pointer)) as string);
					}
					catch
					{
					}
				}
			}
		}

		private static void ScanObjects(string baseType)
		{
			foreach (Il2CppObjectBase item in FindAll(baseType))
			{
				try
				{
					Type type = ManagedTypeOf(item.Pointer);
					if (!(type == null) && type.FullName.StartsWith("Il2CppScheduleOne", StringComparison.Ordinal))
					{
						Walk(Activator.CreateInstance(type, item.Pointer), type, type.Name, 0, new HashSet<IntPtr> { item.Pointer });
					}
				}
				catch
				{
				}
			}
		}

		private static void Walk(object obj, Type t, string section, int depth, HashSet<IntPtr> seen)
		{
			foreach (PropertyInfo item in FieldProps(t))
			{
				object value;
				try
				{
					value = item.GetValue(obj);
				}
				catch
				{
					continue;
				}
				if (value == null)
				{
					continue;
				}
				if (value is string text)
				{
					Add(section, text);
				}
				else
				{
					if (depth >= 4)
					{
						continue;
					}
					if (value is IEnumerable enumerable && !(value is Il2CppObjectBase))
					{
						foreach (object item2 in enumerable)
						{
							Visit(item2, section, depth, seen);
						}
					}
					else
					{
						Visit(value, section, depth, seen);
					}
				}
			}
		}

		private static void Visit(object v, string section, int depth, HashSet<IntPtr> seen)
		{
			if (v is string text)
			{
				Add(section, text);
				return;
			}
			Il2CppObjectBase val = (Il2CppObjectBase)((v is Il2CppObjectBase) ? v : null);
			if (val == null || val.Pointer == IntPtr.Zero || !seen.Add(val.Pointer))
			{
				return;
			}
			Type type = v.GetType();
			if (unityObject.IsAssignableFrom(type))
			{
				return;
			}
			if (type.Name.StartsWith("List`1") || type.Name.StartsWith("Il2CppStringArray") || type.Name.StartsWith("Il2CppReferenceArray"))
			{
				foreach (object item in Items(v))
				{
					Visit(item, section, depth + 1, seen);
				}
				return;
			}
			if (type.FullName != null && type.FullName.StartsWith("Il2CppScheduleOne", StringComparison.Ordinal))
			{
				Walk(v, type, section, depth + 1, seen);
			}
		}

		private static IEnumerable<object> Items(object list)
		{
			Type type = list.GetType();
			PropertyInfo propertyInfo = type.GetProperty("Count") ?? type.GetProperty("Length");
			MethodInfo getter = type.GetMethod("get_Item", new Type[1] { typeof(int) });
			if (propertyInfo == null || getter == null)
			{
				yield break;
			}
			int n;
			try
			{
				n = Convert.ToInt32(propertyInfo.GetValue(list));
			}
			catch
			{
				yield break;
			}
			for (int i = 0; i < Math.Min(n, 5000); i++)
			{
				object obj2 = null;
				try
				{
					obj2 = getter.Invoke(list, new object[1] { i });
				}
				catch
				{
				}
				if (obj2 != null)
				{
					yield return obj2;
				}
			}
		}

		private static List<PropertyInfo> FieldProps(Type t)
		{
			if (PropCache.TryGetValue(t, out var value))
			{
				return value;
			}
			value = new List<PropertyInfo>();
			Type type = t;
			while (type != null && type != typeof(object) && type.FullName != null && type.FullName.StartsWith("Il2CppScheduleOne", StringComparison.Ordinal))
			{
				PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.CanRead && !(type.GetField("NativeFieldInfoPtr_" + propertyInfo.Name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) == null))
					{
						value.Add(propertyInfo);
					}
				}
				type = type.BaseType;
			}
			PropCache[t] = value;
			return value;
		}

		private static Type ManagedTypeOf(IntPtr obj)
		{
			IntPtr intPtr = IL2CPP.il2cpp_object_get_class(obj);
			if (intPtr == IntPtr.Zero)
			{
				return null;
			}
			string text = Marshal.PtrToStringAnsi(IL2CPP.il2cpp_class_get_namespace(intPtr)) ?? "";
			string text2 = Marshal.PtrToStringAnsi(IL2CPP.il2cpp_class_get_name(intPtr)) ?? "";
			string text3 = ((text.Length > 0) ? (text + "." + text2) : text2);
			if (TypeIndex.TryGetValue(text3, out var value))
			{
				return value;
			}
			value = FindType("Il2Cpp" + text3) ?? FindType(text3);
			TypeIndex[text3] = value;
			return value;
		}

		private static void Add(string section, string text, bool escaped = false)
		{
			if (text == null)
			{
				return;
			}
			string item = (escaped ? text : TextFormat.Escape(text));
			if (escaped || (!string.IsNullOrWhiteSpace(text) && text.Length <= 4000 && text.Any(char.IsLetter) && !ignore.Contains(item)))
			{
				if (!Found.TryGetValue(section, out var value))
				{
					value = (Found[section] = new SortedSet<string>(StringComparer.Ordinal));
				}
				value.Add(item);
			}
		}

		private static void Save()
		{
			StringBuilder stringBuilder = new StringBuilder();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, SortedSet<string>> item in Found.OrderBy<KeyValuePair<string, SortedSet<string>>, string>((KeyValuePair<string, SortedSet<string>> k) => (!(k.Key == "UI")) ? k.Key : "", StringComparer.Ordinal))
			{
				List<string> list = item.Value.Where(hashSet.Add).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				stringBuilder.Append("//").Append(item.Key).Append('\n');
				foreach (string item2 in list)
				{
					stringBuilder.Append(item2).Append("=\n");
				}
			}
			File.WriteAllText(outPath, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static object ToIl2CppType(Type t)
		{
			MethodInfo methodInfo = (from m in typeof(Il2CppObjectBase).Assembly.GetType("Il2CppInterop.Runtime.Il2CppType")?.GetMethods(BindingFlags.Static | BindingFlags.Public)
				where m.Name == "From" && m.GetParameters().FirstOrDefault()?.ParameterType == typeof(Type)
				orderby m.GetParameters().Length
				select m).FirstOrDefault();
			if (methodInfo == null)
			{
				return null;
			}
			object[] parameters = methodInfo.GetParameters().Select((ParameterInfo p, int i) => (i != 0) ? ((!(p.ParameterType == typeof(bool))) ? p.DefaultValue : ((object)false)) : t).ToArray();
			return methodInfo.Invoke(null, parameters);
		}

		private static Type FindType(string fullName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = null;
				try
				{
					type = assembly.GetType(fullName, throwOnError: false);
				}
				catch
				{
				}
				if (type != null)
				{
					return type;
				}
			}
			string[] array = new string[4] { "UnityEngine.CoreModule", "Unity.TextMeshPro", "UnityEngine.UI", "Assembly-CSharp" };
			foreach (string assemblyString in array)
			{
				try
				{
					Type type2 = Assembly.Load(assemblyString).GetType(fullName, throwOnError: false);
					if (type2 != null)
					{
						return type2;
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
	public class Plugin : MelonPlugin
	{
		internal const string Id = "ScheduleI_PTBR";

		private const string StampName = ".scheduleI_ptbr";

		private const string LangDir = "Translation/pt/Text";

		internal static Instance Log;

		private MelonPreferences_Entry<bool> dumpMode;

		private MelonPreferences_Entry<bool> fullScan;

		private string atRoot;

		private List<string> deployedFiles = new List<string>();

		public override void OnPreInitialization()
		{
			Log = ((MelonBase)this).LoggerInstance;
			MelonPreferences_Category val = MelonPreferences.CreateCategory("ScheduleI_PTBR", "Tradução PT-BR");
			dumpMode = val.CreateEntry<bool>("DumpMode", false, "Modo dump", "Registra os textos exibidos em UserData/ScheduleI_PTBR_dump.txt", false, false, (ValueValidator)null, (string)null);
			fullScan = val.CreateEntry<bool>("FullScan", false, "Varredura completa", "Lê todos os textos e dados carregados na memória e grava em UserData/ScheduleI_PTBR_fullscan.txt", false, false, (ValueValidator)null, (string)null);
			atRoot = Path.Combine(MelonEnvironment.GameRootDirectory, "AutoTranslator");
			try
			{
				Deploy();
			}
			catch (Exception ex)
			{
				Log.Error("Falha ao instalar as traduções: " + ex);
			}
			try
			{
				PatchConfig();
			}
			catch (Exception ex2)
			{
				Log.Error("Falha ao ajustar o Config.ini: " + ex2);
			}
		}

		public override void OnApplicationStarted()
		{
			if (fullScan.Value)
			{
				try
				{
					FullScan.Init(Path.Combine(MelonEnvironment.UserDataDirectory, "ScheduleI_PTBR_fullscan.txt"), LoadTranslatedValues());
				}
				catch (Exception ex)
				{
					Log.Error("Falha ao iniciar a varredura completa: " + ex);
				}
			}
			if (!dumpMode.Value)
			{
				return;
			}
			try
			{
				Dumper.Init(Path.Combine(MelonEnvironment.UserDataDirectory, "ScheduleI_PTBR_dump.txt"), LoadTranslatedValues());
			}
			catch (Exception ex2)
			{
				Log.Error("Falha ao iniciar o modo dump: " + ex2);
			}
		}

		public override void OnUpdate()
		{
			if (dumpMode != null && dumpMode.Value)
			{
				Dumper.Tick();
			}
			if (fullScan != null && fullScan.Value)
			{
				FullScan.Tick();
			}
		}

		public override void OnApplicationQuit()
		{
			Dumper.Close();
		}

		private string FindSource()
		{
			string text = Path.Combine(MelonEnvironment.UserDataDirectory, "ScheduleI_PTBR");
			if (Directory.Exists(Path.Combine(text, "Translation/pt/Text")))
			{
				return text;
			}
			foreach (string item in Directory.EnumerateDirectories(MelonEnvironment.MelonBaseDirectory, "ScheduleI_PTBR", SearchOption.AllDirectories))
			{
				if (Directory.Exists(Path.Combine(item, "Translation/pt/Text")))
				{
					return item;
				}
			}
			return null;
		}

		private void Deploy()
		{
			string text = FindSource();
			if (text == null)
			{
				Log.Error("Pasta de traduções não encontrada. Reinstale o pacote.");
				return;
			}
			string path = Path.Combine(text, "Translation/pt/Text");
			string dstText = Path.Combine(atRoot, "Translation/pt/Text");
			Directory.CreateDirectory(dstText);
			List<string> list = Directory.GetFiles(path, "*.txt").OrderBy<string, string>((string f) => Path.GetFileName(f), StringComparer.Ordinal).ToList();
			string text2 = HashFiles(list);
			string path2 = Path.Combine(atRoot, ".scheduleI_ptbr");
			Dictionary<string, string> dictionary = ReadStamp(path2);
			List<string> names = list.Select(Path.GetFileName).ToList();
			if (!dictionary.TryGetValue("hash", out var value) || !(value == text2) || !names.All((string n) => File.Exists(Path.Combine(dstText, n))))
			{
				if (dictionary.TryGetValue("files", out var value2))
				{
					foreach (string item in from n in value2.Split('|')
						where n.Length > 0 && !names.Contains(n)
						select n)
					{
						string path3 = Path.Combine(dstText, item);
						if (File.Exists(path3))
						{
							File.Delete(path3);
						}
					}
				}
				foreach (string item2 in list)
				{
					File.Copy(item2, Path.Combine(dstText, Path.GetFileName(item2)), overwrite: true);
				}
				File.WriteAllText(path2, "hash=" + text2 + "\nfiles=" + string.Join("|", names) + "\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Log.Msg($"Traduções instaladas ({list.Count} arquivos).");
			}
			deployedFiles = names.Select((string n) => Path.Combine(dstText, n)).ToList();
			List<string> list2 = (from n in Directory.GetFiles(dstText, "*.txt").Select(Path.GetFileName)
				where !names.Contains(n) && !n.StartsWith("_")
				select n).ToList();
			if (list2.Count > 0)
			{
				Log.Warning("Arquivos de outras traduções em AutoTranslator/Translation/pt/Text podem conflitar: " + string.Join(", ", list2));
			}
		}

		private void PatchConfig()
		{
			Directory.CreateDirectory(atRoot);
			string text = Path.Combine(atRoot, "Config.ini");
			List<string> list = (File.Exists(text) ? File.ReadAllLines(text).ToList() : new List<string>());
			string text2 = string.Join("\n", list);
			string text3 = text + ".ptbr-bak";
			if (File.Exists(text) && !File.Exists(text3))
			{
				File.Copy(text, text3);
			}
			SetIni(list, "Service", "Endpoint", "");
			SetIni(list, "Service", "FallbackEndpoint", "");
			SetIni(list, "General", "Language", "pt");
			SetIni(list, "General", "FromLanguage", "en");
			if (string.Join("\n", list) != text2)
			{
				File.WriteAllLines(text, list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Log.Msg("Config.ini do AutoTranslator ajustado para PT-BR.");
			}
		}

		private static void SetIni(List<string> lines, string section, string key, string value)
		{
			string header = "[" + section + "]";
			int num = lines.FindIndex((string l) => l.Trim().Equals(header, StringComparison.OrdinalIgnoreCase));
			if (num < 0)
			{
				lines.Add(header);
				lines.Add(key + "=" + value);
				return;
			}
			int num2 = lines.FindIndex(num + 1, (string l) => l.TrimStart().StartsWith("["));
			if (num2 < 0)
			{
				num2 = lines.Count;
			}
			for (int num3 = num + 1; num3 < num2; num3++)
			{
				if (lines[num3].TrimStart().StartsWith(key + "=", StringComparison.OrdinalIgnoreCase))
				{
					lines[num3] = key + "=" + value;
					return;
				}
			}
			lines.Insert(num + 1, key + "=" + value);
		}

		private HashSet<string> LoadTranslatedValues()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (string item in deployedFiles.Where(File.Exists))
			{
				foreach (string item2 in File.ReadLines(item))
				{
					if (!item2.StartsWith("//") && !item2.StartsWith("r:") && !item2.StartsWith("sr:"))
					{
						int num = TextFormat.FindSeparator(item2);
						if (num > 0 && num < item2.Length - 1)
						{
							hashSet.Add(item2.Substring(num + 1));
						}
					}
				}
			}
			return hashSet;
		}

		private static string HashFiles(List<string> files)
		{
			using SHA256 sHA = SHA256.Create();
			foreach (string file in files)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(Path.GetFileName(file));
				sHA.TransformBlock(bytes, 0, bytes.Length, null, 0);
				byte[] array = File.ReadAllBytes(file);
				sHA.TransformBlock(array, 0, array.Length, null, 0);
			}
			sHA.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
			return BitConverter.ToString(sHA.Hash).Replace("-", "");
		}

		private static Dictionary<string, string> ReadStamp(string path)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			if (!File.Exists(path))
			{
				return dictionary;
			}
			string[] array = File.ReadAllLines(path);
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0)
				{
					dictionary[text.Substring(0, num)] = text.Substring(num + 1);
				}
			}
			return dictionary;
		}
	}
	internal static class TextFormat
	{
		public static string Escape(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 8);
			string text = s.Replace("\r\n", "\n");
			foreach (char c in text)
			{
				switch (c)
				{
				case '\\':
					stringBuilder.Append("\\\\");
					break;
				case '\n':
					stringBuilder.Append("\\n");
					break;
				case '\r':
					stringBuilder.Append("\\r");
					break;
				case '=':
					stringBuilder.Append("\\=");
					break;
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			return stringBuilder.ToString();
		}

		public static int FindSeparator(string line)
		{
			for (int i = 0; i < line.Length; i++)
			{
				if (line[i] == '\\')
				{
					i++;
				}
				else if (line[i] == '=')
				{
					return i;
				}
			}
			return -1;
		}
	}
}