Decompiled source of White Knuckle Russian Localization v2.1.6

plugins/WKLoc/WKLoc.Core.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("WKLoc.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+de287f2d82518f94b32be74ee68529cb9c9f954e")]
[assembly: AssemblyProduct("WKLoc.Core")]
[assembly: AssemblyTitle("WKLoc.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 WKLoc.Core
{
	public static class AnchorExtractor
	{
		private const int MinLength = 4;

		public static string FromTemplateKey(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return string.Empty;
			}
			string text = string.Empty;
			string[] array = Regex.Split(key, "\\{\\d+\\}");
			foreach (string text2 in array)
			{
				if (text2.Length > text.Length)
				{
					text = text2;
				}
			}
			if (text.Length < 4)
			{
				return string.Empty;
			}
			return text;
		}

		public static string FromRegex(string pattern)
		{
			if (string.IsNullOrEmpty(pattern))
			{
				return string.Empty;
			}
			if (Regex.IsMatch(pattern, "\\(\\?[a-zA-Z-]*[iI]"))
			{
				return string.Empty;
			}
			char c = '\\';
			string best = string.Empty;
			StringBuilder run = new StringBuilder();
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < pattern.Length; i++)
			{
				char c2 = pattern[i];
				if (num2 > 0)
				{
					if (c2 == c)
					{
						i++;
					}
					else if (c2 == ']')
					{
						num2--;
					}
					continue;
				}
				if (num > 0)
				{
					if (c2 == c)
					{
						i++;
						continue;
					}
					switch (c2)
					{
					case '[':
						num2++;
						break;
					case '(':
						num++;
						break;
					case ')':
						num--;
						break;
					}
					continue;
				}
				switch (c2)
				{
				case '|':
					return string.Empty;
				case '(':
					EndRun();
					num = 1;
					continue;
				case '[':
					EndRun();
					num2 = 1;
					continue;
				case '{':
					DropLastAndEndRun();
					for (; i + 1 < pattern.Length && pattern[i + 1] != '}'; i++)
					{
					}
					i++;
					continue;
				case '*':
				case '?':
					DropLastAndEndRun();
					continue;
				case '+':
					EndRun();
					continue;
				case '$':
				case '.':
				case '^':
					EndRun();
					continue;
				case ')':
				case ']':
				case '}':
					return string.Empty;
				}
				if (c2 == c)
				{
					if (i + 1 >= pattern.Length)
					{
						return string.Empty;
					}
					char c3 = pattern[i + 1];
					i++;
					if (char.IsLetterOrDigit(c3))
					{
						EndRun();
					}
					else
					{
						run.Append(c3);
					}
				}
				else
				{
					run.Append(c2);
				}
			}
			EndRun();
			if (best.Length < 4)
			{
				return string.Empty;
			}
			return best;
			void DropLastAndEndRun()
			{
				if (run.Length > 0)
				{
					run.Length--;
				}
				EndRun();
			}
			void EndRun()
			{
				if (run.Length > best.Length)
				{
					best = run.ToString();
				}
				run.Length = 0;
			}
		}
	}
	public sealed class AutoGeneratedTextWriter : IDisposable
	{
		private readonly string _langDir;

		private readonly TimeSpan _delay;

		private readonly Func<string, TranslationData?> _deserialize;

		private readonly Func<TranslationData, string> _serialize;

		private readonly Action<Exception>? _onError;

		private readonly Action<int, string>? _onWrote;

		private readonly object _gate = new object();

		private readonly object _flushGate = new object();

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

		private readonly AutoResetEvent _signal = new AutoResetEvent(initialState: false);

		private Thread? _worker;

		private bool _started;

		private volatile bool _disposed;

		public AutoGeneratedTextWriter(string langDir, TimeSpan delay, Func<string, TranslationData?> deserialize, Func<TranslationData, string> serialize, Action<Exception>? onError = null, Action<int, string>? onWrote = null)
		{
			_langDir = langDir;
			_delay = ((delay < TimeSpan.Zero) ? TimeSpan.Zero : delay);
			_deserialize = deserialize;
			_serialize = serialize;
			_onError = onError;
			_onWrote = onWrote;
		}

		public void Enqueue(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return;
			}
			lock (_gate)
			{
				if (_disposed)
				{
					return;
				}
				_pending.Add(key);
				EnsureWorker();
			}
			_signal.Set();
		}

		private void EnsureWorker()
		{
			if (!_started)
			{
				_started = true;
				_worker = new Thread(WorkerLoop)
				{
					IsBackground = true,
					Name = "WKLoc-AutoGenWriter"
				};
				_worker.Start();
			}
		}

		private void WorkerLoop()
		{
			while (true)
			{
				_signal.WaitOne();
				if (_disposed)
				{
					break;
				}
				if (_delay > TimeSpan.Zero)
				{
					Thread.Sleep(_delay);
				}
				Flush();
			}
		}

		public void Flush()
		{
			lock (_flushGate)
			{
				FlushCore();
			}
		}

		private void FlushCore()
		{
			while (true)
			{
				List<string> list;
				lock (_gate)
				{
					if (_pending.Count == 0)
					{
						break;
					}
					list = new List<string>(_pending);
					_pending.Clear();
				}
				try
				{
					WriteBatch(list);
				}
				catch (Exception obj)
				{
					lock (_gate)
					{
						foreach (string item in list)
						{
							_pending.Add(item);
						}
					}
					_onError?.Invoke(obj);
					break;
				}
			}
		}

		private void WriteBatch(List<string> batch)
		{
			if (batch.Count == 0)
			{
				return;
			}
			Directory.CreateDirectory(_langDir);
			string text = Path.Combine(_langDir, "_AutoGenerated.json");
			TranslationData translationData = new TranslationData();
			if (File.Exists(text))
			{
				TranslationData translationData2 = _deserialize(File.ReadAllText(text));
				if (translationData2 != null)
				{
					translationData = translationData2;
				}
			}
			int num = 0;
			foreach (string item in batch)
			{
				if (!translationData.Exact.ContainsKey(item))
				{
					translationData.Exact[item] = "";
					num++;
				}
			}
			if (num != 0)
			{
				File.WriteAllText(text, _serialize(translationData));
				_onWrote?.Invoke(num, text);
			}
		}

		public void Dispose()
		{
			lock (_gate)
			{
				if (_disposed)
				{
					return;
				}
				_disposed = true;
			}
			_signal.Set();
			try
			{
				_worker?.Join(2000);
			}
			catch
			{
			}
			Flush();
			_signal.Dispose();
		}
	}
	public sealed class BatchCursor
	{
		public int Length { get; private set; }

		public int Index { get; private set; }

		public bool HasRemaining => Index < Length;

		public void Reset(int length)
		{
			Length = ((length >= 0) ? length : 0);
			Index = 0;
		}

		public bool Step(int batchSize, out int start, out int end)
		{
			if (Length <= 0)
			{
				start = 0;
				end = 0;
				return true;
			}
			if (Index >= Length)
			{
				start = Length;
				end = Length;
				return true;
			}
			start = Index;
			end = Math.Min(Index + Math.Max(1, batchSize), Length);
			Index = end;
			return Index >= Length;
		}
	}
	public static class BudgetMath
	{
		private const float BootstrapMs = 1f;

		private const int MinSamples = 5;

		private const double AbsFloorMs = 0.5;

		private const double FloorFraction = 0.05;

		private const double CeilingFraction = 0.5;

		public static float ComputeBudgetMs(float[] frameMs, int count)
		{
			if (count < 5)
			{
				return 1f;
			}
			double num = 0.0;
			double num2 = 0.0;
			for (int i = 0; i < count; i++)
			{
				float num3 = frameMs[i];
				num += (double)num3;
				num2 += (double)num3 * (double)num3;
			}
			double num4 = num / (double)count;
			double num5 = num2 / (double)count - num4 * num4;
			double num6 = ((num5 > 0.0) ? Math.Sqrt(num5) : 0.0);
			double num7 = num4 * 0.05;
			double num8 = ((num6 > num7) ? num6 : num7);
			double num9 = num4 * 0.5;
			return (float)((num8 < 0.5) ? 0.5 : ((num8 > num9) ? num9 : num8));
		}
	}
	public static class CollectFilter
	{
		private static readonly Regex Garbage = new Regex("^[.\\-_~=\\s\\/\\\\|:;()\\[\\]{}*+?!@#$%^&<>,\"']*$", RegexOptions.Compiled);

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

		private static readonly Regex PureNumber = new Regex("^[+-]?\\d+(?:[.,]\\d+)?$", RegexOptions.Compiled);

		private static readonly Regex Timing = new Regex("^\\d{1,2}(?::\\d{1,2}){1,3}$", RegexOptions.Compiled);

		public static bool ShouldCollect(string? key)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return false;
			}
			string text = key.Trim();
			if (text.Length <= 1)
			{
				return false;
			}
			if (Cyrillic.IsMatch(text))
			{
				return false;
			}
			if (Garbage.IsMatch(text))
			{
				return false;
			}
			if (PureNumber.IsMatch(text))
			{
				return false;
			}
			if (Timing.IsMatch(text))
			{
				return false;
			}
			if (text.StartsWith("<") && text.EndsWith(">") && !text.Contains(" "))
			{
				return false;
			}
			return true;
		}
	}
	public sealed class DeferredRefreshQueue<T> where T : class
	{
		private struct Entry
		{
			public readonly T Item;

			public int Frames;

			public Entry(T item, int frames)
			{
				Item = item;
				Frames = frames;
			}
		}

		private readonly List<Entry> _entries = new List<Entry>();

		public void Enqueue(T item, int frames)
		{
			if (item == null)
			{
				return;
			}
			frames = Math.Max(1, frames);
			for (int i = 0; i < _entries.Count; i++)
			{
				if (_entries[i].Item == item)
				{
					_entries[i] = new Entry(item, frames);
					return;
				}
			}
			_entries.Add(new Entry(item, frames));
		}

		public void Tick(Action<T> action)
		{
			if (action == null)
			{
				return;
			}
			for (int num = _entries.Count - 1; num >= 0; num--)
			{
				Entry value = _entries[num];
				value.Frames--;
				if (value.Frames > 0)
				{
					_entries[num] = value;
				}
				else
				{
					_entries.RemoveAt(num);
					action(value.Item);
				}
			}
		}
	}
	public static class FontBundleLocator
	{
		private static readonly HashSet<string> NonBundleExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			".cfg", ".config", ".cs", ".dll", ".exe", ".json", ".md", ".pdb", ".png", ".txt",
			".xml"
		};

		public static string[] GetCandidatePaths(IEnumerable<string> files)
		{
			List<string> list = new List<string>();
			foreach (string file in files)
			{
				if (!string.IsNullOrWhiteSpace(file))
				{
					string extension = Path.GetExtension(file);
					if (extension.Length <= 0 || !NonBundleExtensions.Contains(extension))
					{
						list.Add(file);
					}
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return list.ToArray();
		}
	}
	public static class HotkeyDefaults
	{
		public const bool EnableHotkeys = false;

		public const string ToggleTranslationKey = "F6";

		public const string ReloadKey = "F7";
	}
	public static class LegacyTxtParser
	{
		private const char Backslash = '\\';

		private const char Quote = '"';

		private static readonly string RegexPrefix = "r:\"";

		private static readonly string SplitPrefix = "sr:\"";

		private static readonly string QuoteEq = "\"=";

		private static readonly string EscapedEq = "\\=";

		public static TranslationData Parse(IEnumerable<string> lines)
		{
			TranslationData translationData = new TranslationData();
			HashSet<int> levels = new HashSet<int>();
			bool captured = false;
			foreach (string line in lines)
			{
				if (string.IsNullOrEmpty(line))
				{
					continue;
				}
				string text = line.TrimEnd('\r', '\n');
				if (text.Length == 0)
				{
					continue;
				}
				if (text[0] == '#')
				{
					ApplyDirective(text, levels);
					continue;
				}
				if (text.StartsWith(SplitPrefix))
				{
					ParseRegex(text, SplitPrefix, translationData.SplitterRegex);
					CaptureLevels(levels, translationData, ref captured);
					continue;
				}
				if (text.StartsWith(RegexPrefix))
				{
					ParseRegex(text, RegexPrefix, translationData.Regex);
					CaptureLevels(levels, translationData, ref captured);
					continue;
				}
				int num = IndexOfUnescapedEquals(text);
				if (num >= 0)
				{
					string text2 = Unescape(text.Substring(0, num));
					string value = Unescape(text.Substring(num + 1));
					if (text2.Length != 0)
					{
						translationData.Exact[text2] = value;
						CaptureLevels(levels, translationData, ref captured);
					}
				}
			}
			return translationData;
		}

		private static void ApplyDirective(string line, HashSet<int> levels)
		{
			string[] array = line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length < 3)
			{
				return;
			}
			string text = array[0].ToLowerInvariant();
			if (array[1].ToLowerInvariant() != "level")
			{
				return;
			}
			for (int i = 2; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ',' });
				for (int j = 0; j < array2.Length; j++)
				{
					if (int.TryParse(array2[j], out var result))
					{
						if (text == "#set")
						{
							levels.Add(result);
						}
						else if (text == "#unset")
						{
							levels.Remove(result);
						}
					}
				}
			}
		}

		private static void CaptureLevels(HashSet<int> levels, TranslationData data, ref bool captured)
		{
			if (!captured)
			{
				captured = true;
				if (levels.Count > 0)
				{
					data.Levels = new List<int>(levels).ToArray();
				}
			}
		}

		private static void ParseRegex(string line, string prefix, Dictionary<string, string> target)
		{
			int num = line.IndexOf(QuoteEq, prefix.Length);
			if (num >= 0)
			{
				string text = Unescape(line.Substring(prefix.Length, num - prefix.Length));
				string text2 = line.Substring(num + QuoteEq.Length);
				if (text2.Length >= 2 && text2[0] == '"' && text2[text2.Length - 1] == '"')
				{
					text2 = text2.Substring(1, text2.Length - 2);
				}
				text2 = Unescape(text2);
				if (text.Length != 0)
				{
					target[text] = text2;
				}
			}
		}

		private static int IndexOfUnescapedEquals(string s)
		{
			for (int i = 0; i < s.Length; i++)
			{
				if (s[i] == '=' && (i == 0 || s[i - 1] != '\\'))
				{
					return i;
				}
			}
			return -1;
		}

		private static string Unescape(string s)
		{
			return s.Replace(EscapedEq, "=");
		}
	}
	public static class PngWriter
	{
		private static readonly byte[] Signature = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 };

		private static readonly uint[] CrcTable = BuildCrcTable();

		public static byte[] EncodeRgba32(byte[] rgba, int width, int height, bool bottomUp)
		{
			if (rgba == null)
			{
				throw new ArgumentNullException("rgba");
			}
			if (width <= 0 || height <= 0)
			{
				throw new ArgumentException("invalid dimensions");
			}
			if (rgba.Length != width * height * 4)
			{
				throw new ArgumentException($"pixel buffer {rgba.Length} != {width}x{height}x4");
			}
			int num = width * 4;
			byte[] array = new byte[height * (1 + num)];
			for (int i = 0; i < height; i++)
			{
				int num2 = (bottomUp ? (height - 1 - i) : i);
				int num3 = i * (1 + num);
				array[num3] = 0;
				Buffer.BlockCopy(rgba, num2 * num, array, num3 + 1, num);
			}
			using MemoryStream memoryStream = new MemoryStream();
			memoryStream.Write(Signature, 0, Signature.Length);
			byte[] array2 = new byte[13];
			WriteBE32(array2, 0, width);
			WriteBE32(array2, 4, height);
			array2[8] = 8;
			array2[9] = 6;
			array2[10] = 0;
			array2[11] = 0;
			array2[12] = 0;
			WriteChunk(memoryStream, "IHDR", array2);
			WriteChunk(memoryStream, "IDAT", ZlibDeflate(array));
			WriteChunk(memoryStream, "IEND", Array.Empty<byte>());
			return memoryStream.ToArray();
		}

		private static byte[] ZlibDeflate(byte[] data)
		{
			using MemoryStream memoryStream = new MemoryStream();
			memoryStream.WriteByte(120);
			memoryStream.WriteByte(1);
			using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Fastest, leaveOpen: true))
			{
				deflateStream.Write(data, 0, data.Length);
			}
			uint num = Adler32(data);
			memoryStream.WriteByte((byte)((num >> 24) & 0xFF));
			memoryStream.WriteByte((byte)((num >> 16) & 0xFF));
			memoryStream.WriteByte((byte)((num >> 8) & 0xFF));
			memoryStream.WriteByte((byte)(num & 0xFF));
			return memoryStream.ToArray();
		}

		private static void WriteChunk(Stream s, string type, byte[] data)
		{
			byte[] array = new byte[4];
			WriteBE32(array, 0, data.Length);
			s.Write(array, 0, 4);
			byte[] bytes = Encoding.ASCII.GetBytes(type);
			s.Write(bytes, 0, 4);
			if (data.Length != 0)
			{
				s.Write(data, 0, data.Length);
			}
			uint value = Crc32(bytes, data);
			byte[] array2 = new byte[4];
			WriteBE32(array2, 0, (int)value);
			s.Write(array2, 0, 4);
		}

		private static void WriteBE32(byte[] buf, int offset, int value)
		{
			buf[offset] = (byte)((value >> 24) & 0xFF);
			buf[offset + 1] = (byte)((value >> 16) & 0xFF);
			buf[offset + 2] = (byte)((value >> 8) & 0xFF);
			buf[offset + 3] = (byte)(value & 0xFF);
		}

		private static uint Adler32(byte[] data)
		{
			uint num = 1u;
			uint num2 = 0u;
			foreach (byte b in data)
			{
				num = (num + b) % 65521;
				num2 = (num2 + num) % 65521;
			}
			return (num2 << 16) | num;
		}

		private static uint[] BuildCrcTable()
		{
			uint[] array = new uint[256];
			for (uint num = 0u; num < 256; num++)
			{
				uint num2 = num;
				for (int i = 0; i < 8; i++)
				{
					num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
				}
				array[num] = num2;
			}
			return array;
		}

		private static uint Crc32(byte[] type, byte[] data)
		{
			uint num = uint.MaxValue;
			byte[] array = type;
			foreach (byte b in array)
			{
				num = CrcTable[(num ^ b) & 0xFF] ^ (num >> 8);
			}
			array = data;
			foreach (byte b2 in array)
			{
				num = CrcTable[(num ^ b2) & 0xFF] ^ (num >> 8);
			}
			return num ^ 0xFFFFFFFFu;
		}
	}
	public sealed class ReloadableScaleState
	{
		private const float Epsilon = 0.001f;

		private float _baseSize;

		private float _lastApplied;

		private int _generation = -1;

		private bool _initialized;

		public bool NeedsApply(int generation)
		{
			if (_initialized)
			{
				return _generation != generation;
			}
			return true;
		}

		public float BeginApply(int generation, float current)
		{
			if (!_initialized || Math.Abs(current - _lastApplied) > 0.001f)
			{
				_baseSize = current;
			}
			_generation = generation;
			_initialized = true;
			return _baseSize;
		}

		public void CompleteApply(float applied)
		{
			_lastApplied = applied;
		}
	}
	public sealed class ReloadDebounce
	{
		private readonly object _gate = new object();

		private volatile bool _pending;

		private double _lastEventSeconds;

		public void NotifyChange(double nowSeconds)
		{
			lock (_gate)
			{
				_lastEventSeconds = nowSeconds;
				_pending = true;
			}
		}

		public bool TryConsume(double nowSeconds, double quietSeconds)
		{
			if (!_pending)
			{
				return false;
			}
			lock (_gate)
			{
				if (!_pending)
				{
					return false;
				}
				if (nowSeconds - _lastEventSeconds < quietSeconds)
				{
					return false;
				}
				_pending = false;
				return true;
			}
		}
	}
	public static class ResizerTxtParser
	{
		public static Dictionary<string, string> Parse(IEnumerable<string> lines)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (string line in lines)
			{
				if (string.IsNullOrEmpty(line))
				{
					continue;
				}
				string text = line.TrimEnd('\r', '\n');
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string text2 = text.Substring(0, num).Trim();
					string text3 = text.Substring(num + 1).Trim();
					if (text2.Length != 0 && text3.Length != 0)
					{
						dictionary[text2] = text3;
					}
				}
			}
			return dictionary;
		}
	}
	public sealed class RetirementQueue<T>
	{
		private readonly Queue<T> _items = new Queue<T>();

		public int Count => _items.Count;

		public void Enqueue(T item)
		{
			_items.Enqueue(item);
		}

		public int Drain(int maxItems, Action<T> retire)
		{
			if (maxItems <= 0 || retire == null)
			{
				return 0;
			}
			int num = Math.Min(maxItems, _items.Count);
			for (int i = 0; i < num; i++)
			{
				retire(_items.Dequeue());
			}
			return num;
		}
	}
	public static class ScanInterval
	{
		public static bool ShouldRefresh(float now, float lastRefresh, float intervalSeconds)
		{
			if (intervalSeconds <= 0f)
			{
				return false;
			}
			return now - lastRefresh > intervalSeconds;
		}
	}
	public static class SceneLogFormatter
	{
		public static IEnumerable<string> FormatBuildScenes(IEnumerable<(int Index, string Path)> scenes)
		{
			yield return "Build scenes:";
			foreach (var scene in scenes)
			{
				string text = Path.GetFileNameWithoutExtension(scene.Path);
				if (string.IsNullOrWhiteSpace(text))
				{
					text = "<unknown>";
				}
				yield return $"  {scene.Index}: {text}";
			}
		}
	}
	public static class SpriteGeometry
	{
		public static (float X, float Y) NormalizedPivot(float pivotPxX, float pivotPxY, float rectW, float rectH)
		{
			if (rectW <= 0f || rectH <= 0f)
			{
				return (X: 0.5f, Y: 0.5f);
			}
			return (X: pivotPxX / rectW, Y: pivotPxY / rectH);
		}

		public static float MatchedPpu(float origPpu, float origW, float newW)
		{
			if (!(origPpu <= 0f) && !(origW <= 0f))
			{
				return origPpu * (newW / origW);
			}
			return 100f;
		}

		public static (float L, float B, float R, float T) ScaleBorder(float l, float b, float r, float t, float origW, float origH, float newW, float newH)
		{
			if (origW <= 0f || origH <= 0f)
			{
				return (L: 0f, B: 0f, R: 0f, T: 0f);
			}
			float num = newW / origW;
			float num2 = newH / origH;
			float num3 = l * num;
			float num4 = r * num;
			float num5 = b * num2;
			float num6 = t * num2;
			if (num3 + num4 > newW && num3 + num4 > 0f)
			{
				float num7 = newW / (num3 + num4);
				num3 *= num7;
				num4 *= num7;
			}
			if (num5 + num6 > newH && num5 + num6 > 0f)
			{
				float num8 = newH / (num5 + num6);
				num5 *= num8;
				num6 *= num8;
			}
			return (L: num3, B: num5, R: num4, T: num6);
		}
	}
	public static class TextNormalizer
	{
		private const char Cr = '\r';

		private const char Lf = '\n';

		private const char Backslash = '\\';

		private const char Nul = '\0';

		private const char ZeroWidth = '\u200b';

		private const char Nbsp = '\u00a0';

		public static string CanonicalKey(string? str)
		{
			if (string.IsNullOrEmpty(str))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(str.Length);
			for (int i = 0; i < str.Length; i++)
			{
				char c = str[i];
				switch (c)
				{
				case '\r':
					if (i + 1 < str.Length && str[i + 1] == '\n')
					{
						i++;
					}
					stringBuilder.Append('\\');
					stringBuilder.Append('n');
					break;
				case '\n':
					stringBuilder.Append('\\');
					stringBuilder.Append('n');
					break;
				default:
					stringBuilder.Append(c);
					break;
				case '\0':
				case '\u00a0':
				case '\u200b':
					break;
				}
			}
			return stringBuilder.ToString().Replace("<noparse></noparse>", "").Trim();
		}
	}
	public static class TextureNameNormalizer
	{
		private static readonly Regex HashSuffix = new Regex("\\s*-?\\[[0-9A-Fa-f]+-[0-9A-Fa-f]+\\]$", RegexOptions.Compiled);

		public static string GetOriginalName(string fileNameNoExt)
		{
			if (string.IsNullOrEmpty(fileNameNoExt))
			{
				return fileNameNoExt;
			}
			return HashSuffix.Replace(fileNameNoExt, "").TrimEnd(Array.Empty<char>());
		}
	}
	public static class TextureSlots
	{
		public static IReadOnlyList<string> Allowed { get; } = new string[3] { "_MainTex", "_BaseMap", "_BaseColorMap" };

		public static bool ShouldDump(int width, int height, int maxSize)
		{
			if (maxSize > 0)
			{
				if (width <= maxSize)
				{
					return height <= maxSize;
				}
				return false;
			}
			return true;
		}

		public static IEnumerable<string> FilterSlots(IEnumerable<string> materialPropertyNames)
		{
			HashSet<string> hashSet = new HashSet<string>(materialPropertyNames);
			return Allowed.Where(hashSet.Contains);
		}
	}
	public static class ThirdPartyTextTranslator
	{
		private static readonly Regex Cyrillic = new Regex("[\\u0400-\\u04FF]", RegexOptions.Compiled);

		public static string TranslateOrCapture(string s, Func<string, string?> resolve, Action<string> capture)
		{
			if (string.IsNullOrWhiteSpace(s) || Cyrillic.IsMatch(s))
			{
				return s;
			}
			string text = resolve(s);
			if (text == null)
			{
				string text2 = s.ToLowerInvariant();
				if (text2 != s)
				{
					text = resolve(text2);
				}
			}
			if (text != null)
			{
				return text;
			}
			capture(s);
			return s;
		}
	}
	public sealed class TranslationData
	{
		public int[] Levels { get; set; } = new int[0];

		public string? FileFontOverride { get; set; }

		public Dictionary<string, string> Exact { get; set; } = new Dictionary<string, string>();

		public Dictionary<string, string> Regex { get; set; } = new Dictionary<string, string>();

		public Dictionary<string, string> SplitterRegex { get; set; } = new Dictionary<string, string>();

		public Dictionary<string, string> StringFontOverride { get; set; } = new Dictionary<string, string>();
	}
	public sealed class TranslationStore
	{
		private sealed class Bucket
		{
			public readonly Dictionary<string, string> Exact = new Dictionary<string, string>(StringComparer.Ordinal);

			public readonly List<(Regex Rx, string Repl, string Anchor)> Regex = new List<(Regex, string, string)>();

			public readonly List<(Regex Rx, string Repl, string Anchor)> Splitters = new List<(Regex, string, string)>();

			public readonly List<(Regex Rx, string Repl, List<string> Ph, string Anchor)> Templates = new List<(Regex, string, List<string>, string)>();

			public readonly List<(Regex Rx, string Font)> FontRegex = new List<(Regex, string)>();

			public readonly List<(Regex Rx, string Font)> FontSplitters = new List<(Regex, string)>();

			public readonly Dictionary<string, string> FontExactByKey = new Dictionary<string, string>(StringComparer.Ordinal);

			public readonly Dictionary<string, string> FontExactByValue = new Dictionary<string, string>(StringComparer.Ordinal);

			public readonly HashSet<string> AutoSize = new HashSet<string>(StringComparer.Ordinal);

			public readonly List<Regex> AutoSizePatterns = new List<Regex>();

			public readonly Dictionary<string, float> TextSize = new Dictionary<string, float>(StringComparer.Ordinal);

			public readonly List<(Regex Rx, float Size)> TextSizePatterns = new List<(Regex, float)>();
		}

		private readonly struct PrefixMeta
		{
			public string Val { get; }

			public bool AutoSize { get; }

			public float? TextSize { get; }

			public PrefixMeta(string val, bool autoSize, float? textSize)
			{
				Val = val;
				AutoSize = autoSize;
				TextSize = textSize;
			}
		}

		private static readonly TimeSpan DefaultRegexTimeout = TimeSpan.FromMilliseconds(50.0);

		private static readonly string LiteralNl = "\\n";

		private static readonly string RealNl = '\n'.ToString();

		private const string AutoSizePrefix = "autosize:";

		private const string SetSizePrefix = "setsize:";

		private static readonly Regex SetSizePrefixRegex = new Regex("^setsize:([0-9]+(?:[.,][0-9]+)?):", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

		public const int Global = int.MinValue;

		private readonly Dictionary<int, Bucket> _buckets = new Dictionary<int, Bucket>();

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

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

		private readonly Dictionary<string, Regex> _rxCache = new Dictionary<string, Regex>(StringComparer.Ordinal);

		private readonly HashSet<Regex> _disabledRegex = new HashSet<Regex>();

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

		private readonly TimeSpan _regexTimeout;

		private readonly Action<string>? _warning;

		private const int ResolveCacheCap = 10000;

		private readonly Dictionary<string, string?> _resolveCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, string?> _fontCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, (float? Size, bool AutoSize)> _directiveCache = new Dictionary<string, (float?, bool)>(StringComparer.Ordinal);

		private int _currentLevel = int.MinValue;

		public int CurrentLevel
		{
			get
			{
				return _currentLevel;
			}
			set
			{
				if (_currentLevel != value)
				{
					_currentLevel = value;
					InvalidateCaches();
				}
			}
		}

		public int ExactCount
		{
			get
			{
				int num = 0;
				foreach (Bucket value in _buckets.Values)
				{
					num += value.Exact.Count + value.Templates.Count;
				}
				return num;
			}
		}

		public int RegexCount
		{
			get
			{
				int num = 0;
				foreach (Bucket value in _buckets.Values)
				{
					num += value.Regex.Count;
				}
				return num;
			}
		}

		public int CompiledPatternCount => _rxCache.Count;

		public TranslationStore(TimeSpan? regexTimeout = null, Action<string>? warning = null)
		{
			_regexTimeout = regexTimeout ?? DefaultRegexTimeout;
			_warning = warning;
		}

		private static string ToRealNl(string s)
		{
			if (s.IndexOf(LiteralNl, StringComparison.Ordinal) >= 0)
			{
				return s.Replace(LiteralNl, RealNl);
			}
			return s;
		}

		private Bucket B(int level)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				value = new Bucket();
				_buckets[level] = value;
			}
			return value;
		}

		private Regex GetCompiled(string pattern)
		{
			if (!_rxCache.TryGetValue(pattern, out Regex value))
			{
				value = new Regex(pattern, RegexOptions.Compiled | RegexOptions.CultureInvariant, _regexTimeout);
				_rxCache[pattern] = value;
			}
			return value;
		}

		public void Add(TranslationData data, bool scoping = true)
		{
			if (data == null)
			{
				return;
			}
			InvalidateCaches();
			int[] array = ((scoping && data.Levels != null && data.Levels.Length != 0) ? data.Levels : new int[1] { -2147483648 });
			int[] array2 = array;
			foreach (int level in array2)
			{
				Bucket bucket = B(level);
				foreach (KeyValuePair<string, string> item in data.Exact)
				{
					string text = TextNormalizer.CanonicalKey(item.Key);
					if (text.Length == 0 || string.IsNullOrEmpty(item.Value))
					{
						continue;
					}
					PrefixMeta prefixMeta = StripCommandPrefixes(item.Value);
					if (Regex.IsMatch(text, "\\{\\d+\\}"))
					{
						AddTemplateRule(bucket, text, prefixMeta.Val, prefixMeta.AutoSize, prefixMeta.TextSize);
						_knownSourceKeys.Add(text);
						continue;
					}
					bucket.Exact[text] = prefixMeta.Val;
					_knownSourceKeys.Add(text);
					_knownResultKeys.Add(TextNormalizer.CanonicalKey(prefixMeta.Val));
					if (prefixMeta.AutoSize)
					{
						bucket.AutoSize.Add(text);
					}
					if (prefixMeta.TextSize.HasValue)
					{
						bucket.TextSize[text] = prefixMeta.TextSize.Value;
					}
				}
				foreach (KeyValuePair<string, string> item2 in data.Regex)
				{
					if (string.IsNullOrEmpty(item2.Key))
					{
						continue;
					}
					try
					{
						PrefixMeta prefixMeta2 = StripCommandPrefixes(item2.Value ?? string.Empty);
						Regex compiled = GetCompiled(item2.Key);
						bucket.Regex.Add((compiled, prefixMeta2.Val, AnchorExtractor.FromRegex(item2.Key)));
						_knownSourceKeys.Add(TextNormalizer.CanonicalKey(item2.Key));
						if (prefixMeta2.AutoSize)
						{
							bucket.AutoSizePatterns.Add(compiled);
						}
						if (prefixMeta2.TextSize.HasValue)
						{
							bucket.TextSizePatterns.Add((compiled, prefixMeta2.TextSize.Value));
						}
					}
					catch (Exception e)
					{
						WarnInvalidPattern(item2.Key, e);
					}
				}
				if (data.SplitterRegex == null)
				{
					continue;
				}
				foreach (KeyValuePair<string, string> item3 in data.SplitterRegex)
				{
					if (string.IsNullOrEmpty(item3.Key))
					{
						continue;
					}
					try
					{
						PrefixMeta prefixMeta3 = StripCommandPrefixes(item3.Value ?? string.Empty);
						Regex compiled2 = GetCompiled(AnchorSplitterPattern(item3.Key));
						bucket.Splitters.Add((compiled2, prefixMeta3.Val, AnchorExtractor.FromRegex(item3.Key)));
						_knownSourceKeys.Add(TextNormalizer.CanonicalKey(item3.Key));
						if (prefixMeta3.AutoSize)
						{
							bucket.AutoSizePatterns.Add(compiled2);
						}
						if (prefixMeta3.TextSize.HasValue)
						{
							bucket.TextSizePatterns.Add((compiled2, prefixMeta3.TextSize.Value));
						}
					}
					catch (Exception e2)
					{
						WarnInvalidPattern(item3.Key, e2);
					}
				}
			}
			string fileFontOverride = data.FileFontOverride;
			if (!string.IsNullOrEmpty(fileFontOverride))
			{
				foreach (KeyValuePair<string, string> item4 in data.Exact)
				{
					RegisterExactFontOverride(array, item4.Key, item4.Value, fileFontOverride);
				}
				RegisterRegexFontOverrides(array, data.Regex, fileFontOverride, splitter: false);
				RegisterRegexFontOverrides(array, data.SplitterRegex, fileFontOverride, splitter: true);
			}
			if (data.StringFontOverride == null)
			{
				return;
			}
			foreach (KeyValuePair<string, string> item5 in data.StringFontOverride)
			{
				if (TextNormalizer.CanonicalKey(item5.Key).Length != 0 && !string.IsNullOrEmpty(item5.Value))
				{
					data.Exact.TryGetValue(item5.Key, out string value);
					RegisterExactFontOverride(array, item5.Key, value ?? string.Empty, item5.Value);
					if (data.Regex != null && data.Regex.ContainsKey(item5.Key))
					{
						RegisterRegexFontOverride(array, item5.Key, item5.Value, splitter: false);
					}
					if (data.SplitterRegex != null && data.SplitterRegex.ContainsKey(item5.Key))
					{
						RegisterRegexFontOverride(array, item5.Key, item5.Value, splitter: true);
					}
				}
			}
		}

		private void InvalidateCaches()
		{
			_resolveCache.Clear();
			_fontCache.Clear();
			_directiveCache.Clear();
		}

		private void RegisterExactFontOverride(int[] levels, string sourceKey, string translatedValue, string fontName)
		{
			string text = TextNormalizer.CanonicalKey(sourceKey);
			if (text.Length == 0 || string.IsNullOrEmpty(fontName))
			{
				return;
			}
			string text2 = (string.IsNullOrEmpty(translatedValue) ? string.Empty : TextNormalizer.CanonicalKey(StripCommandPrefixes(translatedValue).Val));
			foreach (int level in levels)
			{
				Bucket bucket = B(level);
				bucket.FontExactByKey[text] = fontName;
				string text3 = text2;
				if (text3.Length == 0 && bucket.Exact.TryGetValue(text, out string value))
				{
					text3 = TextNormalizer.CanonicalKey(value);
				}
				if (text3.Length > 0)
				{
					bucket.FontExactByValue[text3] = fontName;
				}
			}
		}

		private void RegisterRegexFontOverrides(int[] levels, Dictionary<string, string>? patterns, string fontName, bool splitter)
		{
			if (patterns == null)
			{
				return;
			}
			foreach (KeyValuePair<string, string> pattern in patterns)
			{
				RegisterRegexFontOverride(levels, pattern.Key, fontName, splitter);
			}
		}

		private void RegisterRegexFontOverride(int[] levels, string pattern, string fontName, bool splitter)
		{
			if (string.IsNullOrEmpty(pattern) || string.IsNullOrEmpty(fontName))
			{
				return;
			}
			try
			{
				string pattern2 = (splitter ? AnchorSplitterPattern(pattern) : pattern);
				Regex compiled = GetCompiled(pattern2);
				foreach (int level in levels)
				{
					Bucket bucket = B(level);
					if (splitter)
					{
						bucket.FontSplitters.Add((compiled, fontName));
					}
					else
					{
						bucket.FontRegex.Add((compiled, fontName));
					}
				}
			}
			catch (Exception e)
			{
				WarnInvalidPattern(pattern, e);
			}
		}

		private static string AnchorSplitterPattern(string pattern)
		{
			string text = pattern;
			if (!text.StartsWith("^"))
			{
				text = "^" + text;
			}
			if (!text.EndsWith("$"))
			{
				text += "$";
			}
			return text;
		}

		private void AddTemplateRule(Bucket bucket, string key, string val, bool autoSize, float? textSize)
		{
			List<string> list = new List<string>();
			foreach (Match item in Regex.Matches(key, "\\{(\\d+)\\}"))
			{
				list.Add(item.Groups[1].Value);
			}
			string input = Regex.Escape(key);
			input = Regex.Replace(input, "\\\\\\{\\d+\\}", "([+-]?\\d+(?:[.,]\\d+)?)");
			input = "^" + input + "$";
			try
			{
				Regex compiled = GetCompiled(input);
				bucket.Templates.Add((compiled, val, list, AnchorExtractor.FromTemplateKey(key)));
				if (autoSize)
				{
					bucket.AutoSizePatterns.Add(compiled);
				}
				if (textSize.HasValue)
				{
					bucket.TextSizePatterns.Add((compiled, textSize.Value));
				}
			}
			catch (Exception e)
			{
				WarnInvalidPattern(input, e);
			}
		}

		private static PrefixMeta StripCommandPrefixes(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return new PrefixMeta(value, autoSize: false, null);
			}
			bool autoSize = false;
			float? textSize = null;
			string text = value;
			while (!string.IsNullOrEmpty(text))
			{
				if (text.StartsWith("autosize:", StringComparison.OrdinalIgnoreCase))
				{
					autoSize = true;
					text = text.Substring("autosize:".Length);
					continue;
				}
				if (!text.StartsWith("setsize:", StringComparison.OrdinalIgnoreCase))
				{
					break;
				}
				Match match = SetSizePrefixRegex.Match(text);
				if (!match.Success || !float.TryParse(match.Groups[1].Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || result <= 0f)
				{
					break;
				}
				textSize = result;
				text = text.Substring(match.Length);
			}
			return new PrefixMeta(text, autoSize, textSize);
		}

		public string? Resolve(string? current)
		{
			if (string.IsNullOrEmpty(current))
			{
				return null;
			}
			string key = TextNormalizer.CanonicalKey(current);
			if (_resolveCache.TryGetValue(key, out string value))
			{
				return value;
			}
			string text = ResolveByKey(key, useSplitter: true);
			if (_resolveCache.Count >= 10000)
			{
				_resolveCache.Clear();
			}
			_resolveCache[key] = text;
			return text;
		}

		private string? Resolve(string? current, bool useSplitter)
		{
			if (string.IsNullOrEmpty(current))
			{
				return null;
			}
			return ResolveByKey(TextNormalizer.CanonicalKey(current), useSplitter);
		}

		private string? ResolveByKey(string key, bool useSplitter)
		{
			string rxText = ToRealNl(key);
			string text = ExactStage(CurrentLevel, key);
			if (text == null && CurrentLevel != int.MinValue)
			{
				text = ExactStage(int.MinValue, key);
			}
			if (text != null)
			{
				return text;
			}
			text = RegexStage(CurrentLevel, rxText);
			if (text == null && CurrentLevel != int.MinValue)
			{
				text = RegexStage(int.MinValue, rxText);
			}
			if (text != null)
			{
				return text;
			}
			if (useSplitter)
			{
				text = SplitterStage(CurrentLevel, rxText);
				if (text == null && CurrentLevel != int.MinValue)
				{
					text = SplitterStage(int.MinValue, rxText);
				}
			}
			return text;
		}

		private string? ExactStage(int level, string key)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			if (value.Exact.TryGetValue(key, out string value2))
			{
				return ToRealNl(value2);
			}
			foreach (var (rx, text, list, text2) in value.Templates)
			{
				if ((text2.Length <= 0 || key.IndexOf(text2, StringComparison.Ordinal) >= 0) && TryMatch(rx, key, out Match match))
				{
					string text3 = text;
					for (int i = 1; i < match.Groups.Count; i++)
					{
						text3 = text3.Replace("{" + list[i - 1] + "}", match.Groups[i].Value);
					}
					return ToRealNl(text3);
				}
			}
			return null;
		}

		private string? RegexStage(int level, string rxText)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			foreach (var (rx, replacement, text) in value.Regex)
			{
				if ((text.Length <= 0 || rxText.IndexOf(text, StringComparison.Ordinal) >= 0) && TryMatch(rx, rxText, out Match match))
				{
					return ToRealNl(match.Result(replacement));
				}
			}
			return null;
		}

		private string? SplitterStage(int level, string rxText)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			foreach (var (rx, template, text) in value.Splitters)
			{
				if ((text.Length <= 0 || rxText.IndexOf(text, StringComparison.Ordinal) >= 0) && TryMatch(rx, rxText, out Match match))
				{
					return ToRealNl(ApplySplitter(match, template));
				}
			}
			return null;
		}

		private string ApplySplitter(Match m, string template)
		{
			StringBuilder stringBuilder = new StringBuilder(template);
			for (int num = m.Groups.Count - 1; num >= 1; num--)
			{
				Group obj = m.Groups[num];
				string oldValue = "$" + num;
				if (!obj.Success)
				{
					stringBuilder.Replace(oldValue, string.Empty);
				}
				else
				{
					string value = obj.Value;
					string newValue = Resolve(value, useSplitter: false) ?? value;
					stringBuilder.Replace(oldValue, newValue);
				}
			}
			return stringBuilder.ToString();
		}

		public string? GetFont(string? key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			string text = TextNormalizer.CanonicalKey(key);
			if (_fontCache.TryGetValue(text, out string value))
			{
				return value;
			}
			string fontByKey = GetFontByKey(text);
			if (_fontCache.Count >= 10000)
			{
				_fontCache.Clear();
			}
			_fontCache[text] = fontByKey;
			return fontByKey;
		}

		private string? GetFontByKey(string normalized)
		{
			string text = FontExactStage(CurrentLevel, normalized);
			if (text == null && CurrentLevel != int.MinValue)
			{
				text = FontExactStage(int.MinValue, normalized);
			}
			if (text != null)
			{
				return text;
			}
			string rxText = ToRealNl(normalized);
			text = FontRegexStage(CurrentLevel, rxText);
			if (text == null && CurrentLevel != int.MinValue)
			{
				text = FontRegexStage(int.MinValue, rxText);
			}
			return text;
		}

		private string? FontExactStage(int level, string normalized)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			if (value.FontExactByKey.TryGetValue(normalized, out string value2))
			{
				return value2;
			}
			if (!value.FontExactByValue.TryGetValue(normalized, out value2))
			{
				return null;
			}
			return value2;
		}

		private string? FontRegexStage(int level, string rxText)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			for (int num = value.FontRegex.Count - 1; num >= 0; num--)
			{
				if (IsMatchSafe(value.FontRegex[num].Rx, rxText))
				{
					return value.FontRegex[num].Font;
				}
			}
			for (int num2 = value.FontSplitters.Count - 1; num2 >= 0; num2--)
			{
				if (IsMatchSafe(value.FontSplitters[num2].Rx, rxText))
				{
					return value.FontSplitters[num2].Font;
				}
			}
			return null;
		}

		public bool KnowsText(string? text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			return KnowsCanonical(TextNormalizer.CanonicalKey(text));
		}

		public bool KnowsCanonical(string? canonicalKey)
		{
			if (!string.IsNullOrEmpty(canonicalKey))
			{
				if (!_knownSourceKeys.Contains(canonicalKey))
				{
					return _knownResultKeys.Contains(canonicalKey);
				}
				return true;
			}
			return false;
		}

		public bool RequiresAutoSize(string? original)
		{
			if (string.IsNullOrEmpty(original))
			{
				return false;
			}
			return GetDirectives(TextNormalizer.CanonicalKey(original)).AutoSize;
		}

		private bool AutoSizeIn(int level, string key)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return false;
			}
			if (value.AutoSize.Contains(key))
			{
				return true;
			}
			string input = ToRealNl(key);
			foreach (Regex autoSizePattern in value.AutoSizePatterns)
			{
				if (IsMatchSafe(autoSizePattern, key) || IsMatchSafe(autoSizePattern, input))
				{
					return true;
				}
			}
			return false;
		}

		public float? GetTextSize(string? original)
		{
			if (string.IsNullOrEmpty(original))
			{
				return null;
			}
			return GetDirectives(TextNormalizer.CanonicalKey(original)).Size;
		}

		private (float? Size, bool AutoSize) GetDirectives(string key)
		{
			if (_directiveCache.TryGetValue(key, out (float?, bool) value))
			{
				return value;
			}
			float? item = TextSizeIn(CurrentLevel, key);
			if (!item.HasValue && CurrentLevel != int.MinValue)
			{
				item = TextSizeIn(int.MinValue, key);
			}
			bool item2 = AutoSizeIn(CurrentLevel, key) || (CurrentLevel != int.MinValue && AutoSizeIn(int.MinValue, key));
			value = (item, item2);
			if (_directiveCache.Count >= 10000)
			{
				_directiveCache.Clear();
			}
			_directiveCache[key] = value;
			return value;
		}

		private float? TextSizeIn(int level, string key)
		{
			if (!_buckets.TryGetValue(level, out Bucket value))
			{
				return null;
			}
			if (value.TextSize.TryGetValue(key, out var value2))
			{
				return value2;
			}
			string input = ToRealNl(key);
			foreach (var (rx, value3) in value.TextSizePatterns)
			{
				if (IsMatchSafe(rx, key) || IsMatchSafe(rx, input))
				{
					return value3;
				}
			}
			return null;
		}

		private bool TryMatch(Regex rx, string input, out Match match)
		{
			match = Match.Empty;
			if (_disabledRegex.Count > 0 && _disabledRegex.Contains(rx))
			{
				return false;
			}
			try
			{
				match = rx.Match(input);
				return match.Success;
			}
			catch (RegexMatchTimeoutException)
			{
				_disabledRegex.Add(rx);
				WarnOnce("timeout:" + rx, $"Regex timed out and was disabled: {rx}");
				return false;
			}
		}

		private bool IsMatchSafe(Regex rx, string input)
		{
			Match match;
			return TryMatch(rx, input, out match);
		}

		private void WarnInvalidPattern(string pattern, Exception e)
		{
			WarnOnce("invalid:" + pattern, "Invalid regex skipped: " + pattern + " (" + e.Message + ")");
		}

		private void WarnOnce(string key, string message)
		{
			if (_warning != null && _warnedPatterns.Add(key))
			{
				_warning(message);
			}
		}
	}
	public static class TranslationWatchFilter
	{
		private const char Backslash = '\\';

		public static bool ShouldTrigger(string? fullPath)
		{
			if (string.IsNullOrEmpty(fullPath))
			{
				return false;
			}
			string extension = Path.GetExtension(fullPath);
			if (!extension.Equals(".json", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".png", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (Path.GetFileName(fullPath).Equals("_AutoGenerated.json", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (fullPath.Replace('\\', '/').IndexOf("/Texture_Dump/", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return false;
			}
			return true;
		}
	}
	public sealed class VersionedTextValue
	{
		private string _text = string.Empty;

		private string _canonical = string.Empty;

		private int _generation = -1;

		public void Set(string text, string canonical, int generation)
		{
			_text = text;
			_canonical = canonical;
			_generation = generation;
		}

		public bool Matches(string text, int generation)
		{
			if (generation == _generation)
			{
				if (!(_text == text))
				{
					return _canonical == TextNormalizer.CanonicalKey(text);
				}
				return true;
			}
			return false;
		}
	}
}

plugins/WKLoc/WKLoc.dll

Decompiled a month ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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 System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Febucci.UI;
using Febucci.UI.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WKLoc.Apply;
using WKLoc.Assets;
using WKLoc.Collect;
using WKLoc.Core;
using WKLoc.Layout;
using WKLoc.Loc;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[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 WKLoc
{
	internal static class Diagnostics
	{
		private static readonly HashSet<string> Warned = new HashSet<string>();

		public static void WarnOnce(string key, string message)
		{
			if (Warned.Add(key))
			{
				Debug.LogWarning((object)("[WKLoc] " + message));
			}
		}
	}
	[BepInPlugin("WKLoc", "WKLoc", "1.0.0")]
	public sealed class WkLocPlugin : BaseUnityPlugin
	{
		public const string Guid = "WKLoc";

		public static WkLocPlugin Instance;

		public static PluginConfig Cfg;

		public static TranslationStore Store;

		private Harmony _harmony;

		private static bool _applicationQuitting;

		public ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		public string PluginDir => Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);

		public string LangDir => Path.Combine(PluginDir, "Translation", Cfg.Language.Value);

		private void Awake()
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			Instance = this;
			Cfg = new PluginConfig(((BaseUnityPlugin)this).Config);
			Application.quitting += ShutdownApplication;
			Store = TranslationLoader.ReadAll(LangDir, ((BaseUnityPlugin)this).Logger, Cfg.EnableTranslationScoping.Value);
			FontProvider.Init(PluginDir, Cfg.OverrideFontTMP.Value, Cfg.OverrideFontLegacy.Value, ((BaseUnityPlugin)this).Logger);
			TextureSwapper.Init(LangDir, ((BaseUnityPlugin)this).Logger);
			UiResizer.Init(LangDir, ((BaseUnityPlugin)this).Logger);
			TextCollector.Enabled = Cfg.EnableTextCollection.Value;
			TextCollector.Init(LangDir);
			TextureCollector.Enabled = Cfg.EnableTextureCollection.Value;
			TextureCollector.MaxSize = Cfg.TextureDumpMaxSize.Value;
			TextureCollector.Init(LangDir);
			SceneManager.sceneLoaded += HandleLoadedScene;
			TranslationStore store = Store;
			Scene activeScene = SceneManager.GetActiveScene();
			store.CurrentLevel = ((Scene)(ref activeScene)).buildIndex;
			_harmony = new Harmony("WKLoc");
			PatchAllIsolated();
			FrameDriver.Install(typeof(WkLocPlugin), Sweeper.Tick);
			if (Cfg.EnableHotkeys.Value && Cfg.EnableAutoReload.Value)
			{
				TranslationWatcher.Install(LangDir);
			}
			foreach (string item in SceneLogFormatter.FormatBuildScenes(GetBuildScenes()))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)item);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("WKLoc loaded. LangDir=" + LangDir));
		}

		private void PatchAllIsolated()
		{
			int num = 0;
			int num2 = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					List<MethodInfo> list = _harmony.CreateClassProcessor(type).Patch();
					if (list != null && list.Count > 0)
					{
						num++;
					}
				}
				catch (Exception ex)
				{
					num2++;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[WKLoc] Patch class FAILED: " + type.FullName + ": " + ex.Message));
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"[WKLoc] Harmony patch classes: {num} ok, {num2} failed.");
		}

		private static IEnumerable<(int Index, string Path)> GetBuildScenes()
		{
			for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
			{
				yield return (Index: i, Path: SceneUtility.GetScenePathByBuildIndex(i));
			}
		}

		private void HandleLoadedScene(Scene scene, LoadSceneMode mode)
		{
			Store.CurrentLevel = ((Scene)(ref scene)).buildIndex;
			TextPipeline.InvalidateTranslationContext();
			Sweeper.RefreshTouchedComponents();
			RendererNet.OnSceneChange();
			TextureScanner.OnSceneChange();
			if (Cfg.DebugLog.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"[WKLoc] Scene loaded: '{((Scene)(ref scene)).name}' buildIndex={((Scene)(ref scene)).buildIndex}");
			}
			if (TextPipeline.Enabled)
			{
				TextureSwapper.ReplaceAllInScene();
			}
			TextureCollector.CollectScene();
			TextCollector.Flush();
		}

		private void OnDestroy()
		{
			TextCollector.Flush();
		}

		private void OnApplicationQuit()
		{
			ShutdownApplication();
		}

		private static void ShutdownApplication()
		{
			if (!_applicationQuitting)
			{
				_applicationQuitting = true;
				TextCollector.Shutdown();
				TextureDumpQueue.Shutdown();
				TranslationWatcher.Shutdown();
				TextureSwapper.Shutdown();
			}
		}
	}
	public sealed class PluginConfig
	{
		public ConfigEntry<string> Language;

		public ConfigEntry<string> OverrideFontTMP;

		public ConfigEntry<string> OverrideFontLegacy;

		public ConfigEntry<bool> EnableTranslationScoping;

		public ConfigEntry<bool> EnableTextCollection;

		public ConfigEntry<bool> EnableTextureCollection;

		public ConfigEntry<int> TextureDumpMaxSize;

		public ConfigEntry<bool> EnablePolling;

		public ConfigEntry<float> RendererScanSeconds;

		public ConfigEntry<bool> EnableHotkeys;

		public ConfigEntry<KeyCode> ToggleKey;

		public ConfigEntry<KeyCode> ReloadKey;

		public ConfigEntry<bool> EnableAutoReload;

		public ConfigEntry<bool> DebugLog;

		public PluginConfig(ConfigFile cfg)
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			Language = cfg.Bind<string>("General", "Language", "ru", "Код языка перевода (папка Translation/<lang>).");
			OverrideFontTMP = cfg.Bind<string>("Fonts", "OverrideFontTMP", "main", "Имя TMP-шрифта из первого font AssetBundle рядом с плагином (пусто = первый/default из бандла).");
			OverrideFontLegacy = cfg.Bind<string>("Fonts", "OverrideFontLegacy", "main-legacy", "Имя legacy-шрифта (uGUI Text) из первого font AssetBundle рядом с плагином (пусто = default/первый).");
			EnableTranslationScoping = cfg.Bind<bool>("General", "EnableTranslationScoping", true, "Привязка переводов к сценам (Levels). Выкл = все переводы глобальные.");
			EnableTextCollection = cfg.Bind<bool>("Collection", "EnableTextCollection", false, "Собирать непереведённый текст в _AutoGenerated.json на ходу.");
			EnableTextureCollection = cfg.Bind<bool>("Collection", "EnableTextureCollection", false, "Дампить невстреченные текстуры в Texture_Dump/ на ходу.");
			TextureDumpMaxSize = cfg.Bind<int>("Collection", "TextureDumpMaxSize", 0, "Макс. сторона текстуры (px) для дампа материалов; 0 = без ограничения.");
			EnablePolling = cfg.Bind<bool>("Apply", "EnablePolling", true, "Добор пропущенного текста/текстур бюджетным сканером (мастер-тумблер).");
			RendererScanSeconds = cfg.Bind<float>("Apply", "RendererScanSeconds", 3f, "Период active-scene скана 3D-рендереров для динамики (сек). 0 = только при смене сцены. Текст и UI-текстуры ловятся хуками без скана.");
			EnableHotkeys = cfg.Bind<bool>("Hotkeys", "EnableHotkeys", false, "Включить горячие клавиши.");
			ToggleKey = cfg.Bind<KeyCode>("Hotkeys", "ToggleTranslation", ParseKey("F6"), "Вкл/выкл перевод.");
			ReloadKey = cfg.Bind<KeyCode>("Hotkeys", "Reload", ParseKey("F7"), "Перезагрузить перевод.");
			EnableAutoReload = cfg.Bind<bool>("Hotkeys", "AutoReloadOnFileChange", true, "Авто-перезагрузка перевода при изменении файлов в папке языка (эквивалент F7; работает только при EnableHotkeys=true).");
			DebugLog = cfg.Bind<bool>("Debug", "LogText", false, "Подробный лог перевода/применения (диагностика).");
		}

		private static KeyCode ParseKey(string key)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return (KeyCode)Enum.Parse(typeof(KeyCode), key);
		}
	}
}
namespace WKLoc.Loc
{
	public static class TranslationLoader
	{
		private static readonly string[] NonTranslation = new string[2] { "interface_resizer.json", "resize_rules.json" };

		public static TranslationStore ReadAll(string langDir, ManualLogSource log, bool scoping)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			TranslationStore val = new TranslationStore((TimeSpan?)null, (Action<string>)delegate(string message)
			{
				log.LogWarning((object)message);
			});
			if (!Directory.Exists(langDir))
			{
				log.LogWarning((object)("Lang dir not found: " + langDir));
				return val;
			}
			string[] files = Directory.GetFiles(langDir, "*.json");
			foreach (string path in files)
			{
				string fileName = Path.GetFileName(path);
				bool flag = false;
				string[] nonTranslation = NonTranslation;
				foreach (string value in nonTranslation)
				{
					if (fileName.Equals(value, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
					}
				}
				if (flag)
				{
					continue;
				}
				try
				{
					TranslationData val2 = JsonConvert.DeserializeObject<TranslationData>(File.ReadAllText(path));
					if (val2 != null)
					{
						val.Add(val2, scoping);
					}
				}
				catch (Exception ex)
				{
					log.LogError((object)("Failed to load " + fileName + ": " + ex.Message));
				}
			}
			log.LogInfo((object)$"Loaded {val.ExactCount} exact, {val.RegexCount} regex from {langDir}");
			return val;
		}
	}
	public static class TranslationWatcher
	{
		private const double QuietSeconds = 0.5;

		private static FileSystemWatcher? _fsw;

		private static readonly ReloadDebounce _debounce = new ReloadDebounce();

		public static void Install(string langDir)
		{
			if (_fsw != null)
			{
				return;
			}
			if (!Directory.Exists(langDir))
			{
				Debug.LogWarning((object)("[WKLoc] Auto-reload watcher skipped: lang dir not found: " + langDir));
				return;
			}
			try
			{
				FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(langDir);
				fileSystemWatcher.IncludeSubdirectories = true;
				fileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
				fileSystemWatcher.Changed += HandleEvent;
				fileSystemWatcher.Created += HandleEvent;
				fileSystemWatcher.Deleted += HandleEvent;
				fileSystemWatcher.Renamed += HandleRenamed;
				fileSystemWatcher.EnableRaisingEvents = true;
				_fsw = fileSystemWatcher;
				Debug.Log((object)("[WKLoc] Auto-reload watcher active: " + langDir));
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[WKLoc] Auto-reload watcher unavailable: " + ex.Message));
				_fsw = null;
			}
		}

		public static bool ConsumePendingReload()
		{
			return _debounce.TryConsume(NowSeconds(), 0.5);
		}

		private static void HandleEvent(object sender, FileSystemEventArgs e)
		{
			Notify(e.FullPath);
		}

		private static void HandleRenamed(object sender, RenamedEventArgs e)
		{
			Notify(e.FullPath);
		}

		private static void Notify(string fullPath)
		{
			if (TranslationWatchFilter.ShouldTrigger(fullPath))
			{
				_debounce.NotifyChange(NowSeconds());
			}
		}

		public static void Shutdown()
		{
			FileSystemWatcher fsw = _fsw;
			_fsw = null;
			if (fsw == null)
			{
				return;
			}
			try
			{
				fsw.EnableRaisingEvents = false;
				fsw.Changed -= HandleEvent;
				fsw.Created -= HandleEvent;
				fsw.Deleted -= HandleEvent;
				fsw.Renamed -= HandleRenamed;
				fsw.Dispose();
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[WKLoc] Auto-reload watcher shutdown failed: " + ex.Message));
			}
		}

		private static double NowSeconds()
		{
			return (double)DateTime.UtcNow.Ticks / 10000000.0;
		}
	}
}
namespace WKLoc.Layout
{
	public sealed class ResizeRule
	{
		public float? scale { get; set; }

		public string? align { get; set; }
	}
	public static class UiResizer
	{
		private static Dictionary<string, ResizeRule> _nameRules = new Dictionary<string, ResizeRule>(StringComparer.Ordinal);

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

		private static KeyValuePair<string, ResizeRule>[] _orderedNameRules = Array.Empty<KeyValuePair<string, ResizeRule>>();

		private static readonly Regex _pct = new Regex("ChangeFontSizeByPercentage\\(([\\d.]+)\\)", RegexOptions.Compiled);

		private static readonly Regex _align = new Regex("TMP_Alignment\\((\\w+)\\)", RegexOptions.Compiled);

		public static void Init(string langDir, ManualLogSource log)
		{
			if (TryRead<Dictionary<string, ResizeRule>>(Path.Combine(langDir, "resize_rules.json"), log, out Dictionary<string, ResizeRule> value))
			{
				_nameRules = value;
				List<KeyValuePair<string, ResizeRule>> list = new List<KeyValuePair<string, ResizeRule>>(_nameRules);
				list.Sort(delegate(KeyValuePair<string, ResizeRule> a, KeyValuePair<string, ResizeRule> b)
				{
					int num = b.Key.Length.CompareTo(a.Key.Length);
					return (num == 0) ? StringComparer.Ordinal.Compare(a.Key, b.Key) : num;
				});
				_orderedNameRules = list.ToArray();
			}
			if (TryRead<Dictionary<string, string>>(Path.Combine(langDir, "interface_resizer.json"), log, out Dictionary<string, string> value2))
			{
				_pathRules = value2;
			}
			log.LogInfo((object)$"Resize: {_nameRules.Count} name rules, {_pathRules.Count} path rules.");
		}

		private static bool TryRead<T>(string path, ManualLogSource log, out T? value) where T : class, new()
		{
			if (!File.Exists(path))
			{
				value = new T();
				return true;
			}
			try
			{
				value = JsonConvert.DeserializeObject<T>(File.ReadAllText(path)) ?? new T();
				return true;
			}
			catch (Exception ex)
			{
				value = null;
				log.LogWarning((object)("Resize rules reload kept previous '" + Path.GetFileName(path) + "': " + ex.Message));
				return false;
			}
		}

		private static (float? scale, string? align) Resolve(string objName, Transform tr)
		{
			float? item = null;
			string item2 = null;
			KeyValuePair<string, ResizeRule>[] orderedNameRules = _orderedNameRules;
			for (int i = 0; i < orderedNameRules.Length; i++)
			{
				KeyValuePair<string, ResizeRule> keyValuePair = orderedNameRules[i];
				if (objName.IndexOf(keyValuePair.Key, StringComparison.Ordinal) >= 0)
				{
					if (keyValuePair.Value.scale.HasValue)
					{
						item = keyValuePair.Value.scale;
					}
					if (!string.IsNullOrEmpty(keyValuePair.Value.align))
					{
						item2 = keyValuePair.Value.align;
					}
					break;
				}
			}
			if (_pathRules.TryGetValue(GetPath(tr), out string value))
			{
				Match match = _pct.Match(value);
				if (match.Success && float.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					item = result;
				}
				Match match2 = _align.Match(value);
				if (match2.Success)
				{
					item2 = match2.Groups[1].Value;
				}
			}
			return (scale: item, align: item2);
		}

		public static void Apply(TMP_Text t, float baseSize)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)t == (Object)null)
			{
				return;
			}
			try
			{
				var (num, value) = Resolve(((Object)((Component)t).gameObject).name, t.transform);
				t.fontSize = (num.HasValue ? (baseSize * num.Value) : baseSize);
				if (!string.IsNullOrEmpty(value) && Enum.TryParse<TextAlignmentOptions>(value, out TextAlignmentOptions result))
				{
					t.alignment = result;
				}
			}
			catch (Exception ex)
			{
				Diagnostics.WarnOnce("resize-tmp", "TMP resize apply failed: " + ex.Message);
			}
		}

		public static void Apply(Text t, float baseSize)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)t == (Object)null)
			{
				return;
			}
			try
			{
				var (num, text) = Resolve(((Object)((Component)t).gameObject).name, ((Component)t).transform);
				t.fontSize = Mathf.Max(1, Mathf.RoundToInt(num.HasValue ? (baseSize * num.Value) : baseSize));
				if (!string.IsNullOrEmpty(text))
				{
					TextAnchor? val = ToAnchor(text);
					if (val.HasValue)
					{
						t.alignment = val.Value;
					}
				}
			}
			catch (Exception ex)
			{
				Diagnostics.WarnOnce("resize-legacy", "Legacy resize apply failed: " + ex.Message);
			}
		}

		private static TextAnchor? ToAnchor(string a)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			string text = a.ToLowerInvariant();
			string obj = ((text.Contains("top") || text.Contains("upper") || text.Contains("capline")) ? "Upper" : ((text.Contains("bottom") || text.Contains("lower") || text.Contains("baseline")) ? "Lower" : "Middle"));
			string text2 = (text.Contains("left") ? "Left" : (text.Contains("right") ? "Right" : "Center"));
			if (!Enum.TryParse<TextAnchor>(obj + text2, out TextAnchor result))
			{
				return null;
			}
			return result;
		}

		private static string GetPath(Transform tr)
		{
			string text = ((Object)tr).name;
			while ((Object)(object)tr.parent != (Object)null)
			{
				tr = tr.parent;
				text = ((Object)tr).name + "/" + text;
			}
			return text;
		}
	}
}
namespace WKLoc.Collect
{
	public static class TextCollector
	{
		private static readonly TimeSpan WriteDelay = TimeSpan.FromMilliseconds(50.0);

		public static bool Enabled;

		private static AutoGeneratedTextWriter? _writer;

		public static void Init(string langDir)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			_writer = new AutoGeneratedTextWriter(langDir, WriteDelay, (Func<string, TranslationData>)((string json) => JsonConvert.DeserializeObject<TranslationData>(json)), (Func<TranslationData, string>)((TranslationData data) => JsonConvert.SerializeObject((object)data, (Formatting)1)), (Action<Exception>)delegate(Exception e)
			{
				Debug.LogError((object)("[WKLoc] AutoGenerated write error: " + e.Message));
			}, (Action<int, string>)delegate(int added, string path)
			{
				Debug.Log((object)$"[WKLoc] Collected {added} new string(s) -> {path}");
			});
			if (Enabled)
			{
				Debug.Log((object)("[WKLoc] Text collection ON -> " + langDir + "/_AutoGenerated.json"));
			}
		}

		public static void Capture(string text)
		{
			if (!Enabled)
			{
				return;
			}
			AutoGeneratedTextWriter writer = _writer;
			if (writer != null)
			{
				string text2 = TextNormalizer.CanonicalKey(text);
				if (CollectFilter.ShouldCollect(text2) && !WkLocPlugin.Store.KnowsCanonical(text2))
				{
					writer.Enqueue(text2);
				}
			}
		}

		public static void Flush()
		{
			AutoGeneratedTextWriter writer = _writer;
			if (writer == null)
			{
				return;
			}
			try
			{
				writer.Flush();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[WKLoc] AutoGenerated flush error: " + ex.Message));
			}
		}

		public static void Shutdown()
		{
			AutoGeneratedTextWriter writer = _writer;
			_writer = null;
			if (writer == null)
			{
				return;
			}
			try
			{
				writer.Dispose();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[WKLoc] AutoGenerated shutdown error: " + ex.Message));
			}
		}
	}
	public static class TextureCollector
	{
		public static bool Enabled;

		public static int MaxSize;

		private static string _dumpDir = "";

		private static readonly HashSet<string> _dumped = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static void Init(string langDir)
		{
			_dumpDir = Path.Combine(langDir, "Texture_Dump");
		}

		public static void CollectScene()
		{
			if (!Enabled)
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(_dumpDir);
				Image[] array = Resources.FindObjectsOfTypeAll<Image>();
				foreach (Image val in array)
				{
					if ((Object)(object)val.sprite != (Object)null)
					{
						DumpSprite(val.sprite);
					}
				}
				SpriteRenderer[] array2 = Resources.FindObjectsOfTypeAll<SpriteRenderer>();
				foreach (SpriteRenderer val2 in array2)
				{
					if ((Object)(object)val2.sprite != (Object)null)
					{
						DumpSprite(val2.sprite);
					}
				}
				RawImage[] array3 = Resources.FindObjectsOfTypeAll<RawImage>();
				foreach (RawImage val3 in array3)
				{
					if ((Object)(object)val3.texture != (Object)null)
					{
						DumpTexture(val3.texture, ((Object)val3.texture).name, null);
					}
				}
				CollectMaterials();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[WKLoc] Texture collect error: " + ex.Message));
			}
		}

		public static void DumpSprite(Sprite sprite)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)sprite.texture == (Object)null))
			{
				DumpTexture((Texture)(object)sprite.texture, ((Object)sprite).name, sprite.textureRect);
			}
		}

		public static void DumpOne(Texture tex)
		{
			if ((Object)(object)tex != (Object)null)
			{
				DumpTexture(tex, ((Object)tex).name, null);
			}
		}

		public static void CollectMaterials()
		{
			Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
			for (int i = 0; i < array.Length; i++)
			{
				DumpRendererMaterials(array[i]);
			}
		}

		public static void DumpRendererMaterials(Renderer r)
		{
			if ((Object)(object)r == (Object)null)
			{
				return;
			}
			Material[] sharedMaterials = r.sharedMaterials;
			if (sharedMaterials == null)
			{
				return;
			}
			Material[] array = sharedMaterials;
			foreach (Material val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (val.HasProperty("_MainTex") && (Object)(object)val.mainTexture != (Object)null)
				{
					DumpOne(val.mainTexture);
				}
				foreach (string item in TextureSlots.Allowed)
				{
					if (val.HasProperty(item))
					{
						Texture texture = val.GetTexture(item);
						if ((Object)(object)texture != (Object)null)
						{
							DumpOne(texture);
						}
					}
				}
			}
		}

		private static void DumpTexture(Texture src, string rawName, Rect? region)
		{
			string originalName = TextureNameNormalizer.GetOriginalName(rawName);
			if (string.IsNullOrEmpty(originalName) || _dumped.Contains(originalName))
			{
				return;
			}
			if (TextureSwapper.Has(originalName))
			{
				_dumped.Add(originalName);
			}
			else if (MaxSize <= 0 || TextureSlots.ShouldDump(src.width, src.height, MaxSize))
			{
				try
				{
					Texture2D obj = MakeReadable(src, region);
					byte[] rawTextureData = obj.GetRawTextureData();
					int width = ((Texture)obj).width;
					int height = ((Texture)obj).height;
					Object.Destroy((Object)(object)obj);
					TextureDumpQueue.Enqueue(rawTextureData, width, height, Path.Combine(_dumpDir, originalName + ".png"));
					_dumped.Add(originalName);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("[WKLoc] Dump " + originalName + ": " + ex.Message));
				}
			}
		}

		private static Texture2D MakeReadable(Texture src, Rect? region)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			int num;
			Rect value;
			if (!region.HasValue)
			{
				num = 0;
			}
			else
			{
				value = region.Value;
				num = Mathf.RoundToInt(((Rect)(ref value)).x);
			}
			int num2 = num;
			int num3;
			if (!region.HasValue)
			{
				num3 = 0;
			}
			else
			{
				value = region.Value;
				num3 = Mathf.RoundToInt(((Rect)(ref value)).y);
			}
			int num4 = num3;
			int num5;
			if (!region.HasValue)
			{
				num5 = src.width;
			}
			else
			{
				value = region.Value;
				num5 = Mathf.RoundToInt(((Rect)(ref value)).width);
			}
			int num6 = num5;
			int num7;
			if (!region.HasValue)
			{
				num7 = src.height;
			}
			else
			{
				value = region.Value;
				num7 = Mathf.RoundToInt(((Rect)(ref value)).height);
			}
			int num8 = num7;
			RenderTexture temporary = RenderTexture.GetTemporary(src.width, src.height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			RenderTexture active = RenderTexture.active;
			Texture2D val = null;
			try
			{
				Graphics.Blit(src, temporary);
				RenderTexture.active = temporary;
				val = new Texture2D(num6, num8, (TextureFormat)4, false);
				val.ReadPixels(new Rect((float)num2, (float)num4, (float)num6, (float)num8), 0, 0);
				val.Apply();
				return val;
			}
			catch
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				throw;
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}
	}
	public static class TextureDumpQueue
	{
		private readonly struct Job
		{
			public readonly byte[] Rgba;

			public readonly int W;

			public readonly int H;

			public readonly string Path;

			public Job(byte[] rgba, int w, int h, string path)
			{
				Rgba = rgba;
				W = w;
				H = h;
				Path = path;
			}
		}

		private const int Capacity = 256;

		private static readonly BlockingCollection<Job> _queue = new BlockingCollection<Job>(256);

		private static Thread? _worker;

		private static volatile bool _started;

		private static readonly object _lifecycleGate = new object();

		private static bool _shuttingDown;

		public static void Enqueue(byte[] rgba, int w, int h, string path)
		{
			lock (_lifecycleGate)
			{
				if (_shuttingDown)
				{
					Debug.LogWarning((object)"[WKLoc] Dump queue is shutting down, dropping a texture.");
					return;
				}
				EnsureWorker();
				if (!_queue.TryAdd(new Job(rgba, w, h, path)))
				{
					Debug.LogWarning((object)"[WKLoc] Dump queue full, dropping a texture.");
				}
			}
		}

		private static void EnsureWorker()
		{
			if (!_started)
			{
				_started = true;
				_worker = new Thread(WorkerLoop)
				{
					IsBackground = true,
					Name = "WKLoc-DumpWorker"
				};
				_worker.Start();
			}
		}

		private static void WorkerLoop()
		{
			foreach (Job item in _queue.GetConsumingEnumerable())
			{
				try
				{
					byte[] bytes = PngWriter.EncodeRgba32(item.Rgba, item.W, item.H, true);
					string directoryName = Path.GetDirectoryName(item.Path);
					if (!string.IsNullOrEmpty(directoryName))
					{
						Directory.CreateDirectory(directoryName);
					}
					File.WriteAllBytes(item.Path, bytes);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("[WKLoc] Dump write " + item.Path + ": " + ex.Message));
				}
			}
		}

		public static void Shutdown()
		{
			Thread worker;
			lock (_lifecycleGate)
			{
				if (_shuttingDown)
				{
					return;
				}
				_shuttingDown = true;
				if (!_started)
				{
					return;
				}
				_queue.CompleteAdding();
				worker = _worker;
			}
			try
			{
				worker?.Join(2000);
			}
			catch
			{
			}
		}
	}
}
namespace WKLoc.Assets
{
	public static class FontProvider
	{
		private static readonly Dictionary<string, TMP_FontAsset> _tmp = new Dictionary<string, TMP_FontAsset>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, Font> _legacy = new Dictionary<string, Font>(StringComparer.OrdinalIgnoreCase);

		private static TMP_FontAsset? _mainTmp;

		private static Font? _mainLegacy;

		private static bool _fallbackDone;

		public static void Init(string pluginDir, string overrideTmpName, string overrideLegacyName, ManualLogSource log)
		{
			try
			{
				string text = LoadFirstFontBundle(pluginDir, log);
				if (text == null)
				{
					log.LogInfo((object)"No font AssetBundle with TMP/legacy fonts found next to the plugin.");
				}
				else
				{
					log.LogInfo((object)("Font AssetBundle loaded: " + Path.GetFileName(text)));
				}
				string text2 = overrideTmpName?.Trim() ?? "";
				TMP_FontAsset value2;
				if (text2.Length > 0 && _tmp.TryGetValue(text2, out TMP_FontAsset value))
				{
					_mainTmp = value;
				}
				else if (_tmp.TryGetValue("default", out value2))
				{
					_mainTmp = value2;
				}
				else
				{
					using Dictionary<string, TMP_FontAsset>.Enumerator enumerator = _tmp.GetEnumerator();
					if (enumerator.MoveNext())
					{
						_mainTmp = enumerator.Current.Value;
					}
				}
				string text3 = overrideLegacyName?.Trim() ?? "";
				Font value4;
				if (text3.Length > 0 && _legacy.TryGetValue(text3, out Font value3))
				{
					_mainLegacy = value3;
				}
				else if (_legacy.TryGetValue("default", out value4))
				{
					_mainLegacy = value4;
				}
				else
				{
					using Dictionary<string, Font>.Enumerator enumerator2 = _legacy.GetEnumerator();
					if (enumerator2.MoveNext())
					{
						_mainLegacy = enumerator2.Current.Value;
					}
				}
				if ((Object)(object)_mainLegacy == (Object)null)
				{
					try
					{
						_mainLegacy = Font.CreateDynamicFontFromOSFont(new string[7] { "Arial", "Segoe UI", "Tahoma", "Verdana", "Liberation Sans", "DejaVu Sans", "Noto Sans" }, 16);
						if ((Object)(object)_mainLegacy != (Object)null)
						{
							Object.DontDestroyOnLoad((Object)(object)_mainLegacy);
						}
					}
					catch (Exception ex)
					{
						log.LogWarning((object)("OS legacy font create failed: " + ex.Message));
					}
				}
				log.LogInfo((object)(string.Format("Bundle fonts: {0} TMP [{1}], ", _tmp.Count, string.Join(", ", _tmp.Keys)) + string.Format("{0} legacy [{1}]. ", _legacy.Count, string.Join(", ", _legacy.Keys)) + "MainTMP: " + (((Object)(object)_mainTmp != (Object)null) ? ((Object)_mainTmp).name : "<none>") + ", MainLegacy: " + (((Object)(object)_mainLegacy != (Object)null) ? ((Object)_mainLegacy).name : "<none>")));
				EnsureFallback();
			}
			catch (Exception ex2)
			{
				log.LogError((object)("Font bundle error: " + ex2.Message));
			}
		}

		private static string? LoadFirstFontBundle(string pluginDir, ManualLogSource log)
		{
			if (!Directory.Exists(pluginDir))
			{
				return null;
			}
			string[] candidatePaths = FontBundleLocator.GetCandidatePaths((IEnumerable<string>)Directory.GetFiles(pluginDir));
			foreach (string text in candidatePaths)
			{
				AssetBundle val = null;
				try
				{
					val = AssetBundle.LoadFromFile(text);
					if (!((Object)(object)val == (Object)null))
					{
						int count = _tmp.Count;
						int count2 = _legacy.Count;
						TMP_FontAsset[] array = val.LoadAllAssets<TMP_FontAsset>();
						foreach (TMP_FontAsset val2 in array)
						{
							_tmp[((Object)val2).name.Trim()] = val2;
						}
						Font[] array2 = val.LoadAllAssets<Font>();
						foreach (Font val3 in array2)
						{
							_legacy[((Object)val3).name.Trim()] = val3;
						}
						if (_tmp.Count > count || _legacy.Count > count2)
						{
							return text;
						}
					}
				}
				catch (Exception ex)
				{
					log.LogDebug((object)("Skipped font bundle candidate '" + Path.GetFileName(text) + "': " + ex.Message));
				}
				finally
				{
					if ((Object)(object)val != (Object)null)
					{
						val.Unload(false);
					}
				}
			}
			return null;
		}

		public static void EnsureFallback()
		{
			if (_fallbackDone || (Object)(object)_mainTmp == (Object)null)
			{
				return;
			}
			try
			{
				List<TMP_FontAsset> fallbackFontAssets = TMP_Settings.fallbackFontAssets;
				if (fallbackFontAssets != null)
				{
					if (!fallbackFontAssets.Contains(_mainTmp))
					{
						fallbackFontAssets.Add(_mainTmp);
					}
					_fallbackDone = true;
				}
			}
			catch
			{
			}
		}

		public static TMP_FontAsset? GetTmp(string? name)
		{
			if (string.IsNullOrEmpty(name) || !_tmp.TryGetValue(name.Trim(), out TMP_FontAsset value))
			{
				return null;
			}
			return value;
		}

		public static Font? GetLegacy(string? name)
		{
			if (string.IsNullOrEmpty(name) || !_legacy.TryGetValue(name.Trim(), out Font value))
			{
				return null;
			}
			return value;
		}

		public static void TryApply(TMP_Text text)
		{
			TryApply(text, null);
		}

		public static void TryApply(TMP_Text text, TMP_FontAsset? font)
		{
			if (!((Object)(object)text == (Object)null))
			{
				TMP_FontAsset val = font ?? _mainTmp;
				if (!((Object)(object)val == (Object)null) && (Object)(object)text.font != (Object)(object)val)
				{
					text.font = val;
				}
			}
		}

		public static void TryApply(Text text)
		{
			TryApply(text, null);
		}

		public static void TryApply(Text text, Font? font)
		{
			if (!((Object)(object)text == (Object)null))
			{
				Font val = font ?? _mainLegacy;
				if (!((Object)(object)val == (Object)null) && (Object)(object)text.font != (Object)(object)val)
				{
					text.font = val;
				}
			}
		}
	}
	public static class TextureSwapper
	{
		private sealed class SpriteLookup
		{
			public int Gen;

			public Sprite? Result;
		}

		private static readonly Dictionary<string, Texture2D> _textureMap = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, Sprite> _variantCache = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);

		private static readonly RetirementQueue<Sprite> _retiredSprites = new RetirementQueue<Sprite>();

		private static readonly List<Sprite> _pendingRetiredSprites = new List<Sprite>();

		private static readonly List<Sprite> _retainedSprites = new List<Sprite>();

		private static readonly List<Texture2D> _retainedTextures = new List<Texture2D>();

		private static readonly Action<Sprite> _destroySprite = delegate(Sprite sprite)
		{
			if ((Object)(object)sprite != (Object)null)
			{
				Object.Destroy((Object)(object)sprite);
			}
		};

		private static readonly ConditionalWeakTable<Sprite, SpriteLookup> _spriteLookup = new ConditionalWeakTable<Sprite, SpriteLookup>();

		private static int _lookupGeneration;

		private static bool _shutdown;

		public static void Init(string langDir, ManualLogSource log)
		{
			string path = Path.Combine(langDir, "Texture");
			if (!Directory.Exists(path))
			{
				log.LogInfo((object)"No Texture dir.");
				return;
			}
			string[] files = Directory.GetFiles(path, "*.png");
			for (int i = 0; i < files.Length; i++)
			{
				LoadTextureFile(files[i], log);
			}
			log.LogInfo((object)$"Textures: {_textureMap.Count}.");
		}

		private static bool LoadTextureFile(string file, ManualLogSource log)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			Texture2D val = null;
			try
			{
				string originalName = TextureNameNormalizer.GetOriginalName(Path.GetFileNameWithoutExtension(file));
				if (string.IsNullOrEmpty(originalName))
				{
					return false;
				}
				byte[] array = File.ReadAllBytes(file);
				if (_textureMap.TryGetValue(originalName, out Texture2D value))
				{
					if (!ImageConversion.LoadImage(value, array))
					{
						throw new InvalidDataException("Invalid PNG: " + file);
					}
					((Object)value).name = originalName;
				}
				else
				{
					val = new Texture2D(2, 2, (TextureFormat)4, false);
					if (!ImageConversion.LoadImage(val, array))
					{
						throw new InvalidDataException("Invalid PNG: " + file);
					}
					((Object)val).name = originalName;
					_textureMap[originalName] = val;
					val = null;
				}
				return true;
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				log.LogError((object)("Texture " + file + ": " + ex.Message));
				return false;
			}
		}

		public static void Reload(string langDir, ManualLogSource log)
		{
			_lookupGeneration++;
			List<Sprite> list = new List<Sprite>(_variantCache.Values);
			list.AddRange(_pendingRetiredSprites);
			_pendingRetiredSprites.Clear();
			_variantCache.Clear();
			string path = Path.Combine(langDir, "Texture");
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (Directory.Exists(path))
			{
				string[] files = Directory.GetFiles(path, "*.png");
				foreach (string obj in files)
				{
					string originalName = TextureNameNormalizer.GetOriginalName(Path.GetFileNameWithoutExtension(obj));
					if (_textureMap.ContainsKey(originalName))
					{
						hashSet.Add(originalName);
					}
					if (LoadTextureFile(obj, log))
					{
						hashSet.Add(originalName);
					}
				}
			}
			List<string> list2 = new List<string>();
			foreach (KeyValuePair<string, Texture2D> item in _textureMap)
			{
				if (!hashSet.Contains(item.Key))
				{
					list2.Add(item.Key);
				}
			}
			foreach (string item2 in list2)
			{
				_retainedTextures.Add(_textureMap[item2]);
				_textureMap.Remove(item2);
			}
			for (int num = _retainedSprites.Count - 1; num >= 0; num--)
			{
				if (_textureMap.ContainsKey(((Object)_retainedSprites[num]).name))
				{
					list.Add(_retainedSprites[num]);
					_retainedSprites.RemoveAt(num);
				}
			}
			foreach (Sprite item3 in list)
			{
				if (_textureMap.ContainsKey(((Object)item3).name))
				{
					_pendingRetiredSprites.Add(item3);
				}
				else
				{
					_retainedSprites.Add(item3);
				}
			}
			log.LogInfo((object)$"Textures: {_textureMap.Count}.");
		}

		public static bool Has(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return _textureMap.ContainsKey(name);
			}
			return false;
		}

		public static void TickCleanup(int maxItems = 32)
		{
			_retiredSprites.Drain(maxItems, _destroySprite);
		}

		public static void Shutdown()
		{
			if (_shutdown)
			{
				return;
			}
			_shutdown = true;
			foreach (Sprite value in _variantCache.Values)
			{
				_retiredSprites.Enqueue(value);
			}
			_variantCache.Clear();
			foreach (Sprite pendingRetiredSprite in _pendingRetiredSprites)
			{
				_retiredSprites.Enqueue(pendingRetiredSprite);
			}
			_pendingRetiredSprites.Clear();
			foreach (Sprite retainedSprite in _retainedSprites)
			{
				_retiredSprites.Enqueue(retainedSprite);
			}
			_retainedSprites.Clear();
			_retiredSprites.Drain(int.MaxValue, _destroySprite);
			foreach (Texture2D value2 in _textureMap.Values)
			{
				if ((Object)(object)value2 != (Object)null)
				{
					Object.Destroy((Object)(object)value2);
				}
			}
			_textureMap.Clear();
			foreach (Texture2D retainedTexture in _retainedTextures)
			{
				if ((Object)(object)retainedTexture != (Object)null)
				{
					Object.Destroy((Object)(object)retainedTexture);
				}
			}
			_retainedTextures.Clear();
		}

		private static void MatchSampling(Texture original, Texture2D replacement)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)original == (Object)null) && !((Object)(object)replacement == (Object)null))
			{
				((Texture)replacement).filterMode = original.filterMode;
				((Texture)replacement).wrapMode = original.wrapMode;
				((Texture)replacement).anisoLevel = original.anisoLevel;
			}
		}

		public static Sprite? GetReplacementSprite(Sprite current)
		{
			if ((Object)(object)current == (Object)null)
			{
				return null;
			}
			if (_spriteLookup.TryGetValue(current, out SpriteLookup value) && value.Gen == _lookupGeneration)
			{
				return value.Result;
			}
			Sprite result = BuildReplacementSprite(current);
			_spriteLookup.Remove(current);
			_spriteLookup.Add(current, new SpriteLookup
			{
				Gen = _lookupGeneration,
				Result = result
			});
			return result;
		}

		private static Sprite? BuildReplacementSprite(Sprite current)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: 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_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			if (!_textureMap.TryGetValue(((Object)current).name, out Texture2D value))
			{
				return null;
			}
			MatchSampling((Texture)(object)current.texture, value);
			float x = current.pivot.x;
			float y = current.pivot.y;
			Rect rect = current.rect;
			float width = ((Rect)(ref rect)).width;
			rect = current.rect;
			ValueTuple<float, float> valueTuple = SpriteGeometry.NormalizedPivot(x, y, width, ((Rect)(ref rect)).height);
			float item = valueTuple.Item1;
			float item2 = valueTuple.Item2;
			float pixelsPerUnit = current.pixelsPerUnit;
			rect = current.rect;
			float num = SpriteGeometry.MatchedPpu(pixelsPerUnit, ((Rect)(ref rect)).width, (float)((Texture)value).width);
			float x2 = current.border.x;
			float y2 = current.border.y;
			float z = current.border.z;
			float w = current.border.w;
			rect = current.rect;
			float width2 = ((Rect)(ref rect)).width;
			rect = current.rect;
			ValueTuple<float, float, float, float> valueTuple2 = SpriteGeometry.ScaleBorder(x2, y2, z, w, width2, ((Rect)(ref rect)).height, (float)((Texture)value).width, (float)((Texture)value).height);
			float item3 = valueTuple2.Item1;
			float item4 = valueTuple2.Item2;
			float item5 = valueTuple2.Item3;
			float item6 = valueTuple2.Item4;
			string key = ((Object)current).name + "|" + Math.Round(item, 4) + "|" + Math.Round(item2, 4) + "|" + Math.Round(num, 3) + "|" + Math.Round(item3, 2) + "|" + Math.Round(item4, 2) + "|" + Math.Round(item5, 2) + "|" + Math.Round(item6, 2);
			if (_variantCache.TryGetValue(key, out Sprite value2))
			{
				if (!((Object)(object)value2 == (Object)(object)current))
				{
					return value2;
				}
				return null;
			}
			Sprite val = Sprite.Create(value, new Rect(0f, 0f, (float)((Texture)value).width, (float)((Texture)value).height), new Vector2(item, item2), num, 0u, (SpriteMeshType)0, new Vector4(item3, item4, item5, item6));
			((Object)val).name = ((Object)current).name;
			_variantCache[key] = val;
			return val;
		}

		public static Texture? GetReplacementTexture(Texture current)
		{
			if ((Object)(object)current == (Object)null)
			{
				return null;
			}
			if (_textureMap.TryGetValue(((Object)current).name, out Texture2D value) && (Object)(object)value != (Object)(object)current)
			{
				MatchSampling(current, value);
				return (Texture?)(object)value;
			}
			return null;
		}

		public static void ReplaceMaterials()
		{
			if (_textureMap.Count != 0)
			{
				Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
				for (int i = 0; i < array.Length; i++)
				{
					ReplaceRendererMaterials(array[i]);
				}
			}
		}

		public static void ReplaceRendererMaterials(Renderer r)
		{
			if ((Object)(object)r == (Object)null || _textureMap.Count == 0)
			{
				return;
			}
			Material[] sharedMaterials = r.sharedMaterials;
			if (sharedMaterials == null)
			{
				return;
			}
			Material[] array = sharedMaterials;
			foreach (Material val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (val.HasProperty("_MainTex") && (Object)(object)val.mainTexture != (Object)null)
				{
					Texture replacementTexture = GetReplacementTexture(val.mainTexture);
					if ((Object)(object)replacementTexture != (Object)null)
					{
						val.mainTexture = replacementTexture;
					}
				}
				foreach (string item in TextureSlots.Allowed)
				{
					if (!val.HasProperty(item))
					{
						continue;
					}
					Texture texture = val.GetTexture(item);
					if (!((Object)(object)texture == (Object)null))
					{
						Texture replacementTexture2 = GetReplacementTexture(texture);
						if ((Object)(object)replacementTexture2 != (Object)null)
						{
							val.SetTexture(item, replacementTexture2);
						}
					}
				}
			}
		}

		public static void ReplaceImage(Image img)
		{
			if (!((Object)(object)img == (Object)null) && !((Object)(object)img.sprite == (Object)null))
			{
				Sprite replacementSprite = GetReplacementSprite(img.sprite);
				if ((Object)(object)replacementSprite != (Object)null)
				{
					img.sprite = replacementSprite;
				}
			}
		}

		public static void ReplaceRawImage(RawImage ri)
		{
			if (!((Object)(object)ri == (Object)null) && !((Object)(object)ri.texture == (Object)null))
			{
				Texture replacementTexture = GetReplacementTexture(ri.texture);
				if ((Object)(object)replacementTexture != (Object)null)
				{
					ri.texture = replacementTexture;
				}
			}
		}

		public static void ReplaceSpriteRenderer(SpriteRenderer sr)
		{
			if (!((Object)(object)sr == (Object)null) && !((Object)(object)sr.sprite == (Object)null))
			{
				Sprite replacementSprite = GetReplacementSprite(sr.sprite);
				if ((Object)(object)replacementSprite != (Object)null)
				{
					sr.sprite = replacementSprite;
				}
			}
		}

		public static void ReplaceAllInScene()
		{
			if (_textureMap.Count == 0)
			{
				return;
			}
			Image[] array = Resources.FindObjectsOfTypeAll<Image>();
			for (int i = 0; i < array.Length; i++)
			{
				ReplaceImage(array[i]);
			}
			RawImage[] array2 = Resources.FindObjectsOfTypeAll<RawImage>();
			for (int i = 0; i < array2.Length; i++)
			{
				ReplaceRawImage(array2[i]);
			}
			SpriteRenderer[] array3 = Resources.FindObjectsOfTypeAll<SpriteRenderer>();
			for (int i = 0; i < array3.Length; i++)
			{
				ReplaceSpriteRenderer(array3[i]);
			}
			ReplaceMaterials();
			foreach (Sprite pendingRetiredSprite in _pendingRetiredSprites)
			{
				_retiredSprites.Enqueue(pendingRetiredSprite);
			}
			_pendingRetiredSprites.Clear();
		}
	}
}
namespace WKLoc.Apply
{
	public static class ContextMenuRefresher
	{
		private static readonly DeferredRefreshQueue<Transform> _queue = new DeferredRefreshQueue<Transform>();

		public static void Queue(Transform root, int frames = 2)
		{
			if (!((Object)(object)root == (Object)null))
			{
				_queue.Enqueue(root, frames);
			}
		}

		public static void Tick()
		{
			_queue.Tick((Action<Transform>)Refresh);
		}

		private static void Refresh(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			try
			{
				TextPipeline.ApplyAllInChildren((Component)(object)root);
				TMP_Text[] componentsInChildren = ((Component)root).GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null))
					{
						((Graphic)val).SetAllDirty();
						val.ForceMeshUpdate(true, false);
					}
				}
				Text[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<Text>(true);
				foreach (Text val2 in componentsInChildren2)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						val2.cachedTextGenerator.Invalidate();
						((Graphic)val2).SetAllDirty();
						val2.FontTextureChanged();
					}
				}
				Canvas.ForceUpdateCanvases();
			}
			catch (Exception ex)
			{
				Diagnostics.WarnOnce("context-refresh", "Context menu refresh failed: " + ex.Message);
			}
		}
	}
	public static class FrameBudget
	{
		private const int Size = 60;

		private static readonly float[] _frames = new float[60];

		private static int _idx;

		private static int _count;

		private static readonly Stopwatch _sw = new Stopwatch();

		public static void StartFrame()
		{
			float num = Time.unscaledDeltaTime * 1000f;
			if (num > 0f && num <= 1000f)
			{
				_frames[_idx] = num;
				_idx = (_idx + 1) % 60;
				if (_count < 60)
				{
					_count++;
				}
			}
			_sw.Restart();
		}

		public static float BudgetMs()
		{
			return BudgetMath.ComputeBudgetMs(_frames, _count);
		}

		public static bool Exceeded(float budgetMs)
		{
			return _sw.Elapsed.TotalMilliseconds > (double)budgetMs;
		}
	}
	public static class FrameDriver
	{
		public static void Install(Type marker, Action tick)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: 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)
			PlayerLoopSystem currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
			int num = FindPhase(currentPlayerLoop.subSystemList, typeof(Update));
			if (num >= 0)
			{
				PlayerLoopSystem val = currentPlayerLoop.subSystemList[num];
				PlayerLoopSystem[] array = val.subSystemList ?? Array.Empty<PlayerLoopSystem>();
				if (FindPhase(array, marker) < 0)
				{
					PlayerLoopSystem[] array2 = (PlayerLoopSystem[])(object)new PlayerLoopSystem[array.Length + 1];
					Array.Copy(array, array2, array.Length);
					array2[^1] = new PlayerLoopSystem
					{
						type = marker,
						updateDelegate = new UpdateFunction(RunSafely)
					};
					val.subSystemList = array2;
					currentPlayerLoop.subSystemList[num] = val;
					PlayerLoop.SetPlayerLoop(currentPlayerLoop);
				}
			}
			void RunSafely()
			{
				try
				{
					tick();
				}
				catch
				{
				}
			}
		}

		private static int FindPhase(PlayerLoopSystem[]? phases, Type type)
		{
			if (phases == null)
			{
				return -1;
			}
			for (int i = 0; i < phases.Length; i++)
			{
				if (phases[i].type == type)
				{
					return i;
				}
			}
			return -1;
		}
	}
	[HarmonyPatch]
	public static class HookSetters
	{
		[HarmonyPatch]
		public static class TmpFormattedTextHooks
		{
			public static IEnumerable<MethodBase> TargetMethods()
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(TMP_Text), "SetText", new Type[2]
				{
					typeof(string),
					typeof(bool)
				}, (Type[])null);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
				for (int floats = 1; floats <= 7; floats++)
				{
					Type[] array = new Type[1 + floats];
					array[0] = typeof(string);
					for (int i = 0; i < floats; i++)
					{
						array[1 + i] = typeof(float);
					}
					MethodInfo methodInfo2 = AccessTools.Method(typeof(TMP_Text), "SetText", array, (Type[])null);
					if (methodInfo2 != null)
					{
						yield return methodInfo2;
					}
				}
			}

			[HarmonyPrefix]
			public static void BeforeSetText(TMP_Text __instance, ref string sourceText)
			{
				TouchedComponents.Track((Object)(object)__instance);
				if (TextPipeline.HookSuppressionDepth <= 0)
				{
					TextPipeline.Transform(__instance, ref sourceText);
				}
			}

			[HarmonyPostfix]
			public static void AfterSetText(TMP_Text __instance)
			{
				TextPipeline.PostTransform(__instance);
			}
		}

		[HarmonyPatch]
		public static class ObjectiveViewerHooks
		{
			[HarmonyPatch(typeof(UI_ObjectiveViewer), "Awake")]
			[HarmonyPostfix]
			public static void RefreshObjectiveViewerAfterAwake(UI_ObjectiveViewer __instance)
			{
				ObjectiveViewerRefresher.Register(__instance);
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
				ObjectiveViewerRefresher.Queue(__instance);
			}

			[HarmonyPatch(typeof(UI_ObjectiveViewer), "ShowViewer")]
			[HarmonyPostfix]
			public static void RefreshObjectiveViewerAfterShow(UI_ObjectiveViewer __instance)
			{
				ObjectiveViewerRefresher.Register(__instance);
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
				ObjectiveViewerRefresher.Queue(__instance);
			}

			[HarmonyPatch(typeof(UI_ObjectiveViewer), "SetTitle")]
			[HarmonyPrefix]
			public static void TranslateObjectiveViewerTitle(ref string s)
			{
				TextPipeline.TransformDetached(ref s);
			}

			[HarmonyPatch(typeof(UI_ObjectiveViewer), "CreateOrUpdateObjective")]
			[HarmonyPrefix]
			public static void TranslateObjectiveStrings(ref string title, ref string desc)
			{
				TextPipeline.TransformDetached(ref title);
				TextPipeline.TransformDetached(ref desc);
			}

			[HarmonyPatch(typeof(UI_ObjectiveViewer), "CreateOrUpdateObjective")]
			[HarmonyPostfix]
			public static void RefreshObjectiveViewerAfterCreateOrUpdate()
			{
				ObjectiveViewerRefresher.QueueAll(1);
			}
		}

		[HarmonyPatch]
		public static class ContextMenuHooks
		{
			[HarmonyPatch(typeof(ContextMenu_Manager), "ShowMenu")]
			[HarmonyPostfix]
			public static void RefreshContextMenuOptionsAfterShow(ContextMenu_Manager __instance)
			{
				if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.contextMenu == (Object)null))
				{
					TextPipeline.ApplyAllInChildren((Component)(object)__instance.contextMenu);
					ContextMenuRefresher.Queue((Transform)(object)__instance.contextMenu);
				}
			}
		}

		[HarmonyPatch]
		public static class OsFileHooks
		{
			[HarmonyPatch(typeof(OS_File), "Initialize")]
			[HarmonyPostfix]
			public static void QueueOsFileAfterInitialize(OS_File __instance)
			{
				OsFileBackplateRefresher.Queue(__instance);
			}

			[HarmonyPatch(typeof(OS_File), "SetText")]
			[HarmonyPostfix]
			public static void QueueOsFileAfterSetText(OS_File __instance)
			{
				OsFileBackplateRefresher.Queue(__instance);
			}
		}

		[HarmonyPatch]
		public static class PerkCardHooks
		{
			[HarmonyPatch(typeof(App_PerkPage_Card), "Initialize")]
			[HarmonyPostfix]
			public static void RefreshPerkCardAfterInitialize(App_PerkPage_Card __instance)
			{
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
			}

			[HarmonyPatch(typeof(App_PerkPage_Card), "BuyCard")]
			[HarmonyPostfix]
			public static void RefreshPerkCardAfterPurchase(App_PerkPage_Card __instance)
			{
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
			}
		}

		[HarmonyPatch]
		public static class FacilityCardHooks
		{
			[HarmonyPatch(typeof(App_Facility_Card), "Initialize")]
			[HarmonyPostfix]
			public static void RefreshFacilityCardAfterInitialize(App_Facility_Card __instance)
			{
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
			}

			[HarmonyPatch(typeof(App_Facility_Card), "CheckLock")]
			[HarmonyPostfix]
			public static void RefreshFacilityCardAfterLockCheck(App_Facility_Card __instance)
			{
				TextPipeline.ApplyAllInChildren((Component)(object)__instance);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void BeforeTmpTextSet(TMP_Text __instance, ref string value)
		{
			TouchedComponents.Track((Object)(object)__instance);
			if (TextPipeline.HookSuppressionDepth <= 0)
			{
				TextPipeline.Transform(__instance, ref value);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		public static void AfterTmpTextSet(TMP_Text __instance)
		{
			TextPipeline.PostTransform(__instance);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void BeforeLegacyTextSet(Text __instance, ref string value, out bool __state)
		{
			TouchedComponents.Track((Object)(object)__instance);
			string text = __instance.text;
			if (TextPipeline.HookSuppressionDepth <= 0)
			{
				TextPipeline.Transform(__instance, ref value);
			}
			__state = !string.Equals(text, value, StringComparison.Ordinal);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		public static void AfterLegacyTextSet(Text __instance, bool __state)
		{
			TextPipeline.PostTransform(__instance);
			if (!__state)
			{
				return;
			}
			try
			{
				if (__instance != null)
				{
					__instance.FontTextureChanged();
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void BeforeLegacyInputSet(InputField __instance, ref string value)
		{
			TouchedComponents.Track((Object)(object)__instance);
			if (TextPipeline.HookSuppressionDepth <= 0)
			{
				TextPipeline.Transform(__instance, ref value);
				TextPipeline.PrepareInputRichText(__instance, ref value);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		public static void AfterLegacyInputSet(InputField __instance)
		{
			TextPipeline.PostInputChanged(__instance);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void BeforeTmpInputSet(TMP_InputField __instance, ref string value)
		{
			TouchedComponents.Track((Object)(object)__instance);
			if (TextPipeline.HookSuppressionDepth <= 0)
			{
				TextPipeline.Transform(__instance, ref value);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		public static void AfterTmpInputSet(TMP_InputField __instance)
		{
			TextPipeline.PostInputChanged(__instance);
		}

		[HarmonyPatch(typeof(TextMeshProUGUI), "Awake")]
		[HarmonyPostfix]
		public static void PrimeTmpUiOnAwake(TextMeshProUGUI __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")]
		[HarmonyPostfix]
		public static void PrimeTmpUiOnEnable(TextMeshProUGUI __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(TextMeshPro), "Awake")]
		[HarmonyPostfix]
		public static void PrimeWorldTmpOnAwake(TextMeshPro __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(TextMeshPro), "OnEnable")]
		[HarmonyPostfix]
		public static void PrimeWorldTmpOnEnable(TextMeshPro __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(Text), "OnEnable")]
		[HarmonyPostfix]
		public static void PrimeLegacyOnEnable(Text __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(InputField), "OnEnable")]
		[HarmonyPostfix]
		public static void PrimeLegacyInputOnEnable(InputField __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		[HarmonyPatch(typeof(TMP_InputField), "OnEnable")]
		[HarmonyPostfix]
		public static void PrimeTmpInputOnEnable(TMP_InputField __instance)
		{
			RefreshOne((Component)(object)__instance);
		}

		private static void RefreshOne(Component c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				TouchedComponents.Track((Object)(object)c);
				TextPipeline.ApplyAll(c);
			}
		}
	}
	[HarmonyPatch]
	public static class HookTextures
	{
		[HarmonyPatch(typeof(MaskableGraphic), "OnEnable")]
		[HarmonyPostfix]
		public static void Graphic_Enable(MaskableGraphic __instance)
		{
			if (!TextPipeline.Enabled)
			{
				return;
			}
			try
			{
				Image val = (Image)(object)((__instance is Image) ? __instance : null);
				if (val != null)
				{
					TextureSwapper.ReplaceImage(val);
					return;
				}
				RawImage val2 = (RawImage)(object)((__instance is RawImage) ? __instance : null);
				if (val2 != null)
				{
					TextureSwapper.ReplaceRawImage(val2);
				}
			}
			catch
			{
			}
		}

		public static Sprite FilterSprite(Sprite value)
		{
			if ((Object)(object)value == (Object)null)
			{
				return value;
			}
			try
			{
				if (TextureCollector.Enabled)
				{
					TextureCollector.DumpSprite(value);
				}
				if (TextPipeline.Enabled)
				{
					Sprite replacementSprite = TextureSwapper.GetReplacementSprite(value);
					if ((Object)(object)replacementSprite != (Object)null)
					{
						return replacementSprite;
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[WKLoc] FilterSprite: " + ex.Message));
			}
			return value;
		}

		public static Texture FilterTexture(Texture value)
		{
			if ((Object)(object)value == (Object)null)
			{
				return value;
			}
			try
			{
				if (TextureCollector.Enabled)
				{
					TextureCollector.DumpOne(value);
				}
				if (TextPipeline.Enabled)
				{
					Texture replacementTexture = TextureSwapper.GetReplacementTexture(value);
					if ((Object)(object)replacementTexture != (Object)null)
					{
						return replacementTexture;
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[WKLoc] FilterTexture: " + ex.Message));
			}
			return value;
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void Image_Set(ref Sprite value)
		{
			value = FilterSprite(value);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void SR_Set(ref Sprite value)
		{
			value = FilterSprite(value);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static void Raw_Set(ref Texture value)
		{
			value = FilterTexture(value);
		}

		[HarmonyPatch(typeof(Resources), "Load", new Type[]
		{
			typeof(string),
			typeof(Type)
		})]
		[HarmonyPostfix]
		public static void Load_Post(ref Object __result)
		{
			Object obj = __result;
			Sprite val = (Sprite)(object)((obj is Sprite) ? obj : null);
			if (val != null)
			{
				Sprite val2 = FilterSprite(val);
				if ((Object)(object)val2 != (Object)(object)val)
				{
					__result = (Object)(object)val2;
				}
				return;
			}
			Object obj2 = __result;
			Texture val3 = (Texture)(object)((obj2 is Texture) ? obj2 : null);
			if (val3 != null)
			{
				Texture val4 = FilterTexture(val3);
				if ((Object)(object)val4 != (Object)(object)val3)
				{
					__result = (Object)(object)val4;
				}
			}
		}
	}
	public static class HookThirdParty
	{
		[HarmonyPatch(typeof(UT_TextScrawl), "ShowText")]
		public static class Scrawl_ShowText
		{
			[HarmonyPrefix]
			public static void BeforeScrawlStarts(UT_TextScrawl __instance, ref string s)
			{
				if (TextPipeline.Enabled)
				{
					string original = s;
					s = Translate(s);
					TMP_Text componentInChildren = ((Component)__instance).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						TextPipeline.RememberSourceText(componentInChildren, original);
					}
					else
					{
						Text componentInChildren2 = ((Component)__instance).GetComponentInChildren<Text>(true);
						if ((Object)(object)componentInChildren2 != (Object)null)
						{
							TextPipeline.RememberSourceText(componentInChildren2, original);
						}
					}
				}
				TextPipeline.HookSuppressionDepth++;
			}

			[HarmonyFinalizer]
			public static void AfterScrawlAttempt()
			{
				TextPipeline.HookSuppressionDepth--;
			}
		}

		[HarmonyPatch(typeof(TypewriterCore), "ShowText", new Type[] { typeof(string) })]
		public static class Typewriter_ShowText
		{
			[HarmonyPrefix]
			public static void BeforeTypewriterStarts(ref string text)
			{
				if (TextPipeline.Enabled)
				{
					text = Translate(text);
				}
				TextPipeline.HookSuppressionDepth++;
			}

			[HarmonyFinalizer]
			public static void AfterTypewriterAttempt()
			{
				TextPipeline.HookSuppressionDepth--;
			}
		}

		[HarmonyPatch(typeof(TAnimCore), "ConvertText")]
		public static class TAnim_ConvertText
		{
			[HarmonyPrefix]
			public static void BeforeAnimatorParse(TAnimCore __instance, ref string textToParse)
			{
				if (TextPipeline.Enabled)
				{
					if (WkLocPlugin.Cfg.DebugLog.Value)
					{
						string text = textToParse ?? "<null>";
						string text2 = WkLocPlugin.Store.Resolve(text);
						bool flag = text2 != null && text2 != text;
						Debug.Log((object)string.Format("[WKLoc] TAnim.ConvertText src=[{0}] len={1} hit={2}", text, text.Length, flag ? "YES" : "NO"));
					}
					string text3 = textToParse ?? string.Empty;
					textToParse = Translate(textToParse);
					TextAnimator_TMP val = (TextAnimator_TMP)(object)((__instance is TextAnimator_TMP) ? __instance : null);
					if (val != null && (Object)(object)val.TMProComponent != (Object)null)
					{
						if (textToParse != text3)
						{
							TextPipeline.RememberSourceText(val.TMProComponent, text3);
						}
						TMP_FontAsset val2 = ResolveTmpFont(TextPipeline.RecallSourceText(val.TMProComponent, text3));
						if ((Object)(object)val2 != (Object)null)
						{
							FontProvider.TryApply(val.TMProComponent, val2);
						}
						if (WkLocPlugin.Cfg.DebugLog.Value)
						{
							Debug.Log((object)("[WKLoc] TAnim font now=[" + (((Object)(object)val.TMProComponent.font != (Object)null) ? ((Object)val.TMProComponent.font).name : "<null>") + "] go='" + ((Object)((Component)val.TMProComponent).gameObject).name + "'"));
						}
					}
				}
				TextPipeline.HookSuppressionDepth++;
			}

			[HarmonyFinalizer]
			public static void AfterAnimatorParseAttempt()
			{
				TextPipeline.HookSuppressionDepth--;
			}
		}

		private static string Translate(string s)
		{
			return ThirdPartyTextTranslator.TranslateOrCapture(s, (Func<string, string>)WkLocPlugin.Store.Resolve, (Action<string>)TextCollector.Capture);
		}

		private static TMP_FontAsset? ResolveTmpFont(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return null;
			}
			string font = WkLocPlugin.Store.GetFont(s);
			if (font == null)
			{
				string text = s.ToLowerInvariant();
				if (text != s)
				{
					font = WkLocPlugin.Store.GetFont(text);
				}
			}
			return FontProvider.GetTmp(font);
		}
	}
	public static class ObjectiveViewerRefresher
	{
		private static readonly DeferredRefreshQueue<UI_ObjectiveViewer> _queue = new DeferredRefreshQueue<UI_ObjectiveViewer>();

		private static readonly List<WeakReference<UI_ObjectiveViewer>> _known = new List<WeakReference<UI_ObjectiveViewer>>();

		public static void Register(UI_ObjectiveViewer viewer)
		{
			if ((Object)(object)viewer == (Object)null)
			{
				return;
			}
			for (int num = _known.Count - 1; num >= 0; num--)
			{
				if (!_known[num].TryGetTarget(out UI_ObjectiveViewer target) || (Object)(object)target == (Object)null)
				{
					_known.RemoveAt(num);
				}
				else if (target == viewer)
				{
					return;
				}
			}
			_known.Add(new WeakReference<UI_ObjectiveViewer>(viewer));
		}

		public static void QueueAll(int frames)
		{
			for (int num = _known.Count - 1; num >= 0; num--)
			{
				if (!_known[num].TryGetTarget(out UI_ObjectiveViewer target) || (Object)(object)target == (Object)null)
				{
					_known.RemoveAt(num);
				}
				else
				{
					Queue(target, frames);
				}
			}
		}

		public static void Queue(UI_ObjectiveViewer viewer, int frames = 2)
		{
			if (!((Object)(object)viewer == (Object)null))
			{
				_queue.Enqueue(viewer, frames);
			}
		}

		public static void Tick()
		{
			_queue.Tick((Action<UI_ObjectiveViewer>)Refresh);
		}

		public static void Refresh(UI_ObjectiveViewer viewer)
		{
			if ((Object)(object)viewer == (Object)null)
			{
				return;
			}
			try
			{
				TMP_Text[] componentsInChildren = ((Component)viewer).GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !TextGuards.IsTAnim((Component)(object)val))
					{
						ApplyObjectiveViewerText(val, !TextGuards.HasScrawl((Component)(object)val));
						((Graphic)val).SetAllDirty();
						val.ForceMeshUpdate(true, false);
					}
				}
				Text[] componentsInChildren2 = ((Component)viewer).GetComponentsInChildren<Text>(true);
				foreach (Text val2 in componentsInChildren2)
				{
					if (!((Object)(object)val2 == (Object)null) && !TextGuards.IsInputChild((Graphic)(object)val2))
					{
						ApplyObjectiveViewerText(val2, !TextGuards.HasScrawl((Component)(object)val2));
						val2.cachedTextGenerator.Invalidate();
						((Graphic)val2).SetAllDirty();
						val2.FontTextureChanged();
					}
				}
				Canvas.ForceUpdateCanvases();
			}
			catch (Exception ex)
			{
				Diagnostics.WarnOnce("objective-refresh", "Objective refresh failed: " + ex.Message);
			}
		}

		private static void ApplyObjectiveViewerText(object component, bool applyFont)
		{
			if (IsObjectiveHeader(component))
			{
				TextPipeline.Reapply(component, applyFont);
			}
			else
			{
				TextPipeline.ApplyAll(component);
			}
		}

		private static bool IsObjectiveHeader(object component)
		{
			TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null);
			string text;
			if (val == null)
			{
				Text val2 = (Text)((component is Text) ? component : null);
				text = ((val2 == null) ? null : val2.text);
			}
			else
			{
				text = val.text;
			}
			string text2 = text;
			if (string.IsNullOrEmpty(text2))
			{
				return false;
			}
			string text3 = TextPipeline.RecallSourceText(component, text2);
			if (!IsObjectiveHeaderString(text2))
			{
				return IsObjectiveHeaderString(text3);
			}
			return true;
		}

		private static bool IsObjectiveHeaderString(string text)
		{
			string text2 = TextNormalizer.CanonicalKey(text);
			if (!text2.Contains("OBJECTIVES:"))
			{
				return text2.Contains("ЗАДАЧИ:");
			}
			return true;
		}
	}
	public static class OsFileBackplateRefresher
	{
		private static readonly DeferredRefreshQueue<OS_File> _queue = new DeferredRefreshQueue<OS_File>();

		public static void Queue(OS_File file, int frames = 2)
		{
			if (!((Object)(object)file == (Object)null))
			{
				_queue.Enqueue(file, frames);
			}
		}

		public static void Tick()
		{
			_queue.Tick((Action<OS_File>)Refresh);
		}

		public static void Refresh(OS_File file)
		{
			if ((Object)(object)file == (Object)null)
			{
				return;
			}
			try
			{
				InputField nameText = file.nameText;
				if (!((Object)(object)nameText == (Object)null) && !((Object)(object)file.nameTextBackplate == (Object)null))
				{
					if ((Object)(object)nameText.textComponent != (Object)null)
					{
						nameText.textComponent.supportRichText = true;
						nameText.textComponent.cachedTextGenerator.Invalidate();
						nameText.textComponent.FontTextureChanged();
					}
					Canvas.ForceUpdateCanvases();
					file.ResetBackerWidth();
				}
			}
			catch (Exception ex)
			{
				Diagnostics.WarnOnce("os-file-refresh", "OS file backplate refresh failed: " + ex.Message);
			}
		}
	}
	public static class RendererNet
	{
		public static float LatencySeconds = 3f;

		private const int BatchSize = 100;

		private static float _lastRefresh;

		private static Object[]? _rend;

		private static bool _forceRefresh;

		private static readonly BatchCursor _cursor = new BatchCursor();

		public static void OnSceneChange()
		{
			_rend = null;
			_lastRefresh = 0f;
			_forceRefresh = true;
			_cursor.Reset(0);
		}

		public static void Tick(float budgetMs)
		{
			if (!TextPipeline.Enabled)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_forceRefresh || _rend == null || (!_cursor.HasRemaining && ScanInterval.ShouldRefresh(realtimeSinceStartup, _lastRefresh, LatencySeconds)))
			{
				if (FrameBudget.Exceeded(budgetMs))
				{
					return;
				}
				Object[] rend = (Object[])(object)Object.FindObjectsOfType<Renderer>();
				_rend = rend;
				_cursor.Reset(_rend.Length);
				_lastRefresh = realtimeSinceStartup;
				_forceRefresh = false;
			}
			ProcessBatch(budgetMs);
		}

		private static void ProcessBatch(float budgetMs)
		{
			if (_rend == null || _rend.Length == 0 || !_cursor.HasRemaining || FrameBudget.Exceeded(budgetMs))
			{
				return;
			}
			int num = default(int);
			int num2 = default(int);
			_cursor.Step(100, ref num, ref num2);
			for (int i = num; i < num2; i++)
			{
				Object val = _rend[i];
				if (val == (Object)null)
				{
					continue;
				}
				try
				{
					SpriteRenderer val2 = (SpriteRenderer)(object)((val is SpriteRenderer) ? val : null);
					if (val2 != null)
					{
						TextureSwapper.ReplaceSpriteRenderer(val2);
						continue;
					}
					Renderer val3 = (Renderer)(object)((val is Renderer) ? val : null);
					if (val3 != null)
					{
						TextureSwapper.ReplaceRendererMaterials(val3);
					}
				}
				catch (Exception ex)
				{
					Diagnostics.WarnOnce("renderer-net", "Renderer scan failed: " + ex.Message);
				}
			}
		}
	}
	public static class Sweeper
	{
		private static bool _inputBroken;

		public static void Tick()
		{
			FrameBudget.StartFrame();
			FontProvider.EnsureFallback();
			OsFileBackplateRefresher.Tick();
			ObjectiveViewerRefresher.Tick();
			ContextMenuRefresher.Tick();
			HandleHotkeys();
			if (TranslationWatcher.ConsumePendingReload())
			{
				Debug.Log((object)"[WKLoc] Translation files changed on disk - auto-reloading.");
				ReloadAll();
			}
			TextureSwapper.TickCleanup();
			if (WkLocPlugin.Cfg.EnablePolling.Value)
			{
				float budgetMs = FrameBudget.BudgetMs();
				RendererNet.LatencySeconds = WkLocPlugin.Cfg.RendererScanSeconds.Value;
				RendererNet.Tick(budgetMs);
				TextureScanner.LatencySeconds = WkLocPlugin.Cfg.RendererScanSeconds.Value;
				TextureScanner.Tick(budgetMs);
			}
		}

		public static void ReloadAll()
		{
			int currentLevel = WkLocPlugin.Store.CurrentLevel;
			WkLocPlugin.Store = TranslationLoader.ReadAll(WkLocPlugin.Instance.LangDir, WkLocPlugin.Instance.Log, WkLocPlugin.Cfg.EnableTranslationScoping.Value);
			WkLocPlugin.Store.CurrentLevel = currentLevel;
			TextPipeline.InvalidateTranslationContext();
			TextureSwapper.Reload(WkLocPlugin.Instance.LangDir, WkLocPlugin.Instance.Log);
			UiResizer.Init(WkLocPlugin.Instance.LangDir, WkLocPlugin.Instance.Log);
			TextPipeline.InvalidateResizeRules();
			if (TextPipeline.Enabled)
			{
				TextureSwapper.ReplaceAllInScene();
			}
			ForceRefresh();
			Debug.Log((object)"[WKLoc] Reloaded translation, textures and resize rules.");
		}

		private static void ForceRefresh()
		{
			TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
			foreach (TMP_Text val in array)
			{
				if (((Component)val).gameObject.activeInHierarchy && !TextGuards.IsTAnim((Component)(object)val))
				{
					TextPipeline.Reapply(val, !TextGuards.HasScrawl((Component)(object)val));
				}
			}
			Text[] array2 = Resources.FindObjectsOfTypeAll<Text>();
			foreach (Text val2 in array2)
			{
				if (((Component)val2).gameObject.activeInHierarchy && !TextGuards.IsInputChild((Graphic)(object)val2))
				{
					TextPipeline.Reapply(val2, !TextGuards.HasScrawl((Component)(object)val2));
				}
			}
			InputField[] array3 = Resources.FindObjectsOfTypeAll<InputField>();
			foreach (InputField val3 in array3)
			{
				if (((Component)val3).gameObject.activeInHierarchy)
				{
					TextPipeline.Reapply(val3);
				}
			}
			TMP_InputField[] array4 = Resources.FindObjectsOfTypeAll<TMP_InputField>();
			foreach (TMP_InputField val4 in array4)
			{
				if (((Component)val4).gameObject.activeInHierarchy)
				{
					TextPipeline.Reapply(val4, !TextGuards.HasScrawl((Component)(object)val4));
				}
			}
			RefreshTouchedComponents();
		}

		public static void RefreshTouchedComponents()
		{
			foreach (Object item in TouchedComponents.Snapshot())
			{
				TMP_Text val = (TMP_Text)(object)((item is TMP_Text) ? item : null);
				if (val != null)
				{
					TextPipeline.Reapply(val, !TextGuards.HasScrawl((Component)(object)val));
					continue;
				}
				Text val2 = (Text)(object)((item is Text) ? item : null);
				if (val2 != null)
				{
					if (!TextGuards.IsInputChild((Graphic)(object)val2))
					{
						TextPipeline.Reapply(val2, !TextGuards.HasScrawl((Component)(object)val2));
					}
					continue;
				}
				InputField val3 = (InputField)(object)((item is InputField) ? item : null);
				if (val3 != null)
				{
					TextPipeline.Reapply(val3);
					continue;
				}
				TMP_InputField val4 = (TMP_InputField)(object)((item is TMP_InputField) ? item : null);
				if (val4 != null)
				{
					TextPipeline.Reapply(val4, !TextGuards.HasScrawl((Component)(object)val4));
				}
			}
		}

		private static void HandleHotkeys()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (!WkLocPlugin.Cfg.EnableHotkeys.Value || _inputBroken)
			{
				return;
			}
			try
			{
				if (Input.GetKeyDown(WkLocPlugin.Cfg.ToggleKey.Value))
				{
					TextPipeline.Enabled = !TextPipeline.Enabled;
					if (TextPipeline.Enabled)
					{
						TextureSwapper.ReplaceAllInScene();
					}
					ForceRefresh();
					Debug.Log((object)("[WKLoc] Translation " + (TextPipeline.Enabled ? "ON" : "OFF")));
				}
				if (Input.GetKeyDown(WkLocPlugin.Cfg.ReloadKey.Value))
				{
					ReloadAll();
				}
			}
			catch (Exception ex)
			{
				_inputBroken = true;
				Debug.LogWarning((object)("[WKLoc] Legacy Input unavailable, hotkeys disabled: " + ex.Message));
			}
		}
	}
	public static class TextGuards
	{
		public static bool HasScrawl(Component c)
		{
			try
			{
				return (Object)(object)c != (Object)null && (Object)(object)c.GetComponent<UT_TextScrawl>() != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsInputChild(Graphic g)
		{
			try
			{
				if ((Object)(object)g == (Object)null)
				{
					return false;
				}
				InputField componentInParent = ((Component)g).GetComponentInParent<InputField>();
				if ((Object)(object)componentInParent != (Object)null && ((Object)(object)componentInParent.textComponent == (Object)(object)g || (Object)(object)componentInParent.placeholder == (Object)(object)g))
				{
					return true;
				}
				TMP_InputField componentInParent2 = ((Component)g).GetComponentInParent<TMP_InputField>();
				if ((Object)(object)componentInParent2 != (Object)null && ((Object)(object)componentInParent2.textComponent == (Object)(object)g || (Object)(object)componentInParent2.placeholder == (Object)(object)g))
				{
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsCommandConsole(Component c)
		{
			try
			{
				return (Object)(object)c != (Object)null && (Object)(object)c.GetComponentInParent<CommandConsole>() != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsTAnim(Component c)
		{
			try
			{
				return (Object)(object)c != (Object)null && (Object)(object)c.GetComponent<TAnimCore>() != (Object)null;
			}
			catch
			{
				return false;
			}
		}
	}
	public static class TextPipeline
	{
		private sealed class ResizeComponentState
		{
			public readonly ReloadableScaleState Scale = new ReloadableScaleState();

			public bool AlignmentInitialized;

			public int BaseAlignment;

			public int LastAppliedAlignment;
		}

		private sealed class TextDirectives
		{
			public float? TextSize;

			public bool NeedsAutoSize;
		}

		[ThreadStatic]
		public static bool SuppressSetterHooks;

		[ThreadStatic]
		public static int HookSuppressionDepth;

		[ThreadStatic]
		private static bool _needsAutoSize;

		[ThreadStatic]
		private static float? _explicitTextSize;

		public static bool Enabled = true;

		private static readonly ConditionalWeakTable<object, string> _sourceByComponent = new ConditionalWeakTable<object, string>();

		private static readonly ConditionalWeakTable<object, VersionedTextValue> _assigned = new ConditionalWeakTable<object, VersionedTextValue>();

		private static readonly ConditionalWeakTable<object, ResizeComponentState> _resized = new ConditionalWeakTable<object, ResizeComponentState>();

		private static readonly ConditionalWeakTable<object, TextDirectives> _directivesByComponent = new ConditionalWeakTable<object, TextDirectives>();

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

		private static readonly Regex _sizePct = new Regex("<size=(\\d+(?:\\.\\d+)?)%>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static int _translationGeneration;

		private static int _resizeGeneration;

		public static void InvalidateTranslationContext()
		{
			_translationGeneration++;
		}

		public static void InvalidateResizeRules()
		{
			_resizeGeneration++;
		}

		public static string AdaptLegacyRichText(Text t, string s)
		{
			if ((Object)(object)t == (Object)null || string.IsNullOrEmpty(s) || s.IndexOf("%>", StringComparison.Ordinal) < 0)
			{
				return s;
			}
			float baseSize = t.fontSize;
			float result;
			return _sizePct.Replace(s, (Match m) => float.TryParse(m.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? ("<size=" + Mathf.Max(1, Mathf.RoundToInt(baseSize * result / 100f)) + ">") : m.Value);
		}

		public static void PrepareInputRichText(InputField input, ref string text)
		{
			if (!((Object)(object)input == (Object)null) && !((Object)(object)input.textComponent == (Object)null))
			{
				input.textComponent.supportRichText = true;
				text = AdaptLegacyRichText(input.textComponent, text);
			}
		}

		public static string RecallSourceText(object c, string current)
		{
			if (c == null || !_sourceByComponent.TryGetValue(c, out string value))
			{
				return current;
			}
			return value;
		}

		public static void ApplyFont(object component)
		{
			if (component == null)
			{
				return;
			}
			TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null);
			string text;
			if (val == null)
			{
				Text val2 = (Text)((component is Text) ? component : null);
				text = ((val2 == null) ? null : val2.text);
			}
			else
			{
				text = val.text;
			}
			string text2 = text;
			if (text2 == null)
			{
				return;
			}
			string font = WkLocPlugin.Store.GetFont(RecallSourceText(component, text2));
			TMP_Text val3 = (TMP_Text)((component is TMP_Text) ? component : null);
			if (val3 != null)
			{
				FontProvider.TryApply(val3, FontProvider.GetTmp(font));
				return;
			}
			Text val4 = (Text)((component is Text) ? component : null);
			if (val4 != null)
			{
				FontProvider.TryApply(val4, FontProvider.GetLegacy(font));
			}
		}

		public static void ApplyAll(object component)
		{
			if (component == null)
			{
				return;
			}
			Component val = (Component)((component is Component) ? component : null);
			if (val != null && TextGuards.IsCommandConsole(val))
			{
				return;
			}
			TMP_Text val2 = (TMP_Text)((component is TMP_Text) ? component : null);
			if (val2 == null)
			{
				Text val3 = (Text)((component is Text) ? component : null);
				if (val3 == null)
				{
					InputField val4 = (InputField)((component is InputField) ? component : null);
					if (val4 == null)
					{
						TMP_InputField val5 = (TMP_InputField)((component is TMP_InputField) ? component : null);
						if (val5 != null)
						{
							TranslateInput(val5);
						}
					}
					else
					{
						TranslateInput(val4);
					}
				}
				else
				{
					if (Enabled)
					{
						ApplyFont(val3);
					}
					ResizeOnce(val3);
					if (!TextGuards.HasScrawl((Component)(object)val3) && !TextGuards.IsInputChild((Graphic)(object)val3))
					{
						TranslateInPlace(val3);
					}
				}
			}
			else
			{
				ResizeOnce(val2);
				bool flag = TextGuards.HasScrawl((Component)(object)val2);
				if (Enabled && !flag)
				{
					ApplyFont(val2);
				}
				if (!flag && !TextGuards.IsTAnim((Component)(object)val2) && !TextGuards.IsInputChild((Graphic)(object)val2))
				{
					TranslateInPlace(val2);
				}
			}
		}

		public static void ApplyAllInChildren(Component root, bool includeInactive = true)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			try
			{
				TMP_Text[] componentsInChildren = root.GetComponentsInChildren<TMP_Text>(includeInactive);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					ApplyAll(componentsInChildren[i]);
				}
				Text[] componentsInChildren2 = root.GetComponentsInChildren<Text>(includeInactive);
				for (int i = 0; i < componentsInChildren2.Length; i++)
				{
					ApplyAll(componentsInChildren2[i]);
				}
				InputField[] componentsInChildren3 = root.GetComponentsInChildren<InputField>(includeInactive);
				for (int i = 0; i < componentsInChildren3.Length; i++)
				{
					ApplyAll(componentsInChildren3[i]);
				}
				TMP_InputField[] componentsInChildren4 = root.GetComponentsInChildren<TMP_InputField>(includeInactive);
				for (int i = 0; i < componentsInChildren4.Length; i++)
				{
					ApplyAll(componentsInChildren4[i]);
				}
			}
			catch
			{
			}
		}

		private static void TranslateInPlace(object component)
		{
			TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null);
			string text;
			if (val == null)
			{
				Text val2 = (Text)((component is Text) ? component : null);
				text = ((val2 == null) ? null : val2.text);
			}
			else
			{
				text = val.text;
			}
			string text2 = text;
			if (text2 == null)
			{
				return;
			}
			string text3 = text2;
			Transform(component, ref text3);
			if (text3 != text2)
			{
				SuppressSetterHooks = true;
				try
				{
					TMP_Text val3 = (TMP_Text)((component is TMP_Text) ? component : null);
					if (val3 != null)
					{
						val3.text = text3;
					}
					else
					{
						Text val4 = (Text)((component is Text) ? component : null);
						if (val4 != null)
						{
							val4.text = text3;
						}
					}
				}
				finally
				{
					SuppressSetterHooks = false;
				}
			}
			PostTransform(component);
		}

		private static void TranslateInput(object component)
		{
			InputField val = (InputField)((component is InputField) ? component : null);
			string text;
			if (val == null)
			{
				TMP_InputField val2 = (TMP_InputField)((component is TMP_InputField) ? component : null);
				text = ((val2 == null) ? null : val2.text);
			}
			else
			{
				text = val.text;
			}
			string text2 = text;
			if (text2 == null)
			{
				return;
			}
			string text3 = text2;
			Transform(component, ref text3);
			if (text3 != text2)
			{
				SuppressSetterHooks = true;
				try
				{
					InputField val3 = (InputField)((component is InputField) ? component : null);
					if (val3 != null)
					{
						val3.text = text3;
					}
					else
					{
						TMP_InputField val4 = (TMP_InputField)((component is TMP_InputField) ? component : null);
						if (val4 != null)
						{
							val4.text = text3;
						}
					}
				}
				finally
				{
					SuppressSetterHooks = false;
				}
			}
			PostInputChanged(component);
		}

		private static string Trunc(string? s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				if (s.Length <= 40)
				{
					return s;
				}
				return s.Substring(0, 40) + "...";
			}
			return "<empty>";
		}

		private static string CharCodes(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char value in s)
			{
				stringBuilder.Append((int)value);
				stringBuilder.Append(' ');
			}
			return stringBuilder.ToString().TrimEnd();
		}

		public static void RememberSourceText(object component, string original)
		{
			if (component != null && !string.IsNullOrEmpty(original))
			{
				_sourceByComponent.Remove(component);
				_sourceByComponent.Add(component, original);
				CacheTextDirectives(component, original);
			}
		}

		public static void Transform(object component, ref string text)
		{
			if (SuppressSetterHooks || component == null || string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			Component val = (Component)((component is Component) ? component : null);
			if ((val != null && TextGuards.IsCommandConsole(val)) || IsAssigned(component, text) || !Enabled)
			{
				return;
			}
			string text2 = text;
			string text3 = WkLocPlugin.Store.Resolve(text2);
			string value;
			if (text3 != null && text3 != text)
			{
				_sourceByComponent.Remove(component);
				_sourceByComponent.Add(component, text2);
				CacheTextDirectives(component, text2);
			}
			else if (!_sourceByComponent.TryGetValue(component, out value))
			{
				_sourceByComponent.Add(component, text2);
				CacheTextDirectives(component, text2);
			}
			if (text3 == null || text3 == text)
			{
				SetPendingTextDirectives(component, text2);
				Text val2 = (Text)((component is Text) ? component : null);
				if (val2 != null)
				{
					string text4 = AdaptLegacyRichText(val2, text);
					if (!val2.supportRichText || text4 != text)
					{
						val2.supportRichText = true;
						text = text4;
					}
				}
				else
				{
					InputField val3 = (InputField)((component is InputField) ? component : null);
					if (val3 != null)
					{
						PrepareInputRichText(val3, ref text);
					}
				}
				Text val4 = (Text)((component is Text) ? component : null);
				if (val4 != null && WkLocPlugin.Cfg.DebugLog.Value)
				{
					Debug.Log((object)("[WKLoc] Process Text NO-MATCH '" + ((Object)((Component)val4).gameObject).name + "' orig='" + Trunc(text2) + "'"));
				}
				TMP_Text val5 = (TMP_Text)((component is TMP_Text) ? component : null);
				if (val5 != null && WkLocPlugin.Cfg.DebugLog.Value && _hasLetters.IsMatch(text2))
				{
					Debug.Log((object)string.Format("[WKLoc] Process TMP NO-MATCH '{0}' orig=[{1}] len={2} codes={3}", ((Object)((Component)val5).gameObject).name, text2, text2.Length, (text2.Length <= 40) ? CharCodes(text2) : "(long)"));
				}
				if (_hasLetters.IsMatch(text2))
				{
					TextCollector.Capture(text2);
				}
				SetAssigned(component, text);
				return;
			}
			if (WkLocPlugin.Cfg.DebugLog.Value)
			{
				string text5 = ((component is TMP_Text) ? "TMP" : "Text");
				TMP_Text val6 = (TMP_Text)((component is TMP_Text) ? component : null);
				if (val6 == null)
				{
					Text val7 = (Text)((component is Text) ? component : null);
					value = ((val7 == null) ? "?" : ((Object)((Component)val7).gameObject).name);
				}
				else
				{
					value = ((Object)((Component)val6).gameObject).name;
				}
				string text6 = value;
				Debug.Log((object)("[WKLoc] Process " + text5 + " HIT '" + text6 + "' orig='" + Trunc(text2) + "' -> '" + Trunc(text3) + "'"));
			}
			text = string.Concat(text3);
			Text val8 = (Text)((component is Text) ? component : null);
			if (val8 != null)
			{
				val8.supportRichText = true;
				text = AdaptLegacyRichText(val8, text);
			}
			else
			{
				InputField val9 = (InputField)((component is InputField) ? component : null);
				if (val9 != null)
				{
					PrepareInputRichText(val9, ref text);
				}
			}
			SetAssigned(component, text);
			string font = WkLocPlugin.Store.GetFont(text2);
			TMP_Text val10 = (TMP_Text)((component is TMP_Text) ? component : null);
			if (val10 != null)
			{
				FontProvider.TryApply(val10, FontProvider.GetTmp(font));
			}
			else
			{
				Text val11 = (Text)((component is Text) ? component : null);
				if (val11 != null)
				{
					FontProvider.TryApply(val11, FontProvider.GetLegacy(font));
				}
			}
			ResizeOnce(component);
			SetPendingTextDirectives(component, text2);
		}

		public static void TransformDetached(ref string text)
		{
			if (SuppressSetterHooks || string.IsNullOrWhiteSpace(text) || !Enabled)
			{
				return;
			}
			string text2 = text;
			string text3 = WkLocPlugin.Store.Resolve(text2);
			if (text3 == null || text3 == text)
			{
				if (_hasLetters.IsMatch(text2))
				{
					TextCollector.Capture(text2);
				}
			}
			else
			{
				text = string.Concat(text3);
			}
		}

		public static void Reapply(object component, bool applyFont = true)
		{
			if (component == null)
			{
				return;
			}
			Component val = (Component)((component is Component) ? component : null);
			if (val != null && TextGuards.IsCommandConsole(val))
			{
				return;
			}
			TMP_Text val2 = (TMP_Text)((component is TMP_Text) ? component : null);
			string text;
			if (val2 == null)
			{
				Text val3 = (Text)((component is Text) ? component : null);
				if (val3 == null)
				{
					InputField val4 = (InputField)((component is InputField) ? component : null);
					if (val4 == null)
					{
						TMP_InputField val5 = (TMP_InputField)((component is TMP_InputField) ? component : null);
						text = ((val5 == null) ? null : val5.text);
					}
					else
					{
						text = val4.text;
					}
				}
				else
				{
					text = val3.text;
				}
			}
			else
			{
				text = val2.text;
			}
			string text2 = text;
			if (text2 == null)
			{
				return;
			}
			string text3 = RecallSourceText(component, text2);
			string text4 = (Enabled ? (WkLocPlugin.Store.Resolve(text3) ?? text3) : text3);
			Text val6 = (Text)((component is Text) ? component : null);
			if (val6 != null)
			{
				val6.supportRichText = true;
				text4 = AdaptLegacyRichText(val6, text4);
			}
			else
			{
				InputField val7 = (InputField)((component is InputField) ? component : null);
				if (val7 != null)
				{
					PrepareInputRichText(val7, ref text4);
				}
			}
			if (WkLocPlugin.Cfg.DebugLog.Value)
			{
				Component val8 = (Component)((component is Component) ? component : null);
				if (val8 != null && (component is Text || component is InputField || component is TMP_InputField))
				{
					bool flag = ((component is InputField || component is TMP_InputField) ? true : false);
					Debug.Log((object)string.Format("[WKLoc] Reapply {0} '{1}' enabled={2} change={3} cur='{4}' orig='{5}' tgt='{6}'", flag ? "Input" : "Text", ((Object)val8.gameObject).name, Enabled, text2 != text4, Trunc(text2), Trunc(text3), Trunc(text4)));
				}
			}
			if (text2 != text4)
			{
				SuppressSetterHooks = true;
				try
				{
					TMP_Text val9 = (TMP_Text)((component is TMP_Text) ? component : null);
					if (val9 != null)
					{
						val9.text = text4;
					}
					else
					{
						Text val10 = (Text)((component is Text) ? component : null);
						if (val10 != null)
						{
							val10.text = text4;
						}
						else
						{
							InputField val11 = (InputField)((component is InputField) ? component : null);
							if (val11 != null)
							{
								val11.text = text4;
							}
							else
							{
								TMP_InputField val12 = (TMP_InputField)((component is TMP_InputField) ? component : null);
								if (val12 != null)
								{
									val12.text = text4;
								}
							}
						}
					}
				}
				finally
				{
					SuppressSetterHooks = false;
				}
			}
			SetAssigned(component, text4);
			CacheTextDirectives(component, text3);
			if (!Enabled)
			{
				return;
			}
			if ((component is TMP_Text || component is Text) ? true : false)
			{
				ResizeOnce(component);
			}
			else
			{
				InputField val13 = (InputField)((component is InputField) ? component : null);
				if (val13 != null && (Object)(object)val13.textComponent != (Object)null)
				{
					ResizeOnce(val13.textComponent);
				}
				else
				{
					TMP_InputField val14 = (TMP_InputField)((component is TMP_InputField) ? component : null);
					if (val14 != null && (Object)(object)val14.textComponent != (Object)null)
					{
						ResizeOnce(val14.textComponent);
					}
				}
			}
			string font = WkLocPlugin.Store.GetFont(text3);
			Text val15 = (Text)((component is Text) ? component : null);
			if (val15 != null)
			{
				FontProvider.TryApply(val15, FontProvider.GetLegacy(font));
			}
			else if (applyFont)
			{
				TMP_Text val16 = (TMP_Text)((component is TMP_Text) ? component : null);
				if (val16 != null)
				{
					FontProvider.TryApply(val16, FontProvider.GetTmp(font));
				}
			}
			InputField val17 = (InputField)((component is InputField) ? component : null);
			if (val17 != null)
			{
				OsFileBackplateRefresher.Queue(((Component)val17).GetComponentInParent<OS_File>());
			}
			else
			{
				TMP_InputField val18 = (TMP_InputField)((component is TMP_InputField) ? component : null);
				if (val18 != null)
				{
					OsFileBackplateRefresher.Queue(((Component)val18).GetComponentInParent<OS_File>());
				}
			}
			if (applyFont)
			{
				SetPendingTextDirectives(component, text3);
				PostTransform(component);
			}
		}

		public static void ResizeOnce(object component)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected I4, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected I4, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected I4, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected I4, but got Unknown
			if (!Enabled || component == null || (!(component is TMP_Text) && !(component is Text)))
			{
				return;
			}
			ResizeComponentState orCreateValue = _resized.GetOrCreateValue(component);
			if (!orCreateValue.Scale.NeedsApply(_resizeGeneration))
			{
				return;
			}
			bool suppressSetterHooks = SuppressSetterHooks;
			SuppressSetterHooks = true;
			try
			{
				TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null);
				if (val != null)
				{
					int num = (int)val.alignment;
					if (!orCreateValue.AlignmentInitialized || num != orCreateValue.LastAppliedAlignment)
					{
						orCreateValue.BaseAlignment = num;
					}
					val.alignment = (TextAlignmentOptions)orCreateValue.BaseAlignment;
					float baseSize = orCreateValue.Scale.BeginApply(_resizeGeneration, val.fontSize);
					UiResizer.Apply(val, baseSize);
					orCreateValue.Scale.CompleteApply(val.fontSize);
					orCreateValue.LastAppliedAlignment = (int)val.alignment;
					orCreateValue.AlignmentInitialized = true;
					return;
				}
				Text val2 = (Text)((component is Text) ? component : null);
				if (val2 != null)
				{
					int num2 = (int)val2.alignment;
					if (!orCreateValue.AlignmentInitialized || num2 != orCreateValue.LastAppliedAlignment)
					{
						orCreateValue.BaseAlignment = num2;
					}
					val2.alignment = (TextAnchor)orCreateValue.BaseAlignment;
					float baseSize2 = orCreateValue.Scale.BeginApply(_resizeGeneration, (float)val2.fontSize);
					UiResizer.Apply(val2, baseSize2);
					orCreateValue