Decompiled source of RussianFix v1.0.2

plugins/ElfDevelop.RussianFix.dll

Decompiled 7 hours ago
using System;
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.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 RussianFix
{
	internal static class MapScreenText
	{
		private static readonly Regex SelectInDays = new Regex("^SELECT IN (\\d+) DAYS?$");

		private static readonly Regex MaxDepthMeters = new Regex("^Max depth: (\\d+) meters$");

		private static readonly Regex Depth = new Regex("^(\\D*?)(\\d+)m$");

		internal static string? Translate(string? text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			switch (text)
			{
			case "MAP SELECTION":
				return "ВЫБОР КАРТЫ";
			case "MAP SELECTED":
				return "КАРТА ВЫБРАНА";
			case "MAX DEPTH":
				return "МАКС. ГЛУБИНА";
			default:
			{
				Match match = SelectInDays.Match(text);
				if (match.Success)
				{
					int num = int.Parse(match.Groups[1].Value);
					return $"ВЫБОР ЧЕРЕЗ {num} {Days(num)}";
				}
				match = MaxDepthMeters.Match(text);
				if (match.Success)
				{
					return "Макс. глубина: " + match.Groups[1].Value + " м";
				}
				match = Depth.Match(text);
				if (match.Success)
				{
					return match.Groups[1].Value + match.Groups[2].Value + " м";
				}
				return null;
			}
			}
		}

		private static string Days(int n)
		{
			int num = n % 100;
			int num2 = n % 10;
			if (num >= 11 && num <= 14)
			{
				return "ДНЕЙ";
			}
			switch (num2)
			{
			case 1:
				return "ДЕНЬ";
			case 2:
			case 3:
			case 4:
				return "ДНЯ";
			default:
				return "ДНЕЙ";
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static readonly Regex KeyLabel = new Regex("^(\\s*\\{key\\}\\s*)(.+?)\\s*$");

		private static readonly Regex OwnedCamera = new Regex("^(.+)'s (Broken )?Camera$");

		[HarmonyPatch(typeof(Item), "GetLocalizedDisplayName")]
		[HarmonyPrefix]
		private static bool ItemName(Item __instance, ref string __result)
		{
			if (HasVanillaKey(((Object)__instance).name))
			{
				return true;
			}
			__result = ModdedName(__instance.displayName);
			return false;
		}

		[HarmonyPatch(typeof(Item), "GetLocalizedDisplayName")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void ItemNameAfterOthers(Item __instance, ref string __result)
		{
			if (__result == __instance.displayName && !HasVanillaKey(((Object)__instance).name))
			{
				__result = ModdedName(__instance.displayName);
			}
		}

		[HarmonyPatch(typeof(ShopItem), "UpdateLocalizedName")]
		[HarmonyPrefix]
		private static bool ShopItemName(ref ShopItem __instance)
		{
			if (HasVanillaKey(((Object)__instance.Item).name))
			{
				return true;
			}
			((ShopItem)(ref __instance)).DisplayName = ModdedName(__instance.Item.displayName);
			return false;
		}

		private static bool HasVanillaKey(string itemName)
		{
			Keys result;
			return Enum.TryParse<Keys>(itemName.Trim().Replace(" ", ""), out result);
		}

		private static string ModdedName(string displayName)
		{
			if (RussianFixPlugin.IsRussian && Translations.TryTranslate(Translations.Items, displayName, out string russian))
			{
				return russian;
			}
			return displayName;
		}

		[HarmonyPatch(typeof(Item), "GetTootipData")]
		[HarmonyPostfix]
		private static void Tooltips(IEnumerable<IHaveUIData> __result)
		{
			if (!RussianFixPlugin.IsRussian || __result == null)
			{
				return;
			}
			foreach (IHaveUIData item in __result)
			{
				ItemKeyTooltip val = (ItemKeyTooltip)(object)((item is ItemKeyTooltip) ? item : null);
				if (val != null && val.m_text != null)
				{
					Match match = KeyLabel.Match(val.m_text);
					string english = (match.Success ? match.Groups[2].Value : val.m_text.Trim());
					if (Translations.TryTranslate(Translations.Tooltips, english, out string russian))
					{
						val.m_text = (match.Success ? (match.Groups[1].Value + russian) : russian);
						val.m_info = null;
					}
				}
			}
		}

		[HarmonyPatch(typeof(InteractionUI), "SetData")]
		[HarmonyPostfix]
		private static void HoverText(InteractionUI __instance)
		{
			if (!RussianFixPlugin.IsRussian || (Object)(object)__instance.m_text == (Object)null)
			{
				return;
			}
			string text = ((TMP_Text)__instance.m_text).text;
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if (text == "Pickup Camera")
			{
				((TMP_Text)__instance.m_text).text = "Взять камеру";
				return;
			}
			if (text == "Pickup Broken Camera")
			{
				((TMP_Text)__instance.m_text).text = "Взять сломанную камеру";
				return;
			}
			Match match = OwnedCamera.Match(text);
			string value;
			string value2;
			if (match.Success)
			{
				string text2 = ((match.Groups[1].Value == "?") ? "?" : match.Groups[1].Value);
				((TMP_Text)__instance.m_text).text = (match.Groups[2].Success ? ("Сломанная камера: " + text2) : ("Камера: " + text2));
			}
			else if (Translations.Categories.TryGetValue(text, out value))
			{
				((TMP_Text)__instance.m_text).text = value;
			}
			else if (Translations.HoverTexts.TryGetValue(text, out value2))
			{
				((TMP_Text)__instance.m_text).text = value2;
			}
		}

		[HarmonyPatch(typeof(ShopInteractibleCategory), "Setup")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void CategoryButton(ShopInteractibleCategory __instance)
		{
			if (RussianFixPlugin.IsRussian && !((Object)(object)__instance.m_CategoryText == (Object)null) && Translations.TryTranslate(Translations.Categories, ((TMP_Text)__instance.m_CategoryText).text, out string russian))
			{
				((TMP_Text)__instance.m_CategoryText).text = russian;
				((Interactable)__instance).hoverText = russian;
			}
		}

		[HarmonyPatch(typeof(ShopViewScreen), "UpdateViewScreen")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void CategoryTitle(ShopViewScreen __instance)
		{
			if (RussianFixPlugin.IsRussian && !((Object)(object)__instance.m_CurrentCategoryNameText == (Object)null) && Translations.Categories.TryGetValue(((TMP_Text)__instance.m_CurrentCategoryNameText).text, out string value))
			{
				((TMP_Text)__instance.m_CurrentCategoryNameText).text = value.ToUpper();
			}
		}
	}
	[ContentWarningPlugin("ElfDevelop.RussianFix", "1.0.2", true)]
	[BepInPlugin("ElfDevelop.RussianFix", "RussianFix", "1.0.2")]
	public class RussianFixPlugin : BaseUnityPlugin
	{
		internal static string UntranslatedPath = "";

		private readonly Harmony harmony = new Harmony("ElfDevelop.RussianFix");

		private bool fontsFixed;

		private static Type? mapScreenRootType;

		private static readonly Dictionary<string, string> tabKeys = new Dictionary<string, string>();

		internal static ManualLogSource Logger { get; private set; } = null;

		internal static bool IsRussian
		{
			get
			{
				//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)
				try
				{
					Locale selectedLocale = LocalizationSettings.SelectedLocale;
					object obj;
					if (selectedLocale == null)
					{
						obj = null;
					}
					else
					{
						LocaleIdentifier identifier = selectedLocale.Identifier;
						obj = ((LocaleIdentifier)(ref identifier)).Code;
					}
					return ((string)obj)?.StartsWith("ru", StringComparison.OrdinalIgnoreCase) ?? false;
				}
				catch (Exception)
				{
					return false;
				}
			}
		}

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			string configPath = Paths.ConfigPath;
			UntranslatedPath = Path.Combine(configPath, "ElfDevelop.RussianFix.untranslated.txt");
			Translations.LoadUntranslated(UntranslatedPath);
			Translations.LoadUserFile(Path.Combine(configPath, "ElfDevelop.RussianFix.ru.txt"));
			harmony.PatchAll();
			SceneManager.sceneLoaded += OnSceneLoaded;
			Logger.LogInfo((object)"RussianFix 1.0.2 has loaded!");
		}

		private void Start()
		{
			PatchSettingNames();
			PatchOptionalMods();
		}

		private void PatchOptionalMods()
		{
			TryPatch("Spookbox.Entries.VolumeEntry:GetString", "SpookboxVolume");
			TryPatch("Spookbox.Entries.TrackEntry:GetString", "SpookboxTrack");
			TryPatch("ContentSettings.API.Settings.UI.SettingsNavigation:Create", "SettingsTabCreated");
			TryPatch("ContentSettings.API.SettingsLoader:TryGetTab", "SettingsTabLookup", prefix: true);
			TryPatch("ContentSettings.API.SettingsLoader:HasTab", "SettingsTabLookup", prefix: true);
			TryPatch("RegionsExpanded.MapScreenVisualCoordinator:RefreshAll", "TranslateMapScreens");
			TryPatch("RegionsExpanded.MapScreenVisualCoordinator:RefreshMaxDepth", "TranslateMapScreens");
		}

		private static void TranslateMapScreens()
		{
			if (!IsRussian)
			{
				return;
			}
			if ((object)mapScreenRootType == null)
			{
				mapScreenRootType = AccessTools.TypeByName("RegionsExpanded.MapScreenVisualRoot");
			}
			if (mapScreenRootType == null)
			{
				return;
			}
			Object[] array = Object.FindObjectsOfType(mapScreenRootType);
			foreach (Object obj in array)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if (val == null)
				{
					continue;
				}
				TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val2 in componentsInChildren)
				{
					string text = MapScreenText.Translate(val2.text);
					if (text != null)
					{
						val2.text = text;
					}
				}
			}
		}

		private void TryPatch(string target, string patch, bool prefix = false)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(target, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return;
			}
			HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(RussianFixPlugin), patch, (Type[])null, (Type[])null));
			try
			{
				if (prefix)
				{
					harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Could not patch " + target + ": " + ex.Message));
			}
		}

		private static void SpookboxVolume(ref string __result)
		{
			if (__result != null && IsRussian)
			{
				__result = __result.Replace("Volume", "громкость");
			}
		}

		private static void SpookboxTrack(ref string __result)
		{
			if (__result == "No Tracks Found :[" && IsRussian)
			{
				__result = "Треков нет :[";
			}
		}

		private static void SettingsTabCreated(object __result)
		{
			if (IsRussian && __result != null)
			{
				PropertyInfo propertyInfo = AccessTools.Property(__result.GetType(), "Name");
				if (propertyInfo?.GetValue(__result) is string text && Translations.SettingsTabs.TryGetValue(text, out string value))
				{
					tabKeys[value] = text;
					propertyInfo.SetValue(__result, value);
				}
			}
		}

		private static void SettingsTabLookup(ref string tab)
		{
			if (tab != null && tabKeys.TryGetValue(tab, out string value))
			{
				tab = value;
			}
		}

		private void PatchSettingNames()
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			Assembly assembly = typeof(IExposedSetting).Assembly;
			MethodInfo methodInfo = AccessTools.Method(typeof(RussianFixPlugin), "TranslateSettingName", (Type[])null, (Type[])null);
			int num = 0;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly2 in assemblies)
			{
				if (assembly2 == assembly || assembly2.IsDynamic)
				{
					continue;
				}
				Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly2);
				foreach (Type type in typesFromAssembly)
				{
					if (type.IsAbstract || !typeof(IExposedSetting).IsAssignableFrom(type))
					{
						continue;
					}
					MethodInfo methodInfo2 = AccessTools.DeclaredMethod(type, "GetDisplayName", Type.EmptyTypes, (Type[])null);
					if (!(methodInfo2 == null) && !methodInfo2.IsAbstract)
					{
						try
						{
							harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							num++;
						}
						catch (Exception ex)
						{
							Logger.LogWarning((object)("Could not patch " + type.FullName + ".GetDisplayName: " + ex.Message));
						}
					}
				}
			}
			Logger.LogInfo((object)$"Translating {num} modded setting names");
		}

		private static void TranslateSettingName(ref string __result)
		{
			if (__result != null && IsRussian && Translations.TryTranslate(Translations.Settings, __result, out string russian))
			{
				__result = russian;
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (fontsFixed)
			{
				return;
			}
			List<TMP_FontAsset> list = (from f in Resources.FindObjectsOfTypeAll<TMP_FontAsset>()
				where (Object)(object)f != (Object)null && f.HasCharacter('Ж', false, true) && f.HasCharacter('я', false, true)
				select f).ToList();
			if (list.Count == 0)
			{
				return;
			}
			List<TMP_FontAsset> fallbacks = TMP_Settings.fallbackFontAssets ?? new List<TMP_FontAsset>();
			foreach (TMP_FontAsset item in list.Where((TMP_FontAsset f) => !fallbacks.Contains(f)))
			{
				fallbacks.Add(item);
			}
			TMP_Settings.fallbackFontAssets = fallbacks;
			fontsFixed = true;
			Logger.LogInfo((object)string.Format("Added {0} Cyrillic fallback fonts: {1}", list.Count, string.Join(", ", list.Select((TMP_FontAsset f) => ((Object)f).name))));
		}
	}
	internal static class Translations
	{
		internal static readonly Dictionary<string, string> Items = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Spöökbox"] = "Спукбокс",
			["Angel wings"] = "Крылья ангела",
			["Armor Platings"] = "Бронепластины",
			["Band-aid box"] = "Пластыри",
			["Bigslap Arm Artifact"] = "Рука Бигслэпа",
			["Bright vest"] = "Яркий жилет",
			["Cursed Doll"] = "Проклятая кукла",
			["Electric grenade"] = "Электрограната",
			["Energy bar"] = "Энергетический батончик",
			["Flamethrower"] = "Огнемёт",
			["Fragile bat"] = "Хрупкая бита",
			["Froggy boots"] = "Лягушачьи ботинки",
			["Grenade"] = "Граната",
			["Invisibility spray"] = "Спрей невидимости",
			["Large O2 tank"] = "Большой баллон O2",
			["Medkit"] = "Аптечка",
			["Pop-it"] = "Поп-ит",
			["Radar"] = "Радар",
			["Semtex grenade"] = "Граната с семтексом",
			["Silver fulminate crystal"] = "Кристалл гремучего серебра",
			["Small O2 tank"] = "Малый баллон O2",
			["Snail Egg"] = "Яйцо улитки",
			["Sniper rifle"] = "Снайперская винтовка",
			["Solid bat"] = "Крепкая бита",
			["Tranquilizer gun"] = "Транквилизатор"
		};

		internal static readonly Dictionary<string, string> Categories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Weapons"] = "Оружие",
			["Consumables"] = "Расходники",
			["Equipables"] = "Снаряжение",
			["Explosives"] = "Взрывчатка",
			["Bell upgrades"] = "Улучшения колокола",
			["Artifacts"] = "Артефакты"
		};

		internal static readonly Dictionary<string, string> HoverTexts = new Dictionary<string, string>
		{
			["Ascend"] = "Подняться",
			["Descend"] = "Спуститься",
			["Open"] = "Открыть",
			["Close"] = "Закрыть"
		};

		internal static readonly Dictionary<string, string> SettingsTabs = new Dictionary<string, string>
		{
			["GRAPHICS"] = "ГРАФИКА",
			["AUDIO"] = "ЗВУК",
			["MOUSEKEYBOARD"] = "МЫШЬ И КЛАВИАТУРА",
			["MATCHMAKING"] = "ПОДБОР ИГРЫ",
			["MODS"] = "МОДЫ",
			["CONTROLLER"] = "ГЕЙМПАД",
			["MODDED"] = "ДРУГИЕ МОДЫ"
		};

		internal static readonly Dictionary<string, string> Tooltips = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Play"] = "Играть",
			["Select Track"] = "Выбрать трек"
		};

		internal static readonly Dictionary<string, string> Settings = new Dictionary<string, string>
		{
			["Push"] = "Толкнуть",
			["Kill Key"] = "Самоубийство",
			["Ragdoll"] = "Упасть (рэгдолл)",
			["Drop Equipable Item"] = "Снять снаряжение",
			["Face Color applies to suit"] = "Цвет лица применяется к костюму",
			["[CapsulePath] Hide path in camera footage"] = "[CapsulePath] Скрывать путь на записи камеры",
			["[CapsulePath] Recalculate path key"] = "[CapsulePath] Клавиша пересчёта пути",
			["[CapsulePath] Show on-screen status"] = "[CapsulePath] Показывать статус на экране",
			["[CapsulePath] Toggle path key"] = "[CapsulePath] Клавиша показа пути",
			["[Spöökbox] Infinite battery (Host)"] = "[Spöökbox] Бесконечная батарея (хост)",
			["[Spöökbox] Let BigSlap hear the tunes (Host)"] = "[Spöökbox] Бигслэп слышит музыку (хост)",
			["[Spöökbox] Local volume"] = "[Spöökbox] Громкость у себя",
			["[Spöökbox] Open music folder"] = "[Spöökbox] Открыть папку с музыкой",
			["[Spöökbox] Price (Host)"] = "[Spöökbox] Цена (хост)",
			["[Spöökbox] Reload music"] = "[Spöökbox] Перезагрузить музыку",
			["[Spöökbox] Volume Down Key"] = "[Spöökbox] Клавиша «тише»",
			["[Spöökbox] Volume Up Key"] = "[Spöökbox] Клавиша «громче»",
			["[ContentPOVs] Match camera color to player's visor color"] = "[ContentPOVs] Цвет камеры как у визора игрока",
			["[ContentPOVs] Show user's name while hovering over camera"] = "[ContentPOVs] Показывать владельца при наведении на камеру",
			["[ContentPOVs] Display the camera's owner at the bottom right of recordings"] = "[ContentPOVs] Ник владельца в правом нижнем углу записи",
			["[ContentPOVs] Divide camera views between all players"] = "[ContentPOVs] Делить просмотры между всеми игроками",
			["[ContentPOVs] Only owner can pickup camera"] = "[ContentPOVs] Камеру может взять только владелец",
			["[ContentPOVs] Only owner can pickup broken camera"] = "[ContentPOVs] Сломанную камеру может взять только владелец",
			["[ContentPOVs] Let anyone pickup the cameras of dead players (but not record)"] = "[ContentPOVs] Камеры погибших может взять любой (но не снимать)",
			["[ContentPOVs] Allow recording with dead players' cameras\n\n(Requires above setting)"] = "[ContentPOVs] Разрешить снимать камерами погибших\n\n(нужна настройка выше)",
			["[ContentPOVs] Player view influence.\n\n(Requires above setting. 0 = regular views, 100 = divide views equally between all players)"] = "[ContentPOVs] Доля просмотров игрока.\n\n(нужна настройка выше; 0 = обычные просмотры, 100 = поровну между всеми)"
		};

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

		internal static void LoadUntranslated(string path)
		{
			if (File.Exists(path))
			{
				string[] array = File.ReadAllLines(path, Encoding.UTF8);
				foreach (string text in array)
				{
					int num = text.IndexOf(" = ", StringComparison.Ordinal);
					logged.Add(((num >= 0) ? text.Substring(0, num) : text).Replace("\\n", "\n"));
				}
			}
		}

		internal static void LoadUserFile(string path)
		{
			if (!File.Exists(path))
			{
				File.WriteAllText(path, "# Свои переводы: \"English = Русский\", по одной строке. Перекрывают встроенные.\n# Непереведённые строки, которые мод встретил в игре, записываются в ElfDevelop.RussianFix.untranslated.txt\n", Encoding.UTF8);
				return;
			}
			int num = 0;
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					int num2 = text.IndexOf(" = ", StringComparison.Ordinal);
					if (num2 > 0)
					{
						string key = text.Substring(0, num2).Trim().Replace("\\n", "\n");
						string value = text.Substring(num2 + 3).Trim().Replace("\\n", "\n");
						Items[key] = value;
						Tooltips[key] = value;
						Settings[key] = value;
						num++;
					}
				}
			}
			RussianFixPlugin.Logger.LogInfo((object)$"Loaded {num} user translations");
		}

		internal static bool TryTranslate(Dictionary<string, string> table, string english, out string russian)
		{
			if (table.TryGetValue(english, out russian))
			{
				return true;
			}
			LogUntranslated(english);
			return false;
		}

		private static void LogUntranslated(string english)
		{
			if (string.IsNullOrWhiteSpace(english) || !logged.Add(english) || IsRussian(english))
			{
				return;
			}
			try
			{
				File.AppendAllText(RussianFixPlugin.UntranslatedPath, english.Replace("\n", "\\n") + " = \n", Encoding.UTF8);
			}
			catch (IOException)
			{
			}
		}

		private static bool IsRussian(string s)
		{
			foreach (char c in s)
			{
				if (c >= 'А' && c <= 'я')
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}