Decompiled source of Sweet Russian ScheduleI v0.1.0

Mods/Sweet_Russian_ScheduleI.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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 HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using SweetRu;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Main), "Sweet_Russian_ScheduleI", "0.1.0", "Sweet_team", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Sweet_Russian_ScheduleI")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3940cfe718a226dfe3ec614f878b707838c66e46")]
[assembly: AssemblyProduct("Sweet_Russian_ScheduleI")]
[assembly: AssemblyTitle("Sweet_Russian_ScheduleI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SweetRu
{
	internal static class Fonts
	{
		private static readonly HashSet<int> Done = new HashSet<int>();

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

		private static readonly (string game, string fallback)[] Map = new(string, string)[9]
		{
			("ComicNeue", "file:Pangolin-Regular.ttf"),
			("Handlee", "file:Pangolin-Regular.ttf"),
			("Caveat", "file:Caveat-Regular.ttf"),
			("VPPixel", "file:SweetPixel.ttf"),
			("OpenSans-Bold", "OpenSans-Bold"),
			("OpenSans-SemiBold", "OpenSans-SemiBold"),
			("OpenSans-Medium", "OpenSans-Medium"),
			("OpenSans-Light", "OpenSans-Light"),
			("", "OpenSans-SemiBold")
		};

		public static void Fix()
		{
			foreach (TMP_FontAsset fa in Resources.FindObjectsOfTypeAll<TMP_FontAsset>())
			{
				if ((Object)(object)fa == (Object)null || !Done.Add(((Object)fa).GetInstanceID()) || ((Object)fa).name.StartsWith("SweetRu_"))
				{
					continue;
				}
				try
				{
					bool flag = Array.Exists(Map, ((string game, string fallback) e) => e.game.Length > 0 && !e.game.StartsWith("OpenSans") && ((Object)fa).name.StartsWith(e.game, StringComparison.OrdinalIgnoreCase));
					bool flag2;
					try
					{
						flag2 = fa.HasCharacter('Ж', !flag, true);
					}
					catch
					{
						try
						{
							flag2 = fa.HasCharacter('Ж', !flag, false);
						}
						catch
						{
							flag2 = false;
						}
					}
					if (flag2)
					{
						continue;
					}
					TMP_FontAsset val = Fallback(((Object)fa).name);
					if ((Object)(object)val == (Object)null)
					{
						Main.Log.Warning("Нет запасного шрифта для " + ((Object)fa).name);
						continue;
					}
					if (fa.fallbackFontAssetTable == null)
					{
						fa.fallbackFontAssetTable = new List<TMP_FontAsset>();
					}
					fa.fallbackFontAssetTable.Insert(0, val);
					Main.Log.Msg("Кириллица для " + ((Object)fa).name + ": " + ((Object)val).name);
				}
				catch (Exception ex)
				{
					Main.Log.Warning("Шрифт " + ((Object)fa).name + ": " + ex.Message);
				}
			}
		}

		private static TMP_FontAsset Fallback(string gameFont)
		{
			(string, string)[] map = Map;
			for (int i = 0; i < map.Length; i++)
			{
				var (value, src) = map[i];
				if (gameFont.StartsWith(value, StringComparison.OrdinalIgnoreCase))
				{
					TMP_FontAsset val = Make(src);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			return Make("OpenSans-SemiBold") ?? Make("LiberationSans");
		}

		private static TMP_FontAsset Make(string src)
		{
			if (Made.TryGetValue(src, out var value))
			{
				return value;
			}
			TMP_FontAsset val = null;
			if (src.StartsWith("file:"))
			{
				string text = Extract(src.Substring(5));
				if (text != null)
				{
					try
					{
						val = TMP_FontAsset.CreateFontAsset(text, 0, 90, 9, (GlyphRenderMode)4165, 1024, 1024, (AtlasPopulationMode)1, true);
						if (val != null)
						{
							val.TryAddCharacters("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя", false);
						}
					}
					catch (Exception ex)
					{
						Main.Log.Warning("Шрифт " + src + " не создался: " + ex.Message);
					}
				}
			}
			else if (src.StartsWith("pixel:"))
			{
				string text2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), src.Substring(6));
				try
				{
					if (File.Exists(text2))
					{
						val = TMP_FontAsset.CreateFontAsset(text2, 0, 12, 1, (GlyphRenderMode)4122, 256, 256, (AtlasPopulationMode)1, true);
					}
				}
				catch (Exception ex2)
				{
					Main.Log.Warning("Пиксельный шрифт из файла не создался: " + ex2.Message);
				}
				if ((Object)(object)val == (Object)null)
				{
					try
					{
						Font val2 = Font.CreateDynamicFontFromOSFont(Il2CppStringArray.op_Implicit(new string[1] { "Tahoma" }), 12);
						if ((Object)(object)val2 != (Object)null)
						{
							val = TMP_FontAsset.CreateFontAsset(val2, 12, 1, (GlyphRenderMode)4122, 256, 256, (AtlasPopulationMode)1, true);
						}
					}
					catch (Exception ex3)
					{
						Main.Log.Warning("Пиксельный шрифт из Windows не создался: " + ex3.Message);
					}
				}
				try
				{
					Il2CppReferenceArray<Texture2D> val3 = ((val != null) ? val.atlasTextures : null);
					if (val3 != null)
					{
						foreach (Texture2D item in (Il2CppArrayBase<Texture2D>)(object)val3)
						{
							if ((Object)(object)item != (Object)null)
							{
								((Texture)item).filterMode = (FilterMode)0;
							}
						}
					}
					if ((Object)(object)val != (Object)null && (Object)(object)val.atlasTexture != (Object)null)
					{
						((Texture)val.atlasTexture).filterMode = (FilterMode)0;
					}
				}
				catch
				{
				}
			}
			else
			{
				foreach (Font item2 in Resources.FindObjectsOfTypeAll<Font>())
				{
					if (((Object)item2).name == src)
					{
						val = TMP_FontAsset.CreateFontAsset(item2);
						break;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				((Object)val).name = "SweetRu_" + src.Replace("file:", "").Replace("pixel:", "");
				Object.DontDestroyOnLoad((Object)(object)val);
				((Object)val).hideFlags = (HideFlags)61;
				Done.Add(((Object)val).GetInstanceID());
			}
			Made[src] = val;
			return val;
		}

		private static string Extract(string file)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("font." + file);
			if (stream == null)
			{
				return null;
			}
			string text = Path.Combine(Main.Dir, "fonts", file);
			Directory.CreateDirectory(Path.GetDirectoryName(text));
			if (!File.Exists(text) || new FileInfo(text).Length != stream.Length)
			{
				using FileStream destination = File.Create(text);
				stream.CopyTo(destination);
			}
			return text;
		}
	}
	public class Main : MelonMod
	{
		public const string Version = "0.1.0";

		internal static Instance Log;

		internal static string Dir;

		private float nextFontCheck;

		private float nextTexCheck = 30f;

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			Dir = Path.Combine(MelonEnvironment.UserDataDirectory, "Sweet_Russian_ScheduleI");
			Directory.CreateDirectory(Dir);
			MelonPreferences_Category obj = MelonPreferences.CreateCategory("Sweet_Russian_ScheduleI");
			MelonPreferences_Entry<bool> val = obj.CreateEntry<bool>("LogUntranslated", false, (string)null, "Записывать непереведённые строки в UserData/Sweet_Russian_ScheduleI/untranslated.txt", false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<bool> obj2 = obj.CreateEntry<bool>("TranslateTextures", true, (string)null, "Подменять картинки с надписями на русские (читается при запуске игры)", false, false, (ValueValidator)null, (string)null);
			Tr.LogMisses = val.Value;
			Tr.Load(Dir);
			Textures.Enabled = obj2.Value;
			Textures.Load();
			ImgPatches.Install(((MelonBase)this).HarmonyInstance);
			Log.Msg($"Перевод загружен: {Tr.ExactCount} строк, {Tr.TemplateCount} шаблонов, {Textures.Count} картинок. Версия {"0.1.0"}");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			Fonts.Fix();
			Patches.RetranslateAll();
			MelonCoroutines.Start(Textures.AfterScene());
		}

		public override void OnUpdate()
		{
			if (!(Time.unscaledTime < nextFontCheck))
			{
				nextFontCheck = Time.unscaledTime + 5f;
				Fonts.Fix();
				if (Time.unscaledTime >= nextTexCheck)
				{
					nextTexCheck = Time.unscaledTime + 15f;
					Textures.Apply();
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static readonly Dictionary<int, float> BaseSize = new Dictionary<int, float>();

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void TmpText(TMP_Text __instance, ref string value)
		{
			string text = value;
			value = Tr.Get(value);
			if ((object)text != value)
			{
				Fit(__instance, text, value);
			}
		}

		internal static void Fit(TMP_Text t, string en, string ru)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)t == (Object)null || en == null || ru == null || t.enableAutoSizing)
				{
					return;
				}
				bool flag = (Object)(object)((Il2CppObjectBase)t).TryCast<TextMeshPro>() != (Object)null;
				if (!flag && (t.enableWordWrapping || ru.IndexOf('\n') >= 0))
				{
					return;
				}
				int instanceID = ((Object)t).GetInstanceID();
				if (!BaseSize.TryGetValue(instanceID, out var value))
				{
					value = (BaseSize[instanceID] = t.fontSize);
				}
				t.fontSize = value;
				float x = t.GetPreferredValues(ru).x;
				if (flag)
				{
					float x2 = t.GetPreferredValues(en).x;
					if (x2 > 0f && x > x2 * 1.02f)
					{
						t.fontSize = value * x2 / x;
					}
					return;
				}
				Rect rect = t.rectTransform.rect;
				float width = ((Rect)(ref rect)).width;
				float num = Math.Max(width, t.GetPreferredValues(en).x);
				if (width > 1f && x > num * 1.02f)
				{
					t.fontSize = Math.Max(value * 0.6f, value * num / x);
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(typeof(TMP_Text), "SetText", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void TmpSetText(ref string sourceText)
		{
			sourceText = Tr.Get(sourceText);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void UiText(ref string value)
		{
			value = Tr.Get(value);
		}

		[HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")]
		[HarmonyPrefix]
		private static void UguiEnable(TextMeshProUGUI __instance)
		{
			Fix((TMP_Text)(object)__instance);
		}

		[HarmonyPatch(typeof(TextMeshPro), "OnEnable")]
		[HarmonyPrefix]
		private static void TmpEnable(TextMeshPro __instance)
		{
			Fix((TMP_Text)(object)__instance);
		}

		private static void Fix(TMP_Text t)
		{
			try
			{
				string text = t.m_text;
				string text2 = Tr.Get(text);
				if ((object)text2 != text && text2 != text)
				{
					t.text = text2;
					Fit(t, text, text2);
				}
			}
			catch
			{
			}
		}

		internal static void RetranslateAll()
		{
			int num = 0;
			foreach (TMP_Text item in Resources.FindObjectsOfTypeAll<TMP_Text>())
			{
				string text = item.m_text;
				string text2 = Tr.Get(text);
				if (text2 != text)
				{
					item.text = text2;
					Fit(item, text, text2);
					num++;
				}
			}
			foreach (Text item2 in Resources.FindObjectsOfTypeAll<Text>())
			{
				string text3 = item2.m_Text;
				string text4 = Tr.Get(text3);
				if (text4 != text3)
				{
					item2.text = text4;
					num++;
				}
			}
			Main.Log.Msg($"Надписей в сцене переведено: {num}");
		}
	}
	internal static class ImgPatches
	{
		private static void ImgEnable(Image __instance)
		{
			try
			{
				Sprite sprite = __instance.sprite;
				if ((Object)(object)sprite != (Object)null)
				{
					Textures.ApplyOne(sprite.texture);
				}
			}
			catch
			{
			}
		}

		private static void ImgSprite(Image __instance)
		{
			try
			{
				Sprite sprite = __instance.sprite;
				if ((Object)(object)sprite != (Object)null)
				{
					Textures.ApplyOne(sprite.texture);
				}
			}
			catch
			{
			}
		}

		private static void RawEnable(RawImage __instance)
		{
			try
			{
				Texture texture = __instance.texture;
				Textures.ApplyOne((texture != null) ? ((Il2CppObjectBase)texture).TryCast<Texture2D>() : null);
			}
			catch
			{
			}
		}

		private static void Rollout(ref string text)
		{
			try
			{
				text = Tr.Get(text);
			}
			catch
			{
			}
		}

		internal static void Install(Harmony h)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			P(AccessTools.Method(typeof(Image), "OnEnable", (Type[])null, (Type[])null), "ImgEnable");
			P(AccessTools.PropertySetter(typeof(Image), "sprite"), "ImgSprite");
			P(AccessTools.Method(typeof(RawImage), "OnEnable", (Type[])null, (Type[])null), "RawEnable");
			Type type = AccessTools.TypeByName("Il2CppScheduleOne.UI.DialogueCanvas");
			if (type != null)
			{
				try
				{
					h.Patch((MethodBase)AccessTools.Method(type, "RolloutDialogue", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ImgPatches), "Rollout", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					return;
				}
				catch (Exception ex)
				{
					Main.Log.Warning("Перехват диалогов не встал: " + ex.Message);
					return;
				}
			}
			Main.Log.Warning("Не нашёл DialogueCanvas — диалоги могут обрезаться");
			void P(MethodBase m, string post)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Expected O, but got Unknown
				try
				{
					h.Patch(m, (HarmonyMethod)null, new HarmonyMethod(typeof(ImgPatches), post, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				catch (Exception ex2)
				{
					Main.Log.Warning("Перехват картинок " + post + " не встал: " + ex2.Message);
				}
			}
		}
	}
	internal static class Textures
	{
		private static readonly Dictionary<string, List<(int w, int h, string res)>> ByName = new Dictionary<string, List<(int, int, string)>>(StringComparer.Ordinal);

		private static readonly HashSet<int> Done = new HashSet<int>();

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

		public static bool Enabled = true;

		private const float MaxColorDist = 40f;

		public static int Count;

		private static readonly Dictionary<int, Texture2D> Swaps = new Dictionary<int, Texture2D>();

		private static readonly HashSet<int> Seen = new HashSet<int>();

		private static string ByColor(Texture2D t, List<(int w, int h, string res)> list, out float bd)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			RenderTexture active = RenderTexture.active;
			FilterMode filterMode = ((Texture)t).filterMode;
			((Texture)t).filterMode = (FilterMode)1;
			Texture val = (Texture)(object)t;
			RenderTexture val2 = null;
			int num = ((Texture)t).width;
			int num2 = ((Texture)t).height;
			while (num > 64 || num2 > 64)
			{
				num = Math.Max(1, num / 2);
				num2 = Math.Max(1, num2 / 2);
				RenderTexture temporary = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0);
				((Texture)temporary).filterMode = (FilterMode)1;
				Graphics.Blit(val, temporary);
				if ((Object)(object)val2 != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val2);
				}
				val2 = temporary;
				val = (Texture)(object)temporary;
			}
			((Texture)t).filterMode = filterMode;
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0);
				Graphics.Blit((Texture)(object)t, val2);
			}
			RenderTexture.active = val2;
			Texture2D val3 = new Texture2D(num, num2, (TextureFormat)4, false);
			val3.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0);
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(val2);
			float num3 = 0f;
			float num4 = 0f;
			float num5 = 0f;
			float num6 = 0f;
			Il2CppStructArray<Color> pixels = val3.GetPixels();
			foreach (Color item in (Il2CppArrayBase<Color>)(object)pixels)
			{
				num3 += Mathf.GammaToLinearSpace(item.r) * item.a;
				num4 += Mathf.GammaToLinearSpace(item.g) * item.a;
				num5 += Mathf.GammaToLinearSpace(item.b) * item.a;
				num6 += item.a;
			}
			if (num6 < 0.5f)
			{
				num3 = (num4 = (num5 = 0f));
				num6 = ((Il2CppArrayBase<Color>)(object)pixels).Length;
				foreach (Color item2 in (Il2CppArrayBase<Color>)(object)pixels)
				{
					num3 += Mathf.GammaToLinearSpace(item2.r);
					num4 += Mathf.GammaToLinearSpace(item2.g);
					num5 += Mathf.GammaToLinearSpace(item2.b);
				}
			}
			Object.Destroy((Object)(object)val3);
			num3 = Mathf.LinearToGammaSpace(num3 / num6) * 255f;
			num4 = Mathf.LinearToGammaSpace(num4 / num6) * 255f;
			num5 = Mathf.LinearToGammaSpace(num5 / num6) * 255f;
			string result = null;
			bd = float.MaxValue;
			foreach (var item3 in list)
			{
				if (item3.w == ((Texture)t).width && item3.h == ((Texture)t).height && Sig.TryGetValue(item3.res, out var value))
				{
					float num7 = Math.Abs((float)((value >> 16) & 0xFF) - num3) + Math.Abs((float)((value >> 8) & 0xFF) - num4) + Math.Abs((float)(value & 0xFF) - num5);
					if (num7 < bd)
					{
						bd = num7;
						result = item3.res;
					}
				}
			}
			return result;
		}

		public static void Load()
		{
			string[] manifestResourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (!text.StartsWith("tex.") || !text.EndsWith(".png"))
				{
					continue;
				}
				string text2 = text.Substring(4, text.Length - 8);
				string text3 = null;
				Match match = Regex.Match(text2, "__s([0-9A-Fa-f]{6})$");
				if (match.Success)
				{
					text3 = match.Groups[1].Value;
					text2 = text2.Substring(0, match.Index);
				}
				int num = text2.LastIndexOf("__", StringComparison.Ordinal);
				if (num < 0)
				{
					continue;
				}
				string[] array = text2.Substring(num + 2).Split('x');
				if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2))
				{
					string key = text2.Substring(0, num);
					if (!ByName.TryGetValue(key, out List<(int, int, string)> value))
					{
						value = (ByName[key] = new List<(int, int, string)>());
					}
					value.Add((result, result2, text));
					if (text3 != null)
					{
						Sig[text] = Convert.ToInt32(text3, 16);
					}
					Count++;
				}
			}
		}

		public static IEnumerator AfterScene()
		{
			Apply();
			yield return (object)new WaitForSeconds(3f);
			Apply();
			yield return (object)new WaitForSeconds(7f);
			Apply();
		}

		public static void Apply()
		{
			if (!Enabled || Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (Texture2D item in Resources.FindObjectsOfTypeAll<Texture2D>())
			{
				if (ApplyOne(item))
				{
					num++;
				}
			}
			if (num > 0)
			{
				Main.Log.Msg($"Картинок заменено: {num}");
			}
			if (Swaps.Count > 0)
			{
				SwapMaterials();
			}
		}

		private static void SwapMaterials()
		{
			foreach (Material item in Resources.FindObjectsOfTypeAll<Material>())
			{
				try
				{
					foreach (int item2 in (Il2CppArrayBase<int>)(object)item.GetTexturePropertyNameIDs())
					{
						Texture texture = item.GetTexture(item2);
						if ((Object)(object)texture != (Object)null && Swaps.TryGetValue(((Object)texture).GetInstanceID(), out var value))
						{
							item.SetTexture(item2, (Texture)(object)value);
						}
					}
				}
				catch
				{
				}
			}
		}

		public static bool ApplyOne(Texture2D t)
		{
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || Count == 0 || (Object)(object)t == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)t).GetInstanceID();
			if (Done.Contains(instanceID) || Seen.Contains(instanceID))
			{
				return false;
			}
			if (!ByName.TryGetValue(((Object)t).name, out List<(int, int, string)> value))
			{
				Seen.Add(instanceID);
				return false;
			}
			bool flag = false;
			foreach (var item in value)
			{
				if (item.Item1 == ((Texture)t).width && item.Item2 == ((Texture)t).height)
				{
					flag = true;
				}
			}
			if (!flag)
			{
				Seen.Add(instanceID);
				return false;
			}
			float bd;
			string text = ByColor(t, value, out bd);
			if (text == null || bd > 40f)
			{
				Done.Add(instanceID);
				return false;
			}
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text);
				byte[] array = new byte[stream.Length];
				stream.Read(array, 0, array.Length);
				Done.Add(instanceID);
				bool flag2 = false;
				try
				{
					flag2 = t.streamingMipmaps;
				}
				catch
				{
				}
				if (!flag2 && ImageConversion.LoadImage(t, new Il2CppStructArray<byte>(array), true))
				{
					return true;
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
				Done.Add(((Object)val).GetInstanceID());
				if (!ImageConversion.LoadImage(val, new Il2CppStructArray<byte>(array), true))
				{
					return false;
				}
				((Object)val).name = ((Object)t).name;
				((Texture)val).filterMode = ((Texture)t).filterMode;
				((Texture)val).wrapMode = ((Texture)t).wrapMode;
				((Texture)val).anisoLevel = ((Texture)t).anisoLevel;
				Object.DontDestroyOnLoad((Object)(object)val);
				Swaps[instanceID] = val;
				SwapMaterials();
				return true;
			}
			catch (Exception ex)
			{
				Main.Log.Warning("Картинка " + ((Object)t).name + ": " + ex.Message);
				Done.Add(instanceID);
				return false;
			}
		}
	}
	internal static class Tr
	{
		private static readonly Dictionary<string, string> Exact = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly List<(Regex re, string ru, string[] slots)> Templates = new List<(Regex, string, string[])>();

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

		private static readonly HashSet<string> Missed = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Regex Slot = new Regex("\\{\\d\\}|\\{[A-Za-z]+\\}|<[A-Z][A-Z0-9_]+>|<Input_[A-Za-z0-9_]+>", RegexOptions.Compiled);

		private static readonly Regex OpenTag = new Regex("<h\\d>|<color=[^>]+>", RegexOptions.Compiled);

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

		private static readonly Regex Latin = new Regex("[A-Za-z]{2}", RegexOptions.Compiled);

		private static readonly Regex Cyr = new Regex("[А-Яа-яЁё]", RegexOptions.Compiled);

		private static string missPath;

		public static bool LogMisses = true;

		private static readonly Regex Pronoun = new Regex("\\b(?:they're|them|they|their|They)\\b", RegexOptions.Compiled);

		private static readonly Regex Bullet = new Regex("^([•\\-\\*]\\s*)(.+)$", RegexOptions.Compiled | RegexOptions.Singleline);

		private static readonly Regex Time12 = new Regex("\\b(\\d{1,2})(?::(\\d\\d))? ?(AM|PM)\\b", RegexOptions.Compiled);

		private static readonly Regex Counter = new Regex("^(.+?)(\\s*\\(\\d+\\s*/\\s*\\d+\\))$", RegexOptions.Compiled | RegexOptions.Singleline);

		private static readonly Regex Day = new Regex("\\b(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\\b", RegexOptions.Compiled);

		private static readonly Dictionary<string, string> Days = new Dictionary<string, string>
		{
			["Monday"] = "Понедельник",
			["Tuesday"] = "Вторник",
			["Wednesday"] = "Среда",
			["Thursday"] = "Четверг",
			["Friday"] = "Пятница",
			["Saturday"] = "Суббота",
			["Sunday"] = "Воскресенье"
		};

		private static readonly Regex Mins = new Regex("\\b(\\d+) ?(?:mins?|minutes?)\\b", RegexOptions.Compiled);

		private static readonly Regex Hrs = new Regex("\\b(\\d+) ?(?:hrs?|hours?)\\b", RegexOptions.Compiled);

		private static readonly Regex Wrap = new Regex("^((?:<[^>]+>)*)(.*?)((?:</[^>]+>)*)$", RegexOptions.Compiled | RegexOptions.Singleline);

		public static int ExactCount => Exact.Count;

		public static int TemplateCount => Templates.Count;

		private static string Norm(string s)
		{
			return OpenTag.Replace(s, "<h>").Replace("</color>", "</h>");
		}

		public static void Load(string userDir)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
			Array.Sort(manifestResourceNames, (IComparer<string>?)StringComparer.Ordinal);
			string[] array = manifestResourceNames;
			foreach (string text in array)
			{
				if (text.StartsWith("tsv."))
				{
					using StreamReader streamReader = new StreamReader(executingAssembly.GetManifestResourceStream(text), Encoding.UTF8);
					Parse(streamReader.ReadToEnd());
				}
			}
			if (Directory.Exists(userDir))
			{
				array = Directory.GetFiles(userDir, "*.tsv");
				for (int i = 0; i < array.Length; i++)
				{
					Parse(File.ReadAllText(array[i], Encoding.UTF8));
				}
			}
			missPath = Path.Combine(userDir, "untranslated.txt");
			if (File.Exists(missPath))
			{
				array = File.ReadAllLines(missPath, Encoding.UTF8);
				foreach (string item in array)
				{
					Missed.Add(item);
				}
			}
		}

		private static string Unesc(string s)
		{
			return s.Replace("\\n", "\n").Replace("\\t", "\t");
		}

		private static void Parse(string text)
		{
			string[] array = text.Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].TrimEnd('\r');
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				int num = text2.IndexOf('\t');
				if (num <= 0)
				{
					continue;
				}
				string text3 = Unesc(text2.Substring(0, num)).Trim();
				string text4 = Unesc(text2.Substring(num + 1));
				int num2 = text4.IndexOf('\t');
				if (num2 >= 0)
				{
					text4 = text4.Substring(0, num2);
				}
				if (text4.Length == 0 || text3.Length == 0)
				{
					continue;
				}
				if (Slot.IsMatch(text3))
				{
					AddTemplate(text3, text4);
					continue;
				}
				Exact[text3] = text4;
				if (text3.IndexOf("<h", StringComparison.Ordinal) >= 0)
				{
					ByNorm[Norm(text3)] = text4;
				}
			}
		}

		private static void AddTemplate(string en, string ru)
		{
			List<string> list = new List<string>();
			StringBuilder stringBuilder = new StringBuilder("^");
			int num = 0;
			foreach (Match item in Slot.Matches(en))
			{
				stringBuilder.Append(Regex.Escape(en.Substring(num, item.Index - num)));
				stringBuilder.Append("(.+?)");
				list.Add(item.Value);
				num = item.Index + item.Length;
			}
			stringBuilder.Append(Regex.Escape(en.Substring(num))).Append('$');
			string input = Pronoun.Replace(stringBuilder.ToString(), (Match m) => m.Value switch
			{
				"them" => "(?:them|him|her)", 
				"they" => "(?:they|he|she)", 
				"their" => "(?:their|his|her)", 
				"they're" => "(?:they're|he's|she's)", 
				"They" => "(?:They|He|She)", 
				_ => m.Value, 
			});
			input = Regex.Replace(input, "<h\\d>", "(?:<h\\d>|<color=[^>]+>)").Replace("</h>", "(?:</h>|</color>)");
			input = Regex.Replace(input, "<color=(?:\\\\.|[^>\\\\])+>", "<color=[^>]+>");
			Templates.Add((new Regex(input, RegexOptions.Compiled | RegexOptions.Singleline), ru, list.ToArray()));
		}

		public static string Get(string s)
		{
			if (string.IsNullOrEmpty(s) || s.Length > 4000)
			{
				return s;
			}
			if (Cache.TryGetValue(s, out var value))
			{
				return value;
			}
			string text = s;
			if (Latin.IsMatch(s))
			{
				text = Post(Find(s, top: true) ?? s);
			}
			if (Cache.Count > 30000)
			{
				Cache.Clear();
			}
			Cache[s] = text;
			return text;
		}

		private static string Find(string s, bool top)
		{
			if (Exact.TryGetValue(s, out var value))
			{
				return value;
			}
			if (s.IndexOf("<color", StringComparison.Ordinal) >= 0 && ByNorm.TryGetValue(Norm(s), out value))
			{
				MatchCollection tags = OpenTag.Matches(s);
				int k = 0;
				return OpenTag.Replace(value, (Match m) => (k >= tags.Count) ? m.Value : tags[k++].Value).Replace("</h>", "</color>");
			}
			string text = s.Trim();
			if (text.Length != s.Length && text.Length > 0)
			{
				string text2 = Find(text, top: false);
				if (text2 != null)
				{
					return s.Substring(0, s.IndexOf(text, StringComparison.Ordinal)) + text2 + s.Substring(s.IndexOf(text, StringComparison.Ordinal) + text.Length);
				}
				return Miss(s, top);
			}
			if (s[0] == '<' || s[s.Length - 1] == '>')
			{
				Match match = Wrap.Match(s);
				if (match.Success && match.Groups[2].Length > 0 && match.Groups[2].Length < s.Length)
				{
					string text3 = Find(match.Groups[2].Value, top: false);
					if (text3 != null)
					{
						return match.Groups[1].Value + text3 + match.Groups[3].Value;
					}
				}
			}
			Match match2 = Bullet.Match(s);
			if (match2.Success)
			{
				string text4 = Find(match2.Groups[2].Value, top: false);
				if (text4 != null)
				{
					return match2.Groups[1].Value + text4;
				}
			}
			Match match3 = Counter.Match(s);
			if (match3.Success)
			{
				string text5 = Find(match3.Groups[1].Value, top: false);
				if (text5 != null)
				{
					return text5 + match3.Groups[2].Value;
				}
			}
			foreach (var template in Templates)
			{
				Regex item = template.re;
				string item2 = template.ru;
				string[] item3 = template.slots;
				Match match4 = item.Match(s);
				if (!match4.Success)
				{
					continue;
				}
				string text6 = item2;
				for (int num = 0; num < item3.Length; num++)
				{
					text6 = text6.Replace(item3[num], Slot1(match4.Groups[num + 1].Value));
				}
				if (s.IndexOf("<color", StringComparison.Ordinal) >= 0)
				{
					MatchCollection tags2 = OpenTag.Matches(s);
					int k2 = 0;
					text6 = OpenTag.Replace(text6, (Match mm) => (k2 >= tags2.Count) ? mm.Value : tags2[k2++].Value).Replace("</h>", "</color>");
				}
				return text6;
			}
			if (s.IndexOf('\n') >= 0)
			{
				string[] array = s.Split('\n');
				bool flag = false;
				for (int num2 = 0; num2 < array.Length; num2++)
				{
					string text7 = array[num2].TrimEnd('\r');
					if (text7.Length != 0 && Latin.IsMatch(text7))
					{
						string text8 = Find(text7, top: false);
						if (text8 != null)
						{
							array[num2] = text8;
							flag = true;
						}
						else
						{
							Miss(text7, top: true);
						}
					}
				}
				if (!flag)
				{
					return null;
				}
				return string.Join("\n", array);
			}
			return Miss(s, top);
		}

		private static string Post(string s)
		{
			s = Time12.Replace(s, delegate(Match m)
			{
				int num = int.Parse(m.Groups[1].Value) % 12;
				if (m.Groups[3].Value == "PM")
				{
					num += 12;
				}
				return num + ":" + (m.Groups[2].Success ? m.Groups[2].Value : "00");
			});
			s = Day.Replace(s, (Match m) => Days[m.Value]);
			return Hrs.Replace(Mins.Replace(s, "$1 мин"), "$1 ч");
		}

		private static string Slot1(string v)
		{
			if (Exact.TryGetValue(v.Trim(), out var value))
			{
				return value;
			}
			Match match = Wrap.Match(v);
			if (match.Success && Exact.TryGetValue(match.Groups[2].Value.Trim(), out value))
			{
				return match.Groups[1].Value + value + match.Groups[3].Value;
			}
			string text = (match.Success ? match.Groups[2].Value.Trim() : v.Trim());
			if (text.Length > 1 && char.IsLower(text[0]) && Exact.TryGetValue(char.ToUpper(text[0]) + text.Substring(1), out value))
			{
				return (match.Success ? match.Groups[1].Value : "") + value + (match.Success ? match.Groups[3].Value : "");
			}
			string text2 = v.TrimEnd();
			if (text2.Length > 1 && ".!?,;:".IndexOf(text2[text2.Length - 1]) >= 0 && Exact.TryGetValue(text2.Substring(0, text2.Length - 1), out value))
			{
				return value + text2.Substring(text2.Length - 1);
			}
			return v;
		}

		private static string Miss(string s, bool top)
		{
			if (!top || !LogMisses || missPath == null || Cyr.IsMatch(s))
			{
				return null;
			}
			string text = s.Replace("\r", "").Replace("\n", "\\n").Replace("\t", "\\t");
			if (Missed.Add(text))
			{
				try
				{
					File.AppendAllText(missPath, text + "\n", Encoding.UTF8);
				}
				catch
				{
				}
			}
			return null;
		}
	}
}