Decompiled source of GameTranslator v2.0.6

BepInEx/core/XUnity.Common.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using XUnity.Common.Constants;
using XUnity.Common.Extensions;
using XUnity.Common.Logging;
using XUnity.Common.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("XUnity.AutoTranslator.Plugin.Core")]
[assembly: AssemblyCompany("gravydevsupreme")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Common dependencies shared between XUnity Auto Translator and Resource Redirector.")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("XUnity.Common")]
[assembly: AssemblyTitle("XUnity.Common")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
namespace XUnity.Common.Utilities
{
	public static class ArrayHelper
	{
		public static T[] Null<T>()
		{
			return null;
		}
	}
	public static class CabHelper
	{
		private static string CreateRandomCab()
		{
			return "CAB-" + Guid.NewGuid().ToString("N");
		}

		public static void RandomizeCab(byte[] assetBundleData)
		{
			string @string = Encoding.ASCII.GetString(assetBundleData, 0, Math.Min(1024, assetBundleData.Length - 4));
			int num = @string.IndexOf("CAB-", StringComparison.Ordinal);
			if (num >= 0)
			{
				int num2 = @string.Substring(num).IndexOf('\0');
				if (num2 >= 0 && num2 <= 36)
				{
					string s = CreateRandomCab();
					Buffer.BlockCopy(Encoding.ASCII.GetBytes(s), 36 - num2, assetBundleData, num, num2);
				}
			}
		}

		public static void RandomizeCabWithAnyLength(byte[] assetBundleData)
		{
			FindAndReplaceCab("CAB-", 0, assetBundleData, 2048);
		}

		private static void FindAndReplaceCab(string ansiStringToStartWith, byte byteToEndWith, byte[] data, int maxIterations = -1)
		{
			int num = Math.Min(data.Length, maxIterations);
			if (num == -1)
			{
				num = data.Length;
			}
			int num2 = 0;
			int length = ansiStringToStartWith.Length;
			string text = Guid.NewGuid().ToString("N");
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				char c = (char)data[i];
				if (num2 == length)
				{
					while (data[i] != byteToEndWith && i < num)
					{
						if (num3 >= text.Length)
						{
							num3 = 0;
							text = Guid.NewGuid().ToString("N");
						}
						data[i++] = (byte)text[num3++];
					}
					break;
				}
				num2 = ((c == ansiStringToStartWith[num2]) ? (num2 + 1) : 0);
			}
		}
	}
	internal static class CecilFastReflectionHelper
	{
		private static readonly Type[] DynamicMethodDelegateArgs = new Type[2]
		{
			typeof(object),
			typeof(object[])
		};

		public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs);
			ILProcessor iLProcessor = val.GetILProcessor();
			ParameterInfo[] parameters = method.GetParameters();
			bool flag = true;
			if (!method.IsStatic)
			{
				iLProcessor.Emit(OpCodes.Ldarg_0);
				if (method.DeclaringType.IsValueType)
				{
					Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, method.DeclaringType);
				}
			}
			for (int i = 0; i < parameters.Length; i++)
			{
				Type type = parameters[i].ParameterType;
				bool isByRef = type.IsByRef;
				if (isByRef)
				{
					type = type.GetElementType();
				}
				bool isValueType = type.IsValueType;
				if (isByRef && isValueType && !directBoxValueAccess)
				{
					iLProcessor.Emit(OpCodes.Ldarg_1);
					iLProcessor.Emit(OpCodes.Ldc_I4, i);
				}
				iLProcessor.Emit(OpCodes.Ldarg_1);
				iLProcessor.Emit(OpCodes.Ldc_I4, i);
				if (isByRef && !isValueType)
				{
					Extensions.Emit(iLProcessor, OpCodes.Ldelema, typeof(object));
					continue;
				}
				iLProcessor.Emit(OpCodes.Ldelem_Ref);
				if (!isValueType)
				{
					continue;
				}
				if (!isByRef || !directBoxValueAccess)
				{
					Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, type);
					if (isByRef)
					{
						Extensions.Emit(iLProcessor, OpCodes.Box, type);
						iLProcessor.Emit(OpCodes.Dup);
						Extensions.Emit(iLProcessor, OpCodes.Unbox, type);
						if (flag)
						{
							flag = false;
							val.Definition.Body.Variables.Add(new VariableDefinition((TypeReference)new PinnedType((TypeReference)new PointerType(((MemberReference)val.Definition).Module.TypeSystem.Void))));
						}
						iLProcessor.Emit(OpCodes.Stloc_0);
						iLProcessor.Emit(OpCodes.Stelem_Ref);
						iLProcessor.Emit(OpCodes.Ldloc_0);
					}
				}
				else
				{
					Extensions.Emit(iLProcessor, OpCodes.Unbox, type);
				}
			}
			if (method.IsConstructor)
			{
				Extensions.Emit(iLProcessor, OpCodes.Newobj, (MethodBase)(method as ConstructorInfo));
			}
			else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall)
			{
				Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)(method as MethodInfo));
			}
			else
			{
				Extensions.Emit(iLProcessor, OpCodes.Callvirt, (MethodBase)(method as MethodInfo));
			}
			Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType);
			if ((object)type2 != typeof(void))
			{
				if (type2.IsValueType)
				{
					Extensions.Emit(iLProcessor, OpCodes.Box, type2);
				}
			}
			else
			{
				iLProcessor.Emit(OpCodes.Ldnull);
			}
			iLProcessor.Emit(OpCodes.Ret);
			return (FastReflectionDelegate)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(FastReflectionDelegate));
		}

		public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			if ((object)fieldInfo == null)
			{
				throw new ArgumentNullException("fieldInfo");
			}
			if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
			{
				throw new ArgumentException("FieldInfo type does not match return type.");
			}
			if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
			{
				throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
			}
			DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">", typeof(F), new Type[1] { typeof(T) });
			ILProcessor iLProcessor = val.GetILProcessor();
			if (!fieldInfo.IsStatic)
			{
				iLProcessor.Emit(OpCodes.Ldarg_0);
				Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType);
			}
			Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo);
			if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
			{
				Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType);
			}
			iLProcessor.Emit(OpCodes.Ret);
			return (Func<T, F>)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(Func<T, F>));
		}

		public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo)
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			if ((object)fieldInfo == null)
			{
				throw new ArgumentNullException("fieldInfo");
			}
			if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
			{
				throw new ArgumentException("FieldInfo type does not match argument type.");
			}
			if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
			{
				throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
			}
			DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">", (Type)null, new Type[2]
			{
				typeof(T),
				typeof(F)
			});
			ILProcessor iLProcessor = val.GetILProcessor();
			if (!fieldInfo.IsStatic)
			{
				iLProcessor.Emit(OpCodes.Ldarg_0);
				Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType);
			}
			iLProcessor.Emit(OpCodes.Ldarg_1);
			if ((object)fieldInfo.FieldType != typeof(F))
			{
				if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
				{
					if (fieldInfo.FieldType.IsValueType)
					{
						Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, fieldInfo.FieldType);
					}
					else
					{
						Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType);
					}
				}
				else
				{
					Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.FieldType);
				}
			}
			Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
			iLProcessor.Emit(OpCodes.Ret);
			return (Action<T, F>)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(Action<T, F>));
		}
	}
	public static class CustomFastReflectionHelper
	{
		private struct FastReflectionDelegateKey
		{
			public MethodBase Method { get; }

			public bool DirectBoxValueAccess { get; }

			public bool ForceNonVirtCall { get; }

			public FastReflectionDelegateKey(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall)
			{
				Method = method;
				DirectBoxValueAccess = directBoxValueAccess;
				ForceNonVirtCall = forceNonVirtCall;
			}

			public override bool Equals(object obj)
			{
				if (obj is FastReflectionDelegateKey fastReflectionDelegateKey && EqualityComparer<MethodBase>.Default.Equals(Method, fastReflectionDelegateKey.Method) && DirectBoxValueAccess == fastReflectionDelegateKey.DirectBoxValueAccess)
				{
					return ForceNonVirtCall == fastReflectionDelegateKey.ForceNonVirtCall;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return ((1017116076 * -1521134295 + EqualityComparer<MethodBase>.Default.GetHashCode(Method)) * -1521134295 + DirectBoxValueAccess.GetHashCode()) * -1521134295 + ForceNonVirtCall.GetHashCode();
			}
		}

		private static readonly Dictionary<FastReflectionDelegateKey, FastReflectionDelegate> MethodCache = new Dictionary<FastReflectionDelegateKey, FastReflectionDelegate>();

		public static FastReflectionDelegate CreateFastDelegate(this MethodBase method, bool directBoxValueAccess = true, bool forceNonVirtCall = false)
		{
			FastReflectionDelegateKey key = new FastReflectionDelegateKey(method, directBoxValueAccess, forceNonVirtCall);
			if (MethodCache.TryGetValue(key, out var value))
			{
				return value;
			}
			value = (((object)ClrTypes.DynamicMethodDefinition == null) ? GetFastDelegateForSRE(method, directBoxValueAccess, forceNonVirtCall) : GetFastDelegateForCecil(method, directBoxValueAccess, forceNonVirtCall));
			MethodCache.Add(key, value);
			return value;
		}

		public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo)
		{
			if ((object)ClrTypes.DynamicMethodDefinition != null)
			{
				return CreateFastFieldGetterForCecil<T, F>(fieldInfo);
			}
			return CreateFastFieldGetterForSRE<T, F>(fieldInfo);
		}

		public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo)
		{
			if ((object)ClrTypes.DynamicMethodDefinition != null)
			{
				return CreateFastFieldSetterForCecil<T, F>(fieldInfo);
			}
			return CreateFastFieldSetterForSRE<T, F>(fieldInfo);
		}

		private static FastReflectionDelegate GetFastDelegateForCecil(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall)
		{
			try
			{
				return CecilFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall);
			}
			catch (Exception e)
			{
				try
				{
					XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit...");
					return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall);
				}
				catch (Exception e2)
				{
					XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
					return (object target, object[] args) => method.Invoke(target, args);
				}
			}
		}

		private static Func<T, F> CreateFastFieldGetterForCecil<T, F>(FieldInfo fieldInfo)
		{
			try
			{
				return CecilFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo);
			}
			catch (Exception e)
			{
				try
				{
					XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit...");
					return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo);
				}
				catch (Exception e2)
				{
					XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
					return (T target) => (F)fieldInfo.GetValue(target);
				}
			}
		}

		private static Action<T, F> CreateFastFieldSetterForCecil<T, F>(FieldInfo fieldInfo)
		{
			try
			{
				return CecilFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo);
			}
			catch (Exception e)
			{
				try
				{
					XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit...");
					return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo);
				}
				catch (Exception e2)
				{
					XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
					return delegate(T target, F value)
					{
						fieldInfo.SetValue(target, value);
					};
				}
			}
		}

		private static FastReflectionDelegate GetFastDelegateForSRE(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall)
		{
			try
			{
				return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall);
			}
			catch (Exception e)
			{
				XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
				return (object target, object[] args) => method.Invoke(target, args);
			}
		}

		private static Func<T, F> CreateFastFieldGetterForSRE<T, F>(FieldInfo fieldInfo)
		{
			try
			{
				return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo);
			}
			catch (Exception e)
			{
				XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
				return (T target) => (F)fieldInfo.GetValue(target);
			}
		}

		private static Action<T, F> CreateFastFieldSetterForSRE<T, F>(FieldInfo fieldInfo)
		{
			try
			{
				return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo);
			}
			catch (Exception e)
			{
				XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection...");
				return delegate(T target, F value)
				{
					fieldInfo.SetValue(target, value);
				};
			}
		}
	}
	public static class DiacriticHelper
	{
		public static string RemoveAllDiacritics(this string input)
		{
			return new string((from c in input.SafeNormalize(NormalizationForm.FormD)
				where CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark
				select c).ToArray()).SafeNormalize();
		}

		private static string SafeNormalize(this string input, NormalizationForm normalizationForm = NormalizationForm.FormC)
		{
			return ReplaceNonCharacters(input, '?').Normalize(normalizationForm);
		}

		private static string ReplaceNonCharacters(string input, char replacement)
		{
			StringBuilder stringBuilder = new StringBuilder(input.Length);
			for (int i = 0; i < input.Length; i++)
			{
				if (char.IsSurrogatePair(input, i))
				{
					int num = char.ConvertToUtf32(input, i);
					i++;
					if (IsValidCodePoint(num))
					{
						stringBuilder.Append(char.ConvertFromUtf32(num));
					}
					else
					{
						stringBuilder.Append(replacement);
					}
				}
				else
				{
					char c = input[i];
					if (IsValidCodePoint(c))
					{
						stringBuilder.Append(c);
					}
					else
					{
						stringBuilder.Append(replacement);
					}
				}
			}
			return stringBuilder.ToString();
		}

		private static bool IsValidCodePoint(int point)
		{
			if (point >= 64976)
			{
				if (point >= 65008 && (point & 0xFFFF) != 65535 && (point & 0xFFFE) != 65534)
				{
					return point <= 1114111;
				}
				return false;
			}
			return true;
		}
	}
	public static class ExpressionHelper
	{
		public static Delegate CreateTypedFastInvoke(MethodBase method)
		{
			if ((object)method == null)
			{
				throw new ArgumentNullException("method");
			}
			return CreateTypedFastInvokeUnchecked(method);
		}

		public static Delegate CreateTypedFastInvokeUnchecked(MethodBase method)
		{
			if ((object)method == null)
			{
				return null;
			}
			if (method.IsGenericMethod)
			{
				throw new ArgumentException("The provided method must not be generic.", "method");
			}
			if (method is MethodInfo methodInfo)
			{
				Expression[] arguments;
				if (method.IsStatic)
				{
					ParameterExpression[] array = (from p in methodInfo.GetParameters()
						select Expression.Parameter(p.ParameterType, p.Name)).ToArray();
					arguments = array;
					return Expression.Lambda(Expression.Call(null, methodInfo, arguments), array).Compile();
				}
				List<ParameterExpression> list = (from p in methodInfo.GetParameters()
					select Expression.Parameter(p.ParameterType, p.Name)).ToList();
				list.Insert(0, Expression.Parameter(methodInfo.DeclaringType, "instance"));
				ParameterExpression instance = list[0];
				arguments = list.Skip(1).ToArray();
				return Expression.Lambda(Expression.Call(instance, methodInfo, arguments), list.ToArray()).Compile();
			}
			if (method is ConstructorInfo constructorInfo)
			{
				ParameterExpression[] array2 = (from p in constructorInfo.GetParameters()
					select Expression.Parameter(p.ParameterType, p.Name)).ToArray();
				Expression[] arguments = array2;
				return Expression.Lambda(Expression.New(constructorInfo, arguments), array2).Compile();
			}
			throw new ArgumentException("method", "This method only supports MethodInfo and ConstructorInfo.");
		}
	}
	public static class ExtensionDataHelper
	{
		private static readonly object Sync;

		private static readonly WeakDictionary<object, object> WeakDynamicFields;

		public static int WeakReferenceCount
		{
			get
			{
				lock (Sync)
				{
					return WeakDynamicFields.Count;
				}
			}
		}

		static ExtensionDataHelper()
		{
			Sync = new object();
			WeakDynamicFields = new WeakDictionary<object, object>();
			MaintenanceHelper.AddMaintenanceFunction(Cull, 12);
		}

		public static void SetExtensionData<T>(this object obj, T t)
		{
			lock (Sync)
			{
				if (WeakDynamicFields.TryGetValue(obj, out var value))
				{
					if (value is Dictionary<Type, object> dictionary)
					{
						dictionary[typeof(T)] = t;
						return;
					}
					Dictionary<Type, object> dictionary2 = new Dictionary<Type, object>();
					dictionary2.Add(value.GetType(), value);
					dictionary2[typeof(T)] = t;
					WeakDynamicFields[obj] = dictionary2;
				}
				else
				{
					WeakDynamicFields[obj] = t;
				}
			}
		}

		public static T GetOrCreateExtensionData<T>(this object obj) where T : new()
		{
			if (obj == null)
			{
				return default(T);
			}
			lock (Sync)
			{
				if (WeakDynamicFields.TryGetValue(obj, out var value))
				{
					if (value is Dictionary<Type, object> dictionary)
					{
						if (dictionary.TryGetValue(typeof(T), out value))
						{
							return (T)value;
						}
						T val = new T();
						dictionary[typeof(T)] = val;
						return val;
					}
					if (!(value is T result))
					{
						Dictionary<Type, object> dictionary2 = new Dictionary<Type, object>();
						dictionary2.Add(value.GetType(), value);
						T val2 = new T();
						dictionary2[typeof(T)] = val2;
						WeakDynamicFields[obj] = dictionary2;
						return val2;
					}
					return result;
				}
				T val3 = new T();
				WeakDynamicFields[obj] = val3;
				return val3;
			}
		}

		public static T GetExtensionData<T>(this object obj)
		{
			if (obj == null)
			{
				return default(T);
			}
			lock (Sync)
			{
				if (WeakDynamicFields.TryGetValue(obj, out var value))
				{
					if (value is Dictionary<Type, object> dictionary && dictionary.TryGetValue(typeof(T), out value))
					{
						if (!(value is T result))
						{
							return default(T);
						}
						return result;
					}
					if (!(value is T result2))
					{
						return default(T);
					}
					return result2;
				}
			}
			return default(T);
		}

		public static void Cull()
		{
			lock (Sync)
			{
				WeakDynamicFields.RemoveCollectedEntries();
			}
		}

		public static List<KeyValuePair<object, object>> GetAllRegisteredObjects()
		{
			lock (Sync)
			{
				return IterateAllPairs().ToList();
			}
		}

		public static void Remove(object obj)
		{
			lock (Sync)
			{
				WeakDynamicFields.Remove(obj);
			}
		}

		private static IEnumerable<KeyValuePair<object, object>> IterateAllPairs()
		{
			foreach (KeyValuePair<object, object> kvp in WeakDynamicFields)
			{
				if (kvp.Value is Dictionary<Type, object> dictionary)
				{
					foreach (KeyValuePair<Type, object> item in dictionary)
					{
						yield return new KeyValuePair<object, object>(kvp.Key, item.Value);
					}
				}
				else
				{
					yield return kvp;
				}
			}
		}
	}
	public delegate object FastReflectionDelegate(object target, params object[] args);
	public static class HookingHelper
	{
		private static readonly MethodInfo PatchMethod12;

		private static readonly MethodInfo PatchMethod20;

		private static readonly object Harmony;

		private static bool _loggedHarmonyError;

		static HookingHelper()
		{
			PatchMethod12 = ClrTypes.HarmonyInstance?.GetMethod("Patch", new Type[4]
			{
				ClrTypes.MethodBase,
				ClrTypes.HarmonyMethod,
				ClrTypes.HarmonyMethod,
				ClrTypes.HarmonyMethod
			});
			PatchMethod20 = ClrTypes.Harmony?.GetMethod("Patch", new Type[5]
			{
				ClrTypes.MethodBase,
				ClrTypes.HarmonyMethod,
				ClrTypes.HarmonyMethod,
				ClrTypes.HarmonyMethod,
				ClrTypes.HarmonyMethod
			});
			_loggedHarmonyError = false;
			try
			{
				if ((object)ClrTypes.HarmonyInstance != null)
				{
					Harmony = ClrTypes.HarmonyInstance.GetMethod("Create", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[1] { "xunity.common.hookinghelper" });
				}
				else if ((object)ClrTypes.Harmony != null)
				{
					Harmony = ClrTypes.Harmony.GetConstructor(new Type[1] { typeof(string) }).Invoke(new object[1] { "xunity.common.hookinghelper" });
				}
				else
				{
					XuaLogger.Common.Error("An unexpected exception occurred during harmony initialization, likely caused by unknown Harmony version. Harmony hooks will be unavailable!");
				}
			}
			catch (Exception e)
			{
				XuaLogger.Common.Error(e, "An unexpected exception occurred during harmony initialization. Harmony hooks will be unavailable!");
			}
		}

		public static void PatchAll(IEnumerable<Type> types, bool forceExternHooks)
		{
			foreach (Type type in types)
			{
				PatchType(type, forceExternHooks);
			}
		}

		public static void PatchAll(IEnumerable<Type[]> types, bool forceMonoModHooks)
		{
			foreach (Type[] type in types)
			{
				for (int i = 0; i < type.Length && !PatchType(type[i], forceMonoModHooks); i++)
				{
				}
			}
		}

		public static bool PatchType(Type type, bool forceExternHooks)
		{
			MethodBase methodBase = null;
			IntPtr intPtr = IntPtr.Zero;
			try
			{
				if (Harmony == null && !_loggedHarmonyError)
				{
					_loggedHarmonyError = true;
					XuaLogger.Common.Warn("Harmony is not loaded or could not be initialized. Using fallback hooks instead.");
				}
				BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
				MethodInfo method = type.GetMethod("Prepare", bindingAttr);
				if ((object)method == null || (bool)method.Invoke(null, new object[1] { Harmony }))
				{
					try
					{
						methodBase = (MethodBase)(type.GetMethod("TargetMethod", bindingAttr)?.Invoke(null, new object[1] { Harmony }));
					}
					catch
					{
					}
					try
					{
						intPtr = ((IntPtr?)type.GetMethod("TargetMethodPointer", bindingAttr)?.Invoke(null, null)) ?? IntPtr.Zero;
					}
					catch
					{
					}
					if ((object)methodBase == null && intPtr == IntPtr.Zero)
					{
						if ((object)methodBase != null)
						{
							XuaLogger.Common.Warn("Could not hook '" + methodBase.DeclaringType.FullName + "." + methodBase.Name + "'. Likely due differences between different versions of the engine or text framework.");
						}
						else
						{
							XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework.");
						}
						return false;
					}
					MethodInfo method2 = type.GetMethod("Prefix", bindingAttr);
					MethodInfo method3 = type.GetMethod("Postfix", bindingAttr);
					MethodInfo method4 = type.GetMethod("Finalizer", bindingAttr);
					if ((object)methodBase == null || forceExternHooks || Harmony == null || ((object)method2 == null && (object)method3 == null && (object)method4 == null))
					{
						return PatchWithExternHooks(type, methodBase, intPtr, forced: true);
					}
					if ((object)methodBase != null)
					{
						try
						{
							int? priority = type.GetCustomAttributes(typeof(HookingHelperPriorityAttribute), inherit: false).OfType<HookingHelperPriorityAttribute>().FirstOrDefault()?.priority;
							object obj3 = (((object)method2 != null) ? CreateHarmonyMethod(method2, priority) : null);
							object obj4 = (((object)method3 != null) ? CreateHarmonyMethod(method3, priority) : null);
							object obj5 = (((object)method4 != null) ? CreateHarmonyMethod(method4, priority) : null);
							if ((object)PatchMethod12 != null)
							{
								PatchMethod12.Invoke(Harmony, new object[4] { methodBase, obj3, obj4, null });
							}
							else
							{
								PatchMethod20.Invoke(Harmony, new object[5] { methodBase, obj3, obj4, null, obj5 });
							}
							XuaLogger.Common.Debug("Hooked " + methodBase.DeclaringType.FullName + "." + methodBase.Name + " through Harmony hooks.");
							return true;
						}
						catch (Exception e) when (((Func<bool>)delegate
						{
							// Could not convert BlockContainer to single expression
							System.Runtime.CompilerServices.Unsafe.SkipInit(out int num);
							if (e.FirstInnerExceptionOfType<PlatformNotSupportedException>() == null)
							{
								ArgumentException ex = e.FirstInnerExceptionOfType<ArgumentException>();
								num = ((ex != null && ex.Message?.Contains("no body") == true) ? 1 : 0);
							}
							else
							{
								num = 1;
							}
							return num != 0;
						}).Invoke())
						{
							return PatchWithExternHooks(type, methodBase, intPtr, forced: false);
						}
					}
					XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework.");
				}
			}
			catch (Exception e2)
			{
				if ((object)methodBase != null)
				{
					XuaLogger.Common.Warn(e2, "An error occurred while patching property/method '" + methodBase.DeclaringType.FullName + "." + methodBase.Name + "'. Failing hook: '" + type.Name + "'.");
				}
				else
				{
					XuaLogger.Common.Warn(e2, "An error occurred while patching property/method. Failing hook: '" + type.Name + "'.");
				}
			}
			return false;
		}

		private static bool PatchWithExternHooks(Type type, MethodBase original, IntPtr originalPtr, bool forced)
		{
			BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			if ((object)ClrTypes.Imports != null)
			{
				if (originalPtr == IntPtr.Zero)
				{
					XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework.");
					return false;
				}
				IntPtr? intPtr = type.GetMethod("ML_Detour", bindingAttr)?.MethodHandle.GetFunctionPointer();
				if (intPtr.HasValue && intPtr.Value != IntPtr.Zero)
				{
					ClrTypes.Imports.GetMethod("Hook", bindingAttr).Invoke(null, new object[2] { originalPtr, intPtr.Value });
					XuaLogger.Common.Debug("Hooked " + type.Name + " through MelonMod Imports.Hook method.");
					return true;
				}
				XuaLogger.Common.Warn("Could not hook '" + type.Name + "' because no detour method was found.");
			}
			else
			{
				if ((object)original == null)
				{
					XuaLogger.Common.Warn("Cannot hook '" + type.Name + "'. Could not locate the original method. Failing hook: '" + type.Name + "'.");
					return false;
				}
				if ((object)ClrTypes.Hook == null || (object)ClrTypes.NativeDetour == null)
				{
					XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. MonoMod hooks is not supported in this runtime as MonoMod is not loaded. Failing hook: '" + type.Name + "'.");
					return false;
				}
				object obj = type.GetMethod("Get_MM_Detour", bindingAttr)?.Invoke(null, null) ?? type.GetMethod("MM_Detour", bindingAttr);
				if (obj != null)
				{
					string text = "(managed)";
					object obj2;
					try
					{
						obj2 = ClrTypes.Hook.GetConstructor(new Type[2]
						{
							typeof(MethodBase),
							typeof(MethodInfo)
						}).Invoke(new object[2] { original, obj });
						obj2.GetType().GetMethod("Apply").Invoke(obj2, null);
					}
					catch (Exception e) when (((Func<bool>)delegate
					{
						// Could not convert BlockContainer to single expression
						System.Runtime.CompilerServices.Unsafe.SkipInit(out int num);
						if (e.FirstInnerExceptionOfType<NullReferenceException>() == null)
						{
							NotSupportedException ex = e.FirstInnerExceptionOfType<NotSupportedException>();
							num = ((ex != null && ex.Message?.Contains("Body-less") == true) ? 1 : 0);
						}
						else
						{
							num = 1;
						}
						return num != 0;
					}).Invoke())
					{
						text = "(native)";
						obj2 = ClrTypes.NativeDetour.GetConstructor(new Type[2]
						{
							typeof(MethodBase),
							typeof(MethodBase)
						}).Invoke(new object[2] { original, obj });
						obj2.GetType().GetMethod("Apply").Invoke(obj2, null);
					}
					type.GetMethod("MM_Init", bindingAttr)?.Invoke(null, new object[1] { obj2 });
					if (forced)
					{
						XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through forced MonoMod hooks. " + text);
					}
					else
					{
						XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through MonoMod hooks. " + text);
					}
					return true;
				}
				if (forced)
				{
					XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'.");
				}
				else
				{
					XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'.");
				}
			}
			return false;
		}

		private static object CreateHarmonyMethod(MethodInfo method, int? priority)
		{
			object obj = ClrTypes.HarmonyMethod.GetConstructor(new Type[1] { typeof(MethodInfo) }).Invoke(new object[1] { method });
			if (priority.HasValue)
			{
				(ClrTypes.HarmonyMethod.GetField("priority", BindingFlags.Instance | BindingFlags.Public) ?? ClrTypes.HarmonyMethod.GetField("prioritiy", BindingFlags.Instance | BindingFlags.Public)).SetValue(obj, priority.Value);
			}
			return obj;
		}
	}
	public class HookingHelperPriorityAttribute : Attribute
	{
		public int priority;

		public HookingHelperPriorityAttribute(int priority)
		{
			this.priority = priority;
		}
	}
	public static class HookPriority
	{
		public const int Last = 0;

		public const int VeryLow = 100;

		public const int Low = 200;

		public const int LowerThanNormal = 300;

		public const int Normal = 400;

		public const int HigherThanNormal = 500;

		public const int High = 600;

		public const int VeryHigh = 700;

		public const int First = 800;
	}
	public static class ListExtensions
	{
		public static void BinarySearchInsert<T>(this List<T> items, T item) where T : IComparable<T>
		{
			int num = items.BinarySearch(item);
			if (num < 0)
			{
				items.Insert(~num, item);
			}
			else
			{
				items.Insert(num, item);
			}
		}
	}
	public static class MaintenanceHelper
	{
		private class ActionRegistration
		{
			public Action Action { get; }

			public int Filter { get; }

			public ActionRegistration(Action action, int filter)
			{
				Action = action;
				Filter = filter;
			}
		}

		private static readonly object Sync = new object();

		private static readonly List<ActionRegistration> RegisteredActions = new List<ActionRegistration>();

		private static bool _initialized;

		public static void AddMaintenanceFunction(Action action, int filter)
		{
			lock (Sync)
			{
				if (!_initialized)
				{
					_initialized = true;
					StartMaintenance();
				}
				ActionRegistration item = new ActionRegistration(action, filter);
				RegisteredActions.Add(item);
			}
		}

		private static void StartMaintenance()
		{
			Thread thread = new Thread(MaintenanceLoop);
			thread.IsBackground = true;
			thread.Start();
		}

		private static void MaintenanceLoop(object state)
		{
			int num = 0;
			while (true)
			{
				lock (Sync)
				{
					foreach (ActionRegistration registeredAction in RegisteredActions)
					{
						if (num % registeredAction.Filter == 0)
						{
							try
							{
								registeredAction.Action();
							}
							catch (Exception e)
							{
								XuaLogger.Common.Error(e, "An unexpected error occurred during maintenance.");
							}
						}
					}
				}
				num++;
				Thread.Sleep(5000);
			}
		}
	}
	public static class Paths
	{
		private static string _gameRoot;

		public static string GameRoot
		{
			get
			{
				return _gameRoot ?? GetAndSetGameRoot();
			}
			set
			{
				_gameRoot = value;
			}
		}

		public static void Initialize()
		{
			GetAndSetGameRoot();
		}

		private static string GetAndSetGameRoot()
		{
			return _gameRoot = new DirectoryInfo(Application.dataPath).Parent.FullName;
		}
	}
	public static class ReflectionCache
	{
		private struct MemberLookupKey
		{
			public Type Type { get; set; }

			public string MemberName { get; set; }

			public MemberLookupKey(Type type, string memberName)
			{
				Type = type;
				MemberName = memberName;
			}

			public override bool Equals(object obj)
			{
				if (obj is MemberLookupKey memberLookupKey)
				{
					if ((object)Type == memberLookupKey.Type)
					{
						return MemberName == memberLookupKey.MemberName;
					}
					return false;
				}
				return false;
			}

			public override int GetHashCode()
			{
				return Type.GetHashCode() + MemberName.GetHashCode();
			}
		}

		private static Dictionary<MemberLookupKey, CachedMethod> Methods = new Dictionary<MemberLookupKey, CachedMethod>();

		private static Dictionary<MemberLookupKey, CachedProperty> Properties = new Dictionary<MemberLookupKey, CachedProperty>();

		private static Dictionary<MemberLookupKey, CachedField> Fields = new Dictionary<MemberLookupKey, CachedField>();

		public static CachedMethod CachedMethod(this Type type, string name)
		{
			return type.CachedMethod(name, (Type[])null);
		}

		public static CachedMethod CachedMethod(this Type type, string name, params Type[] types)
		{
			MemberLookupKey key = new MemberLookupKey(type, name);
			if (!Methods.TryGetValue(key, out var value))
			{
				Type type2 = type;
				MethodInfo methodInfo = null;
				while ((object)methodInfo == null && (object)type2 != null)
				{
					methodInfo = ((types != null && types.Length != 0) ? type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) : type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
					type2 = type2.BaseType;
				}
				if ((object)methodInfo != null)
				{
					value = new CachedMethod(methodInfo);
				}
				Methods[key] = value;
			}
			return value;
		}

		public static CachedProperty CachedProperty(this Type type, string name)
		{
			MemberLookupKey key = new MemberLookupKey(type, name);
			if (!Properties.TryGetValue(key, out var value))
			{
				Type type2 = type;
				PropertyInfo propertyInfo = null;
				while ((object)propertyInfo == null && (object)type2 != null)
				{
					propertyInfo = type2.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					type2 = type2.BaseType;
				}
				if ((object)propertyInfo != null)
				{
					value = new CachedProperty(propertyInfo);
				}
				Properties[key] = value;
			}
			return value;
		}

		public static CachedField CachedField(this Type type, string name)
		{
			MemberLookupKey key = new MemberLookupKey(type, name);
			if (!Fields.TryGetValue(key, out var value))
			{
				Type type2 = type;
				FieldInfo fieldInfo = null;
				while ((object)fieldInfo == null && (object)type2 != null)
				{
					fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					type2 = type2.BaseType;
				}
				if ((object)fieldInfo != null)
				{
					value = new CachedField(fieldInfo);
				}
				Fields[key] = value;
			}
			return value;
		}

		public static CachedField CachedFieldByIndex(this Type type, int index, Type fieldType, BindingFlags flags)
		{
			FieldInfo[] array = (from x in type.GetFields(flags)
				where (object)x.FieldType == fieldType
				select x).ToArray();
			if (index < array.Length)
			{
				return new CachedField(array[index]);
			}
			return null;
		}
	}
	public class CachedMethod
	{
		private static readonly object[] Args0 = new object[0];

		private static readonly object[] Args1 = new object[1];

		private static readonly object[] Args2 = new object[2];

		private FastReflectionDelegate _invoke;

		internal CachedMethod(MethodInfo method)
		{
			_invoke = method.CreateFastDelegate();
		}

		public object Invoke(object instance, object[] arguments)
		{
			return _invoke(instance, arguments);
		}

		public object Invoke(object instance)
		{
			return _invoke(instance, Args0);
		}

		public object Invoke(object instance, object arg1)
		{
			try
			{
				Args1[0] = arg1;
				return _invoke(instance, Args1);
			}
			finally
			{
				Args1[0] = null;
			}
		}

		public object Invoke(object instance, object arg1, object arg2)
		{
			try
			{
				Args2[0] = arg1;
				Args2[1] = arg2;
				return _invoke(instance, Args2);
			}
			finally
			{
				Args2[0] = null;
				Args2[1] = null;
			}
		}
	}
	public class CachedProperty
	{
		private static readonly object[] Args0 = new object[0];

		private static readonly object[] Args1 = new object[1];

		private FastReflectionDelegate _set;

		private FastReflectionDelegate _get;

		public Type PropertyType { get; }

		internal CachedProperty(PropertyInfo propertyInfo)
		{
			if (propertyInfo.CanRead)
			{
				_get = propertyInfo.GetGetMethod(nonPublic: true).CreateFastDelegate();
			}
			if (propertyInfo.CanWrite)
			{
				_set = propertyInfo.GetSetMethod(nonPublic: true).CreateFastDelegate();
			}
			PropertyType = propertyInfo.PropertyType;
		}

		public void Set(object instance, object[] arguments)
		{
			if (_set != null)
			{
				_set(instance, arguments);
			}
		}

		public void Set(object instance, object arg1)
		{
			if (_set == null)
			{
				return;
			}
			try
			{
				Args1[0] = arg1;
				_set(instance, Args1);
			}
			finally
			{
				Args1[0] = null;
			}
		}

		public object Get(object instance, object[] arguments)
		{
			if (_get == null)
			{
				return null;
			}
			return _get(instance, arguments);
		}

		public object Get(object instance)
		{
			if (_get == null)
			{
				return null;
			}
			return _get(instance, Args0);
		}
	}
	public class CachedField
	{
		private Func<object, object> _get;

		private Action<object, object> _set;

		public Type FieldType { get; }

		internal CachedField(FieldInfo fieldInfo)
		{
			_get = CustomFastReflectionHelper.CreateFastFieldGetter<object, object>(fieldInfo);
			_set = CustomFastReflectionHelper.CreateFastFieldSetter<object, object>(fieldInfo);
			FieldType = fieldInfo.FieldType;
		}

		public void Set(object instance, object value)
		{
			if (_set != null)
			{
				_set(instance, value);
			}
		}

		public object Get(object instance)
		{
			if (_get == null)
			{
				return null;
			}
			return _get(instance);
		}
	}
	internal static class ReflectionEmitFastReflectionHelper
	{
		private static readonly Type[] DynamicMethodDelegateArgs = new Type[2]
		{
			typeof(object),
			typeof(object[])
		};

		public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall)
		{
			DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs, method.DeclaringType.Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			ParameterInfo[] parameters = method.GetParameters();
			bool flag = true;
			if (!method.IsStatic)
			{
				iLGenerator.Emit(OpCodes.Ldarg_0);
				if (method.DeclaringType.IsValueType)
				{
					iLGenerator.Emit(OpCodes.Unbox_Any, method.DeclaringType);
				}
			}
			for (int i = 0; i < parameters.Length; i++)
			{
				Type type = parameters[i].ParameterType;
				bool isByRef = type.IsByRef;
				if (isByRef)
				{
					type = type.GetElementType();
				}
				bool isValueType = type.IsValueType;
				if (isByRef && isValueType && !directBoxValueAccess)
				{
					iLGenerator.Emit(OpCodes.Ldarg_1);
					iLGenerator.Emit(OpCodes.Ldc_I4, i);
				}
				iLGenerator.Emit(OpCodes.Ldarg_1);
				iLGenerator.Emit(OpCodes.Ldc_I4, i);
				if (isByRef && !isValueType)
				{
					iLGenerator.Emit(OpCodes.Ldelema, typeof(object));
					continue;
				}
				iLGenerator.Emit(OpCodes.Ldelem_Ref);
				if (!isValueType)
				{
					continue;
				}
				if (!isByRef || !directBoxValueAccess)
				{
					iLGenerator.Emit(OpCodes.Unbox_Any, type);
					if (isByRef)
					{
						iLGenerator.Emit(OpCodes.Box, type);
						iLGenerator.Emit(OpCodes.Dup);
						iLGenerator.Emit(OpCodes.Unbox, type);
						if (flag)
						{
							flag = false;
							throw new NotImplementedException("No idea how to implement this...");
						}
						iLGenerator.Emit(OpCodes.Stloc_0);
						iLGenerator.Emit(OpCodes.Stelem_Ref);
						iLGenerator.Emit(OpCodes.Ldloc_0);
					}
				}
				else
				{
					iLGenerator.Emit(OpCodes.Unbox, type);
				}
			}
			if (method.IsConstructor)
			{
				iLGenerator.Emit(OpCodes.Newobj, method as ConstructorInfo);
			}
			else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall)
			{
				iLGenerator.Emit(OpCodes.Call, method as MethodInfo);
			}
			else
			{
				iLGenerator.Emit(OpCodes.Callvirt, method as MethodInfo);
			}
			Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType);
			if ((object)type2 != typeof(void))
			{
				if (type2.IsValueType)
				{
					iLGenerator.Emit(OpCodes.Box, type2);
				}
			}
			else
			{
				iLGenerator.Emit(OpCodes.Ldnull);
			}
			iLGenerator.Emit(OpCodes.Ret);
			return (FastReflectionDelegate)dynamicMethod.CreateDelegate(typeof(FastReflectionDelegate));
		}

		public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo)
		{
			if ((object)fieldInfo == null)
			{
				throw new ArgumentNullException("fieldInfo");
			}
			if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
			{
				throw new ArgumentException("FieldInfo type does not match return type.");
			}
			if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
			{
				throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
			}
			DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">", typeof(F), new Type[1] { typeof(T) }, fieldInfo.DeclaringType.Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			if (!fieldInfo.IsStatic)
			{
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
			}
			iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo);
			if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
			{
				iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType);
			}
			iLGenerator.Emit(OpCodes.Ret);
			return (Func<T, F>)dynamicMethod.CreateDelegate(typeof(Func<T, F>));
		}

		public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo)
		{
			if ((object)fieldInfo == null)
			{
				throw new ArgumentNullException("fieldInfo");
			}
			if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
			{
				throw new ArgumentException("FieldInfo type does not match argument type.");
			}
			if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
			{
				throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
			}
			DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">", null, new Type[2]
			{
				typeof(T),
				typeof(F)
			}, fieldInfo.DeclaringType.Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			if (!fieldInfo.IsStatic)
			{
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
			}
			iLGenerator.Emit(OpCodes.Ldarg_1);
			if ((object)fieldInfo.FieldType != typeof(F))
			{
				if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
				{
					if (fieldInfo.FieldType.IsValueType)
					{
						iLGenerator.Emit(OpCodes.Unbox, fieldInfo.FieldType);
					}
					else
					{
						iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType);
					}
				}
				else
				{
					iLGenerator.Emit(OpCodes.Castclass, fieldInfo.FieldType);
				}
			}
			iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
			iLGenerator.Emit(OpCodes.Ret);
			return (Action<T, F>)dynamicMethod.CreateDelegate(typeof(Action<T, F>));
		}
	}
	public static class TimeHelper
	{
		public static float realtimeSinceStartup => Time.realtimeSinceStartup;
	}
	public class UnityObjectReferenceComparer : IEqualityComparer<object>
	{
		public static readonly UnityObjectReferenceComparer Default = new UnityObjectReferenceComparer();

		public new bool Equals(object x, object y)
		{
			return x == y;
		}

		public int GetHashCode(object obj)
		{
			return obj.GetHashCode();
		}
	}
	public class WeakReference<T> : WeakReference where T : class
	{
		public new T Target => (T)base.Target;

		public static WeakReference<T> Create(T target)
		{
			if (target == null)
			{
				return WeakNullReference<T>.Singleton;
			}
			return new WeakReference<T>(target);
		}

		protected WeakReference(T target)
			: base(target, trackResurrection: false)
		{
		}
	}
	internal class WeakNullReference<T> : WeakReference<T> where T : class
	{
		public static readonly WeakNullReference<T> Singleton = new WeakNullReference<T>();

		public override bool IsAlive => true;

		private WeakNullReference()
			: base((T)null)
		{
		}
	}
	internal sealed class WeakKeyReference<T> : WeakReference<T> where T : class
	{
		public readonly int HashCode;

		public WeakKeyReference(T key, WeakKeyComparer<T> comparer)
			: base(key)
		{
			HashCode = comparer.GetHashCode(key);
		}
	}
	internal sealed class WeakKeyComparer<T> : IEqualityComparer<object> where T : class
	{
		private IEqualityComparer<T> comparer;

		internal WeakKeyComparer(IEqualityComparer<T> comparer)
		{
			if (comparer == null)
			{
				comparer = EqualityComparer<T>.Default;
			}
			this.comparer = comparer;
		}

		public int GetHashCode(object obj)
		{
			if (obj is WeakKeyReference<T> weakKeyReference)
			{
				return weakKeyReference.HashCode;
			}
			return comparer.GetHashCode((T)obj);
		}

		public new bool Equals(object x, object y)
		{
			bool isDead;
			T target = GetTarget(x, out isDead);
			bool isDead2;
			T target2 = GetTarget(y, out isDead2);
			if (isDead)
			{
				if (!isDead2)
				{
					return false;
				}
				return x == y;
			}
			if (isDead2)
			{
				return false;
			}
			return comparer.Equals(target, target2);
		}

		private static T GetTarget(object obj, out bool isDead)
		{
			T result;
			if (obj is WeakKeyReference<T> weakKeyReference)
			{
				result = weakKeyReference.Target;
				isDead = !weakKeyReference.IsAlive;
			}
			else
			{
				result = (T)obj;
				isDead = false;
			}
			return result;
		}
	}
	public sealed class WeakDictionary<TKey, TValue> : BaseDictionary<TKey, TValue> where TKey : class
	{
		private Dictionary<object, TValue> dictionary;

		private WeakKeyComparer<TKey> comparer;

		public override int Count => dictionary.Count;

		public WeakDictionary()
			: this(0, (IEqualityComparer<TKey>)null)
		{
		}

		public WeakDictionary(int capacity)
			: this(capacity, (IEqualityComparer<TKey>)null)
		{
		}

		public WeakDictionary(IEqualityComparer<TKey> comparer)
			: this(0, comparer)
		{
		}

		public WeakDictionary(int capacity, IEqualityComparer<TKey> comparer)
		{
			this.comparer = new WeakKeyComparer<TKey>(comparer);
			dictionary = new Dictionary<object, TValue>(capacity, this.comparer);
		}

		public override void Add(TKey key, TValue value)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			WeakReference<TKey> key2 = new WeakKeyReference<TKey>(key, comparer);
			dictionary.Add(key2, value);
		}

		public override bool ContainsKey(TKey key)
		{
			return dictionary.ContainsKey(key);
		}

		public override bool Remove(TKey key)
		{
			return dictionary.Remove(key);
		}

		public override bool TryGetValue(TKey key, out TValue value)
		{
			if (dictionary.TryGetValue(key, out value))
			{
				return true;
			}
			value = default(TValue);
			return false;
		}

		protected override void SetValue(TKey key, TValue value)
		{
			WeakReference<TKey> key2 = new WeakKeyReference<TKey>(key, comparer);
			dictionary[key2] = value;
		}

		public override void Clear()
		{
			dictionary.Clear();
		}

		public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			foreach (KeyValuePair<object, TValue> item in dictionary)
			{
				WeakReference<TKey> obj = (WeakReference<TKey>)item.Key;
				TValue value = item.Value;
				TKey target = obj.Target;
				if (obj.IsAlive)
				{
					yield return new KeyValuePair<TKey, TValue>(target, value);
				}
			}
		}

		public void RemoveCollectedEntries()
		{
			List<object> list = null;
			foreach (KeyValuePair<object, TValue> item in dictionary)
			{
				WeakReference<TKey> weakReference = (WeakReference<TKey>)item.Key;
				if (!weakReference.IsAlive)
				{
					if (list == null)
					{
						list = new List<object>();
					}
					list.Add(weakReference);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (object item2 in list)
			{
				dictionary.Remove(item2);
			}
		}
	}
	[DebuggerDisplay("Count = {Count}")]
	[DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")]
	public abstract class BaseDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
	{
		private abstract class Collection<T> : ICollection<T>, IEnumerable<T>, IEnumerable
		{
			protected readonly IDictionary<TKey, TValue> dictionary;

			public int Count => dictionary.Count;

			public bool IsReadOnly => true;

			protected Collection(IDictionary<TKey, TValue> dictionary)
			{
				this.dictionary = dictionary;
			}

			public void CopyTo(T[] array, int arrayIndex)
			{
				BaseDictionary<TKey, TValue>.Copy((ICollection<T>)this, array, arrayIndex);
			}

			public virtual bool Contains(T item)
			{
				using (IEnumerator<T> enumerator = GetEnumerator())
				{
					while (enumerator.MoveNext())
					{
						T current = enumerator.Current;
						if (EqualityComparer<T>.Default.Equals(current, item))
						{
							return true;
						}
					}
				}
				return false;
			}

			public IEnumerator<T> GetEnumerator()
			{
				foreach (KeyValuePair<TKey, TValue> item in dictionary)
				{
					yield return GetItem(item);
				}
			}

			protected abstract T GetItem(KeyValuePair<TKey, TValue> pair);

			public bool Remove(T item)
			{
				throw new NotSupportedException("Collection is read-only.");
			}

			public void Add(T item)
			{
				throw new NotSupportedException("Collection is read-only.");
			}

			public void Clear()
			{
				throw new NotSupportedException("Collection is read-only.");
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}
		}

		[DebuggerDisplay("Count = {Count}")]
		[DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryKeyCollectionDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")]
		private class KeyCollection : Collection<TKey>
		{
			public KeyCollection(IDictionary<TKey, TValue> dictionary)
				: base(dictionary)
			{
			}

			protected override TKey GetItem(KeyValuePair<TKey, TValue> pair)
			{
				return pair.Key;
			}

			public override bool Contains(TKey item)
			{
				return dictionary.ContainsKey(item);
			}
		}

		[DebuggerDisplay("Count = {Count}")]
		[DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryValueCollectionDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")]
		private class ValueCollection : Collection<TValue>
		{
			public ValueCollection(IDictionary<TKey, TValue> dictionary)
				: base(dictionary)
			{
			}

			protected override TValue GetItem(KeyValuePair<TKey, TValue> pair)
			{
				return pair.Value;
			}
		}

		private const string PREFIX = "System.Collections.Generic.Mscorlib_";

		private const string SUFFIX = ",mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089";

		private KeyCollection keys;

		private ValueCollection values;

		public abstract int Count { get; }

		public bool IsReadOnly => false;

		public ICollection<TKey> Keys
		{
			get
			{
				if (keys == null)
				{
					keys = new KeyCollection(this);
				}
				return keys;
			}
		}

		public ICollection<TValue> Values
		{
			get
			{
				if (values == null)
				{
					values = new ValueCollection(this);
				}
				return values;
			}
		}

		public TValue this[TKey key]
		{
			get
			{
				if (!TryGetValue(key, out var value))
				{
					throw new KeyNotFoundException();
				}
				return value;
			}
			set
			{
				SetValue(key, value);
			}
		}

		public abstract void Clear();

		public abstract void Add(TKey key, TValue value);

		public abstract bool ContainsKey(TKey key);

		public abstract bool Remove(TKey key);

		public abstract bool TryGetValue(TKey key, out TValue value);

		public abstract IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();

		protected abstract void SetValue(TKey key, TValue value);

		public void Add(KeyValuePair<TKey, TValue> item)
		{
			Add(item.Key, item.Value);
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			if (!TryGetValue(item.Key, out var value))
			{
				return false;
			}
			return EqualityComparer<TValue>.Default.Equals(value, item.Value);
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
			Copy(this, array, arrayIndex);
		}

		public bool Remove(KeyValuePair<TKey, TValue> item)
		{
			if (!Contains(item))
			{
				return false;
			}
			return Remove(item.Key);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		private static void Copy<T>(ICollection<T> source, T[] array, int arrayIndex)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			if (arrayIndex < 0 || arrayIndex > array.Length)
			{
				throw new ArgumentOutOfRangeException("arrayIndex");
			}
			if (array.Length - arrayIndex < source.Count)
			{
				throw new ArgumentException("Destination array is not large enough. Check array.Length and arrayIndex.");
			}
			foreach (T item in source)
			{
				array[arrayIndex++] = item;
			}
		}
	}
}
namespace XUnity.Common.MonoMod
{
	public static class DetourExtensions
	{
		public static T GenerateTrampolineEx<T>(this object detour)
		{
			return (T)(from x in detour.GetType().GetMethods()
				where x.Name == "GenerateTrampoline" && x.IsGenericMethod
				select x).FirstOrDefault().MakeGenericMethod(typeof(T)).Invoke(detour, null);
		}
	}
}
namespace XUnity.Common.Logging
{
	internal class ConsoleLogger : XuaLogger
	{
		public ConsoleLogger(string source)
			: base(source)
		{
		}

		protected override void Log(LogLevel level, string message)
		{
			Console.WriteLine(GetDefaultPrefix(level) + " " + message);
		}
	}
	public enum LogLevel
	{
		Debug,
		Info,
		Warn,
		Error
	}
	internal class ModLoaderSpecificLogger : XuaLogger
	{
		public static class BepInExLogLevel
		{
			public const int None = 0;

			public const int Fatal = 1;

			public const int Error = 2;

			public const int Warning = 4;

			public const int Message = 8;

			public const int Info = 16;

			public const int Debug = 32;

			public const int All = 63;
		}

		private static Action<LogLevel, string> _logMethod;

		public ModLoaderSpecificLogger(string source)
			: base(source)
		{
			if (_logMethod != null)
			{
				return;
			}
			BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public;
			BindingFlags bindingAttr2 = BindingFlags.Instance | BindingFlags.Public;
			Type type = Type.GetType("BepInEx.Logging.LogLevel, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.LogLevel, BepInEx.Core", throwOnError: false);
			if ((object)type != null)
			{
				if ((object)(Type.GetType("BepInEx.Logging.ManualLogSource, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.ManualLogSource, BepInEx.Core", throwOnError: false)) != null)
				{
					MethodInfo method = (Type.GetType("BepInEx.Logging.Logger, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.Logger, BepInEx.Core", throwOnError: false)).GetMethod("CreateLogSource", bindingAttr, null, new Type[1] { typeof(string) }, null);
					object logInstance2 = method.Invoke(null, new object[1] { base.Source });
					MethodInfo method2 = logInstance2.GetType().GetMethod("Log", bindingAttr2, null, new Type[2]
					{
						type,
						typeof(object)
					}, null);
					FastReflectionDelegate log2 = method2.CreateFastDelegate();
					_logMethod = delegate(LogLevel level, string msg)
					{
						int num2 = Convert(level);
						log2(logInstance2, num2, msg);
					};
				}
				else
				{
					Type type2 = Type.GetType("BepInEx.Logger, BepInEx", throwOnError: false);
					object logInstance = type2.GetProperty("CurrentLogger", bindingAttr).GetValue(null, null);
					MethodInfo method3 = logInstance.GetType().GetMethod("Log", bindingAttr2, null, new Type[2]
					{
						type,
						typeof(object)
					}, null);
					FastReflectionDelegate log = method3.CreateFastDelegate();
					_logMethod = delegate(LogLevel level, string msg)
					{
						int num = Convert(level);
						log(logInstance, num, msg);
					};
				}
			}
			else
			{
				Type type3 = Type.GetType("MelonLoader.MelonLogger, MelonLoader.ModHandler", throwOnError: false);
				if ((object)type3 != null)
				{
					MethodInfo method4 = type3.GetMethod("Log", bindingAttr, null, new Type[2]
					{
						typeof(ConsoleColor),
						typeof(string)
					}, null);
					MethodInfo method5 = type3.GetMethod("Log", bindingAttr, null, new Type[1] { typeof(string) }, null);
					MethodInfo method6 = type3.GetMethod("LogWarning", bindingAttr, null, new Type[1] { typeof(string) }, null);
					MethodInfo method7 = type3.GetMethod("LogError", bindingAttr, null, new Type[1] { typeof(string) }, null);
					FastReflectionDelegate logDebug = method4.CreateFastDelegate();
					FastReflectionDelegate logInfo = method5.CreateFastDelegate();
					FastReflectionDelegate logWarning = method6.CreateFastDelegate();
					FastReflectionDelegate logError = method7.CreateFastDelegate();
					_logMethod = delegate(LogLevel level, string msg)
					{
						switch (level)
						{
						case LogLevel.Debug:
							logDebug(null, ConsoleColor.Gray, msg);
							break;
						case LogLevel.Info:
							logInfo(null, msg);
							break;
						case LogLevel.Warn:
							logWarning(null, msg);
							break;
						case LogLevel.Error:
							logError(null, msg);
							break;
						default:
							throw new ArgumentException("level");
						}
					};
				}
			}
			if (_logMethod != null)
			{
				return;
			}
			throw new Exception("Did not recognize any mod loader!");
		}

		protected override void Log(LogLevel level, string message)
		{
			_logMethod(level, message);
		}

		public static int Convert(LogLevel level)
		{
			return level switch
			{
				LogLevel.Debug => 32, 
				LogLevel.Info => 16, 
				LogLevel.Warn => 4, 
				LogLevel.Error => 2, 
				_ => 0, 
			};
		}
	}
	public abstract class XuaLogger
	{
		private static XuaLogger _default;

		private static XuaLogger _common;

		private static XuaLogger _resourceRedirector;

		public static XuaLogger AutoTranslator
		{
			get
			{
				if (_default == null)
				{
					_default = CreateLogger("XUnity.AutoTranslator");
				}
				return _default;
			}
			set
			{
				_default = value ?? throw new ArgumentNullException("value");
			}
		}

		public static XuaLogger Common
		{
			get
			{
				if (_common == null)
				{
					_common = CreateLogger("XUnity.Common");
				}
				return _common;
			}
			set
			{
				_common = value ?? throw new ArgumentNullException("value");
			}
		}

		public static XuaLogger ResourceRedirector
		{
			get
			{
				if (_resourceRedirector == null)
				{
					_resourceRedirector = CreateLogger("XUnity.ResourceRedirector");
				}
				return _resourceRedirector;
			}
			set
			{
				_resourceRedirector = value ?? throw new ArgumentNullException("value");
			}
		}

		public string Source { get; set; }

		internal static XuaLogger CreateLogger(string source)
		{
			try
			{
				return new ModLoaderSpecificLogger(source);
			}
			catch (Exception)
			{
				return new ConsoleLogger(source);
			}
		}

		public XuaLogger(string source)
		{
			Source = source;
		}

		public void Error(Exception e, string message)
		{
			Log(LogLevel.Error, message + Environment.NewLine + e);
		}

		public void Error(string message)
		{
			Log(LogLevel.Error, message);
		}

		public void Warn(Exception e, string message)
		{
			Log(LogLevel.Warn, message + Environment.NewLine + e);
		}

		public void Warn(string message)
		{
			Log(LogLevel.Warn, message);
		}

		public void Info(Exception e, string message)
		{
			Log(LogLevel.Info, message + Environment.NewLine + e);
		}

		public void Info(string message)
		{
			Log(LogLevel.Info, message);
		}

		public void Debug(Exception e, string message)
		{
			Log(LogLevel.Debug, message + Environment.NewLine + e);
		}

		public void Debug(string message)
		{
			Log(LogLevel.Debug, message);
		}

		protected abstract void Log(LogLevel level, string message);

		protected string GetDefaultPrefix(LogLevel level)
		{
			return level switch
			{
				LogLevel.Debug => "[DEBUG][" + Source + "]: ", 
				LogLevel.Info => "[INFO][" + Source + "]: ", 
				LogLevel.Warn => "[WARN][" + Source + "]: ", 
				LogLevel.Error => "[ERROR][" + Source + "]: ", 
				_ => "[UNKNOW][" + Source + "]: ", 
			};
		}
	}
}
namespace XUnity.Common.Harmony
{
	public static class AccessToolsShim
	{
		private static readonly BindingFlags All;

		private static readonly Func<Type, string, Type[], Type[], MethodInfo> AccessTools_Method;

		private static readonly Func<Type, string, PropertyInfo> AccessTools_Property;

		static AccessToolsShim()
		{
			All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			MethodInfo method = ClrTypes.AccessTools.GetMethod("Method", All, null, new Type[4]
			{
				typeof(Type),
				typeof(string),
				typeof(Type[]),
				typeof(Type[])
			}, null);
			MethodInfo? method2 = ClrTypes.AccessTools.GetMethod("Property", All, null, new Type[2]
			{
				typeof(Type),
				typeof(string)
			}, null);
			AccessTools_Method = (Func<Type, string, Type[], Type[], MethodInfo>)ExpressionHelper.CreateTypedFastInvoke(method);
			AccessTools_Property = (Func<Type, string, PropertyInfo>)ExpressionHelper.CreateTypedFastInvoke(method2);
		}

		public static MethodInfo Method(Type type, string name, params Type[] parameters)
		{
			return AccessTools_Method(type, name, parameters, null);
		}

		public static PropertyInfo Property(Type type, string name)
		{
			return AccessTools_Property(type, name);
		}
	}
}
namespace XUnity.Common.Extensions
{
	public static class ExceptionExtensions
	{
		public static TException FirstInnerExceptionOfType<TException>(this Exception e) where TException : Exception
		{
			for (Exception ex = e; ex != null; ex = ex.InnerException)
			{
				if (ex is TException)
				{
					return (TException)ex;
				}
			}
			return null;
		}
	}
	public static class ObjectExtensions
	{
		public static Type GetUnityType(this object obj)
		{
			return obj.GetType();
		}

		public static bool TryCastTo<TObject>(this object obj, out TObject castedObject)
		{
			if (obj is TObject val)
			{
				castedObject = val;
				return true;
			}
			castedObject = default(TObject);
			return false;
		}
	}
	public static class StreamExtensions
	{
		public static byte[] ReadFully(this Stream stream, int initialLength)
		{
			if (initialLength < 1)
			{
				initialLength = 32768;
			}
			byte[] array = new byte[initialLength];
			int num = 0;
			int num2;
			while ((num2 = stream.Read(array, num, array.Length - num)) > 0)
			{
				num += num2;
				if (num == array.Length)
				{
					int num3 = stream.ReadByte();
					if (num3 == -1)
					{
						return array;
					}
					byte[] array2 = new byte[array.Length * 2];
					Array.Copy(array, array2, array.Length);
					array2[num] = (byte)num3;
					array = array2;
					num++;
				}
			}
			byte[] array3 = new byte[num];
			Array.Copy(array, array3, num);
			return array3;
		}
	}
	public static class StringExtensions
	{
		private static readonly HashSet<char> InvalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

		public static string UseCorrectDirectorySeparators(this string path)
		{
			if (Path.DirectorySeparatorChar == '\\')
			{
				return path.Replace('/', Path.DirectorySeparatorChar);
			}
			if (Path.DirectorySeparatorChar == '/')
			{
				return path.Replace('\\', Path.DirectorySeparatorChar);
			}
			return path;
		}

		public static bool IsNullOrWhiteSpace(this string value)
		{
			if (value == null)
			{
				return true;
			}
			for (int i = 0; i < value.Length; i++)
			{
				if (!char.IsWhiteSpace(value[i]))
				{
					return false;
				}
			}
			return true;
		}

		public static string MakeRelativePath(this string fullOrRelativePath, string basePath)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int i = 0;
			bool flag = false;
			string[] array = basePath.Split(':', '\\', '/');
			List<string> list = fullOrRelativePath.Split(':', '\\', '/').ToList();
			if (array.Length == 0 || list.Count <= 0 || array[0] != list[0])
			{
				flag = true;
			}
			bool flag2 = false;
			for (int j = 0; j < list.Count; j++)
			{
				if (list[j] == "..")
				{
					if (flag2)
					{
						int num = j - 1;
						if (num >= 0)
						{
							list.RemoveAt(j);
							list.RemoveAt(num);
							j -= 2;
						}
					}
				}
				else
				{
					flag2 = true;
				}
			}
			if (!flag)
			{
				for (i = 1; i < array.Length && !(array[i] != list[i]); i++)
				{
				}
				for (int k = 0; k < array.Length - i; k++)
				{
					char directorySeparatorChar = Path.DirectorySeparatorChar;
					stringBuilder.Append(".." + directorySeparatorChar);
				}
			}
			for (int l = i; l < list.Count - 1; l++)
			{
				string value = list[l];
				stringBuilder.Append(value).Append(Path.DirectorySeparatorChar);
			}
			string value2 = list[^1];
			stringBuilder.Append(value2);
			return stringBuilder.ToString();
		}

		public static string SanitizeForFileSystem(this string path)
		{
			StringBuilder stringBuilder = new StringBuilder(path.Length);
			foreach (char c in path)
			{
				if (!InvalidFileNameChars.Contains(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		public static string SplitToLines(this string text, int maxStringLength, params char[] splitOnCharacters)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int num;
			for (int i = 0; text.Length > i; i += num)
			{
				if (i != 0)
				{
					stringBuilder.Append('\n');
				}
				num = ((i + maxStringLength <= text.Length) ? text.Substring(i, maxStringLength).LastIndexOfAny(splitOnCharacters) : (text.Length - i));
				num = ((num == -1) ? maxStringLength : num);
				stringBuilder.Append(text.Substring(i, num).Trim());
			}
			return stringBuilder.ToString();
		}

		public static bool StartsWithStrict(this string str, string prefix)
		{
			int num = Math.Min(str.Length, prefix.Length);
			if (num < prefix.Length)
			{
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				if (str[i] != prefix[i])
				{
					return false;
				}
			}
			return true;
		}

		public static string GetBetween(this string strSource, string strStart, string strEnd)
		{
			int num = strSource.IndexOf(strStart);
			if (num != -1)
			{
				num += strStart.Length;
				int num2 = strSource.IndexOf(strEnd, num);
				if (num2 > num)
				{
					return strSource.Substring(num, num2 - num);
				}
			}
			return string.Empty;
		}

		public static bool RemindsOf(this string that, string other)
		{
			if (!that.StartsWith(other) && !other.StartsWith(that) && !that.EndsWith(other))
			{
				return other.EndsWith(that);
			}
			return true;
		}
	}
}
namespace XUnity.Common.Constants
{
	public static class ClrTypes
	{
		public static readonly Type AccessTools = FindTypeStrict("Harmony.AccessTools, 0Harmony") ?? FindTypeStrict("HarmonyLib.AccessTools, 0Harmony") ?? FindTypeStrict("Harmony.AccessTools, MelonLoader.ModHandler") ?? FindTypeStrict("HarmonyLib.AccessTools, MelonLoader.ModHandler");

		public static readonly Type HarmonyMethod = FindTypeStrict("Harmony.HarmonyMethod, 0Harmony") ?? FindTypeStrict("HarmonyLib.HarmonyMethod, 0Harmony") ?? FindTypeStrict("Harmony.HarmonyMethod, MelonLoader.ModHandler") ?? FindTypeStrict("HarmonyLib.HarmonyMethod, MelonLoader.ModHandler");

		public static readonly Type HarmonyInstance = FindTypeStrict("Harmony.HarmonyInstance, 0Harmony") ?? FindTypeStrict("Harmony.HarmonyInstance, MelonLoader.ModHandler");

		public static readonly Type Harmony = FindTypeStrict("HarmonyLib.Harmony, 0Harmony") ?? FindTypeStrict("HarmonyLib.Harmony, MelonLoader.ModHandler");

		public static readonly Type Hook = FindTypeStrict("MonoMod.RuntimeDetour.Hook, MonoMod.RuntimeDetour");

		public static readonly Type Detour = FindTypeStrict("MonoMod.RuntimeDetour.Detour, MonoMod.RuntimeDetour");

		public static readonly Type NativeDetour = FindTypeStrict("MonoMod.RuntimeDetour.NativeDetour, MonoMod.RuntimeDetour");

		public static readonly Type DynamicMethodDefinition = FindTypeStrict("MonoMod.Utils.DynamicMethodDefinition, MonoMod.Utils");

		public static readonly Type Imports = FindTypeStrict("MelonLoader.Imports, MelonLoader.ModHandler");

		public static readonly Type MethodBase = FindType("System.Reflection.MethodBase");

		public static readonly Type Task = FindType("System.Threading.Tasks.Task");

		private static Type FindType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType(name, throwOnError: false);
					if ((object)type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static Type FindTypeStrict(string name)
		{
			return Type.GetType(name, throwOnError: false);
		}
	}
	public class TypeContainer
	{
		public Type ClrType { get; }

		public Type UnityType { get; }

		public TypeContainer(Type type)
		{
			UnityType = type;
			ClrType = type;
		}

		public bool IsAssignableFrom(Type unityType)
		{
			if ((object)UnityType != null)
			{
				return UnityType.IsAssignableFrom(unityType);
			}
			return false;
		}
	}
	public static class UnityFeatures
	{
		private static readonly BindingFlags All;

		public static bool SupportsMouseScrollDelta { get; }

		public static bool SupportsClipboard { get; }

		public static bool SupportsCustomYieldInstruction { get; }

		public static bool SupportsSceneManager { get; }

		public static bool SupportsWaitForSecondsRealtime { get; set; }

		static UnityFeatures()
		{
			All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			SupportsMouseScrollDelta = false;
			SupportsClipboard = false;
			SupportsCustomYieldInstruction = false;
			SupportsSceneManager = false;
			SupportsWaitForSecondsRealtime = false;
			try
			{
				SupportsClipboard = (object)UnityTypes.TextEditor?.ClrType.GetProperty("text")?.GetSetMethod() != null;
			}
			catch (Exception)
			{
			}
			try
			{
				SupportsCustomYieldInstruction = UnityTypes.CustomYieldInstruction != null;
			}
			catch (Exception)
			{
			}
			try
			{
				SupportsSceneManager = UnityTypes.Scene != null && UnityTypes.SceneManager != null && (object)UnityTypes.SceneManager.ClrType.GetMethod("add_sceneLoaded", All) != null;
			}
			catch (Exception)
			{
			}
			try
			{
				SupportsMouseScrollDelta = (object)UnityTypes.Input?.ClrType.GetProperty("mouseScrollDelta") != null;
			}
			catch (Exception)
			{
			}
			try
			{
				SupportsWaitForSecondsRealtime = UnityTypes.WaitForSecondsRealtime != null;
			}
			catch (Exception)
			{
			}
		}
	}
	public static class UnityTypes
	{
		public static class TMP_Settings_Properties
		{
			public static CachedProperty Version = TMP_Settings?.ClrType.CachedProperty("version");

			public static CachedProperty FallbackFontAssets = TMP_Settings?.ClrType.CachedProperty("fallbackFontAssets");
		}

		public static class TMP_FontAsset_Properties
		{
			public static CachedProperty Version = TMP_FontAsset?.ClrType.CachedProperty("version");
		}

		public static class AdvScenarioData_Properties
		{
			public static CachedProperty ScenarioLabels = AdvScenarioData?.ClrType.CachedProperty("ScenarioLabels");
		}

		public static class UguiNovelText_Properties
		{
			public static CachedProperty TextGenerator = UguiNovelText?.ClrType.CachedProperty("TextGenerator");
		}

		public static class UguiNovelText_Methods
		{
			public static CachedMethod SetAllDirty = UguiNovelText?.ClrType.CachedMethod("SetAllDirty");
		}

		public static class UguiNovelTextGenerator_Methods
		{
			public static CachedMethod Refresh = UguiNovelTextGenerator?.ClrType.CachedMethod("Refresh");
		}

		public static class AdvUguiMessageWindow_Properties
		{
			public static CachedProperty Text = AdvUguiMessageWindow?.ClrType.CachedProperty("Text");

			public static CachedProperty Engine = AdvUguiMessageWindow?.ClrType.CachedProperty("Engine");
		}

		public static class AdvUiMessageWindow_Fields
		{
			public static CachedField text = AdvUiMessageWindow?.ClrType.CachedField("text");

			public static CachedField nameText = AdvUiMessageWindow?.ClrType.CachedField("nameText");
		}

		public static class AdvUguiMessageWindow_Fields
		{
			public static FieldInfo text = AdvUguiMessageWindow?.ClrType.GetField("text", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

			public static FieldInfo nameText = AdvUguiMessageWindow?.ClrType.GetField("nameText", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

			public static FieldInfo engine = AdvUguiMessageWindow?.ClrType.GetField("engine", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		public static class AdvEngine_Properties
		{
			public static CachedProperty Page = AdvEngine?.ClrType.CachedProperty("Page");
		}

		public static class AdvPage_Methods
		{
			public static CachedMethod RemakeTextData = AdvPage?.ClrType.CachedMethod("RemakeTextData");

			public static CachedMethod RemakeText = AdvPage?.ClrType.CachedMethod("RemakeText");

			public static CachedMethod ChangeMessageWindowText = AdvPage?.ClrType.CachedMethod("ChangeMessageWindowText", typeof(string), typeof(string), typeof(string), typeof(string));
		}

		public static class UILabel_Properties
		{
			public static CachedProperty MultiLine = UILabel?.ClrType.CachedProperty("multiLine");

			public static CachedProperty OverflowMethod = UILabel?.ClrType.CachedProperty("overflowMethod");

			public static CachedProperty SpacingX = UILabel?.ClrType.CachedProperty("spacingX");

			public static CachedProperty UseFloatSpacing = UILabel?.ClrType.CachedProperty("useFloatSpacing");
		}

		public static class Text_Properties
		{
			public static CachedProperty Font = Text?.ClrType.CachedProperty("font");

			public static CachedProperty FontSize = Text?.ClrType.CachedProperty("fontSize");

			public static CachedProperty HorizontalOverflow = Text?.ClrType.CachedProperty("horizontalOverflow");

			public static CachedProperty VerticalOverflow = Text?.ClrType.CachedProperty("verticalOverflow");

			public static CachedProperty LineSpacing = Text?.ClrType.CachedProperty("lineSpacing");

			public static CachedProperty ResizeTextForBestFit = Text?.ClrType.CachedProperty("resizeTextForBestFit");

			public static CachedProperty ResizeTextMinSize = Text?.ClrType.CachedProperty("resizeTextMinSize");

			public static CachedProperty ResizeTextMaxSize = Text?.ClrType.CachedProperty("resizeTextMaxSize");
		}

		public static class InputField_Properties
		{
			public static CachedProperty Placeholder = InputField?.ClrType.CachedProperty("placeholder");
		}

		public static class TMP_InputField_Properties
		{
			public static CachedProperty Placeholder = TMP_InputField?.ClrType.CachedProperty("placeholder");
		}

		public static class Font_Properties
		{
			public static CachedProperty FontSize = Font?.ClrType.CachedProperty("fontSize");
		}

		public static class AssetBundle_Methods
		{
			public static CachedMethod LoadAll = AssetBundle?.ClrType.CachedMethod("LoadAll", typeof(Type));

			public static CachedMethod LoadAllAssets = AssetBundle?.ClrType.CachedMethod("LoadAllAssets", typeof(Type));

			public static CachedMethod LoadFromFile = AssetBundle?.ClrType.CachedMethod("LoadFromFile", typeof(string));

			public static CachedMethod CreateFromFile = AssetBundle?.ClrType.CachedMethod("CreateFromFile", typeof(string));
		}

		public static class TextExpansion_Methods
		{
			public static CachedMethod SetMessageType = TextExpansion?.ClrType.CachedMethod("SetMessageType");

			public static CachedMethod SkipTypeWriter = TextExpansion?.ClrType.CachedMethod("SkipTypeWriter");
		}

		public static class GameObject_Methods
		{
		}

		public static class TextMesh_Methods
		{
		}

		public static class Text_Methods
		{
		}

		public static class InputField_Methods
		{
		}

		public static class TMP_Text_Methods
		{
		}

		public static class TMP_InputField_Methods
		{
		}

		public static class TextMeshPro_Methods
		{
		}

		public static class TextMeshProUGUI_Methods
		{
		}

		public static class UILabel_Methods
		{
		}

		public static class UIRect_Methods
		{
		}

		public static class SceneManager_Methods
		{
			public static readonly Action<UnityAction<Scene, LoadSceneMode>> add_sceneLoaded = (Action<UnityAction<Scene, LoadSceneMode>>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(SceneManager).GetMethod("add_sceneLoaded", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(UnityAction<Scene, LoadSceneMode>) }, null));
		}

		public static class Texture2D_Methods
		{
			public static readonly Func<Texture2D, byte[], bool> LoadImage = (Func<Texture2D, byte[], bool>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(Texture2D).GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(byte[]) }, null));

			public static readonly Func<Texture2D, byte[]> EncodeToPNG = (Func<Texture2D, byte[]>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(Texture2D).GetMethod("EncodeToPNG", BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null));
		}

		public static class ImageConversion_Methods
		{
			public static readonly Func<Texture2D, byte[], bool, bool> LoadImage = (Func<Texture2D, byte[], bool, bool>)ExpressionHelper.CreateTypedFastInvokeUnchecked(ImageConversion?.ClrType.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null));

			public static readonly Func<Texture2D, byte[]> EncodeToPNG = (Func<Texture2D, byte[]>)ExpressionHelper.CreateTypedFastInvokeUnchecked(ImageConversion?.ClrType.GetMethod("EncodeToPNG", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Texture2D) }, null));
		}

		public static readonly TypeContainer UILabel = FindType("UILabel");

		public static readonly TypeContainer UIWidget = FindType("UIWidget");

		public static readonly TypeContainer UIAtlas = FindType("UIAtlas");

		public static readonly TypeContainer UISprite = FindType("UISprite");

		public static readonly TypeContainer UITexture = FindType("UITexture");

		public static readonly TypeContainer UI2DSprite = FindType("UI2DSprite");

		public static readonly TypeContainer UIFont = FindType("UIFont");

		public static readonly TypeContainer UIPanel = FindType("UIPanel");

		public static readonly TypeContainer UIRect = FindType("UIRect");

		public static readonly TypeContainer UIInput = FindType("UIInput");

		public static readonly TypeContainer TextField = FindType("FairyGUI.TextField");

		public static readonly TypeContainer TMP_InputField = FindType("TMPro.TMP_InputField");

		public static readonly TypeContainer TMP_Text = FindType("TMPro.TMP_Text");

		public static readonly TypeContainer TextMeshProUGUI = FindType("TMPro.TextMeshProUGUI");

		public static readonly TypeContainer TextMeshPro = FindType("TMPro.TextMeshPro");

		public static readonly TypeContainer TMP_FontAsset = FindType("TMPro.TMP_FontAsset");

		public static readonly TypeContainer TMP_Settings = FindType("TMPro.TMP_Settings");

		public static readonly TypeContainer GameObject = FindType("UnityEngine.GameObject");

		public static readonly TypeContainer Transform = FindType("UnityEngine.Transform");

		public static readonly TypeContainer TextMesh = FindType("UnityEngine.TextMesh");

		public static readonly TypeContainer Text = FindType("UnityEngine.UI.Text");

		public static readonly TypeContainer Image = FindType("UnityEngine.UI.Image");

		public static readonly TypeContainer RawImage = FindType("UnityEngine.UI.RawImage");

		public static readonly TypeContainer MaskableGraphic = FindType("UnityEngine.UI.MaskableGraphic");

		public static readonly TypeContainer Graphic = FindType("UnityEngine.UI.Graphic");

		public static readonly TypeContainer GUIContent = FindType("UnityEngine.GUIContent");

		public static readonly TypeContainer WWW = FindType("UnityEngine.WWW");

		public static readonly TypeContainer InputField = FindType("UnityEngine.UI.InputField");

		public static readonly TypeContainer GUI = FindType("UnityEngine.GUI");

		public static readonly TypeContainer GUI_ToolbarButtonSize = FindType("UnityEngine.GUI+ToolbarButtonSize");

		public static readonly TypeContainer GUIStyle = FindType("UnityEngine.GUIStyle");

		public static readonly TypeContainer ImageConversion = FindType("UnityEngine.ImageConversion");

		public static readonly TypeContainer Texture2D = FindType("UnityEngine.Texture2D");

		public static readonly TypeContainer Texture = FindType("UnityEngine.Texture");

		public static readonly TypeContainer SpriteRenderer = FindType("UnityEngine.SpriteRenderer");

		public static readonly TypeContainer Sprite = FindType("UnityEngine.Sprite");

		public static readonly TypeContainer Object = FindType("UnityEngine.Object");

		public static readonly TypeContainer TextEditor = FindType("UnityEngine.TextEditor");

		public static readonly TypeContainer CustomYieldInstruction = FindType("UnityEngine.CustomYieldInstruction");

		public static readonly TypeContainer SceneManager = FindType("UnityEngine.SceneManagement.SceneManager");

		public static readonly TypeContainer Scene = FindType("UnityEngine.SceneManagement.Scene");

		public static readonly TypeContainer UnityEventBase = FindType("UnityEngine.Events.UnityEventBase");

		public static readonly TypeContainer BaseInvokableCall = FindType("UnityEngine.Events.BaseInvokableCall");

		public static readonly TypeContainer Font = FindType("UnityEngine.Font");

		public static readonly TypeContainer WaitForSecondsRealtime = FindType("UnityEngine.WaitForSecondsRealtime");

		public static readonly TypeContainer Input = FindType("UnityEngine.Input");

		public static readonly TypeContainer AssetBundleCreateRequest = FindType("UnityEngine.AssetBundleCreateRequest");

		public static readonly TypeContainer AssetBundle = FindType("UnityEngine.AssetBundle");

		public static readonly TypeContainer AssetBundleRequest = FindType("UnityEngine.AssetBundleRequest");

		public static readonly TypeContainer Resources = FindType("UnityEngine.Resources");

		public static readonly TypeContainer AsyncOperation = FindType("UnityEngine.AsyncOperation");

		public static readonly TypeContainer TextAsset = FindType("UnityEngine.TextAsset");

		public static readonly Type HorizontalWrapMode = FindClrType("UnityEngine.HorizontalWrapMode");

		public static readonly Type TextOverflowModes = FindClrType("TMPro.TextOverflowModes");

		public static readonly Type TextAlignmentOptions = FindClrType("TMPro.TextAlignmentOptions");

		public static readonly Type VerticalWrapMode = FindClrType("UnityEngine.VerticalWrapMode");

		public static readonly TypeContainer TextExpansion = FindType("UnityEngine.UI.TextExpansion");

		public static readonly TypeContainer Typewriter = FindType("Typewriter");

		public static readonly TypeContainer UguiNovelText = FindType("Utage.UguiNovelText");

		public static readonly TypeContainer UguiNovelTextGenerator = FindType("Utage.UguiNovelTextGenerator");

		public static readonly TypeContainer AdvEngine = FindType("Utage.AdvEngine");

		public static readonly TypeContainer AdvPage = FindType("Utage.AdvPage");

		public static readonly TypeContainer TextData = FindType("Utage.TextData");

		public static readonly TypeContainer AdvUguiMessageWindow = FindType("Utage.AdvUguiMessageWindow") ?? FindType("AdvUguiMessageWindow");

		public static readonly TypeContainer AdvUiMessageWindow = FindType("AdvUiMessageWindow");

		public static readonly TypeContainer AdvDataManager = FindType("Utage.AdvDataManager");

		public static readonly TypeContainer AdvScenarioData = FindType("Utage.AdvScenarioData");

		public static readonly TypeContainer AdvScenarioLabelData = FindType("Utage.AdvScenarioLabelData");

		public static readonly TypeContainer DicingTextures = FindType("Utage.DicingTextures");

		public static readonly TypeContainer DicingImage = FindType("Utage.DicingImage");

		public static readonly TypeContainer TextArea2D = FindType("Utage.TextArea2D");

		public static readonly TypeContainer CubismRenderer = FindType("Live2D.Cubism.Rendering.CubismRenderer");

		public static readonly TypeContainer TextWindow = FindType("Assets.System.Text.TextWindow");

		private static Type FindClrType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType(name, throwOnError: false);
					if ((object)type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static TypeContainer FindType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType(name, throwOnError: false);
					if ((object)type != null)
					{
						return new TypeContainer(type);
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
}

BepInEx/plugins/GameTranslator.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameTranslator.Patches;
using GameTranslator.Patches.Hooks;
using GameTranslator.Patches.Hooks.texture;
using GameTranslator.Patches.InteractiveTerminalAPI;
using GameTranslator.Patches.Translatons;
using GameTranslator.Patches.Translatons.Manipulator;
using GameTranslator.Patches.Utils;
using GameTranslator.Patches.Utils.Textures;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.UIElements;
using XUnity.Common.Constants;
using XUnity.Common.Extensions;
using XUnity.Common.Harmony;
using XUnity.Common.Logging;
using XUnity.Common.MonoMod;
using XUnity.Common.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("GameTranslator")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+07f61dc32e06b0a96fb4f1446ea170f3e3b51376")]
[assembly: AssemblyProduct("GameTranslator")]
[assembly: AssemblyTitle("GameTranslator")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 GameTranslator
{
	public class TranslateConfig
	{
		public class TranslateConfigFile
		{
			public string ConfigFilePath;

			public string ConfigFileName;

			public bool shouldTranslate;

			public int shouldTranslateMinLength = 200;

			public int shouldTranslateMaxLength;

			public bool shouldLoad = true;

			public IDictionary<string, string> normal = new Dictionary<string, string>();

			public IDictionary<string, string> special = new Dictionary<string, string>();

			public static HashSet<TranslateConfigFile> configs = new HashSet<TranslateConfigFile>();

			public Dictionary<string, string> translatePairs = new Dictionary<string, string>();

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

			public List<RegexTranslation> regexTranslations = new List<RegexTranslation>();

			public List<RegexTranslationSplitter> splitterRegexTranslations = new List<RegexTranslationSplitter>();

			private static Regex _regex;

			internal readonly ConcurrentDictionary<string, DateTime> _translatePairLastAccess = new ConcurrentDictionary<string, DateTime>();

			public static Regex regex
			{
				get
				{
					if (_regex == null)
					{
						Type typeFromHandle = typeof(TranslateConfigFile);
						lock (typeFromHandle)
						{
							if (_regex == null)
							{
								_regex = new Regex("(?<!\\\\)=", NormalTextTranslator.RegexCompiledSupportedFlag);
							}
						}
					}
					return _regex;
				}
			}

			public TranslateConfigFile(string configName, bool saveOnInit, bool shouldLoad)
			{
				ConfigFileName = configName;
				ConfigFilePath = TranslatePlugin.DefaultPath + configName + ".cfg";
				this.shouldLoad = shouldLoad;
				if (ConfigFilePath == null)
				{
					throw new ArgumentNullException("configPath");
				}
				ConfigFilePath = Path.GetFullPath(ConfigFilePath);
				if (this.shouldLoad && File.Exists(ConfigFilePath))
				{
					Reload();
				}
				else if (saveOnInit)
				{
					Save();
				}
				configs.Add(this);
			}

			public void Reload()
			{
				translatePairs.Clear();
				_translatePairLastAccess.Clear();
				normal.Clear();
				regexTranslations.Clear();
				splitterRegexTranslations.Clear();
				List<string> list = new List<string>();
				string[] array = File.ReadAllLines(ConfigFilePath);
				foreach (string text in array)
				{
					if (text.StartsWith("#") || !text.Contains("="))
					{
						continue;
					}
					string[] array2 = regex.Split(text);
					if (array2.Length != 2)
					{
						continue;
					}
					string text2 = array2[0].Replace("\\=", "=");
					string text3 = array2[1].Replace("\\=", "=");
					if (text2.StartsWith("r:"))
					{
						try
						{
							RegexTranslation item = new RegexTranslation(text2, text3);
							regexTranslations.Add(item);
						}
						catch (Exception ex)
						{
							string text4 = text2 + "=" + text3;
							list.Add("Invalid regex: " + text4 + " - " + ex.Message);
							TranslatePlugin.logger.LogWarning((object)("Failed to parse regex: " + text4 + ". Error: " + ex.Message));
						}
						continue;
					}
					if (text2.StartsWith("sr:"))
					{
						try
						{
							RegexTranslationSplitter item2 = new RegexTranslationSplitter(text2, text3);
							splitterRegexTranslations.Add(item2);
						}
						catch (Exception ex2)
						{
							string text5 = text2 + "=" + text3;
							list.Add("Invalid splitter regex: " + text5 + " - " + ex2.Message);
							TranslatePlugin.logger.LogWarning((object)("Failed to parse splitter regex: " + text5 + ". Error: " + ex2.Message));
						}
						continue;
					}
					if (normal.ContainsKey(text2))
					{
						normal[text2] = text3;
						special[text3] = text2;
					}
					else
					{
						normal.Add(text2, text3);
						if (!special.ContainsKey(text3))
						{
							special.Add(text3, text2);
						}
					}
					if (text2.Length < shouldTranslateMinLength)
					{
						shouldTranslateMinLength = text2.Length;
					}
					if (text2.Length > shouldTranslateMaxLength)
					{
						shouldTranslateMaxLength = text2.Length;
					}
				}
				if (list.Count > 0)
				{
					File.AppendAllLines(Path.Combine(Path.GetDirectoryName(ConfigFilePath), ConfigFileName + "_errors.log"), list);
				}
			}

			public void Log(string text)
			{
				logs.Add(text);
				string directoryName = Path.GetDirectoryName(ConfigFilePath);
				if (directoryName == null)
				{
					Directory.CreateDirectory(directoryName);
				}
				new List<string>().Add("##" + ConfigFileName);
				File.WriteAllLines(ConfigFilePath, logs);
			}

			public void Save()
			{
				string directoryName = Path.GetDirectoryName(ConfigFilePath);
				if (!Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				if (!File.Exists(ConfigFilePath))
				{
					File.Create(ConfigFilePath).Close();
				}
				else
				{
					if (!shouldLoad)
					{
						return;
					}
					List<string> list = new List<string>();
					list.Add("##" + ConfigFileName);
					foreach (KeyValuePair<string, string> item in normal)
					{
						string text = item.Key.Replace("=", "\\=");
						string text2 = item.Value.Replace("=", "\\=");
						list.Add(text + "=" + text2);
					}
					list.Add("##RegularExpression");
					foreach (RegexTranslation regexTranslation in regexTranslations)
					{
						list.Add("r:" + regexTranslation.Key + "=" + regexTranslation.Value);
					}
					list.Add("##SplitterRegularExpression");
					foreach (RegexTranslationSplitter splitterRegexTranslation in splitterRegexTranslations)
					{
						list.Add("sr:" + splitterRegexTranslation.Key + "=" + splitterRegexTranslation.Value);
					}
					File.WriteAllLines(ConfigFilePath, list.ToArray());
				}
			}
		}

		private static Dictionary<string, DateTime> _fileLastModifiedTimes = new Dictionary<string, DateTime>();

		private static DateTime _lastCheckTime = DateTime.Now;

		public static TranslateConfigFile normal;

		public static TranslateConfigFile hud;

		public static TranslateConfigFile items;

		public static TranslateConfigFile terminal;

		public static TranslateConfigFile text;

		public static TranslateConfigFile cmd_py;

		public static TranslateConfigFile cmd_zh;

		public static TranslateConfigFile gui;

		public static TranslateConfigFile interactiveTerminalAPI;

		public static NormalTextTranslator normalText;

		public static NormalTextTranslator hudText;

		public static NormalTextTranslator guiText;

		public static TextureTranslationCache cache;

		public static NormalTextTranslator itemsText;

		public static NormalTextTranslator terminalText;

		public static NormalTextTranslator textText;

		public static NormalTextTranslator cmdPyText;

		public static NormalTextTranslator cmdZhText;

		public static NormalTextTranslator interactiveTerminalAPIText;

		private static DateTime _lastCleanupTime = DateTime.Now;

		private static readonly TimeSpan CLEANUP_INTERVAL = TimeSpan.FromMinutes(30.0);

		public static void Load()
		{
			hud = CreateNewConfig("HUD-Translate", should: false);
			hud.shouldTranslate = TranslatePlugin.shouldTranslateHUD.Value;
			hudText = new NormalTextTranslator(hud.ConfigFileName + ".cfg");
			hudText.Load();
			items = CreateNewConfig("Item-Translate");
			items.shouldTranslate = TranslatePlugin.shouldTranslateItems.Value;
			itemsText = new NormalTextTranslator(items.ConfigFileName + ".cfg");
			itemsText.Load();
			terminal = CreateNewConfig("Terminal-Translate");
			terminal.shouldTranslate = TranslatePlugin.shouldTranslateTerimal.Value;
			terminalText = new NormalTextTranslator(terminal.ConfigFileName + ".cfg");
			terminalText.Load();
			cmd_py = CreateNewConfig("CMD-PY-Translate");
			cmdPyText = new NormalTextTranslator(cmd_py.ConfigFileName + ".cfg");
			cmdPyText.Load();
			cmd_zh = CreateNewConfig("CMD-ZH-Translate");
			cmdZhText = new NormalTextTranslator(cmd_zh.ConfigFileName + ".cfg");
			cmdZhText.Load();
			text = CreateNewConfig("SpecialText-Translate");
			text.shouldTranslate = TranslatePlugin.shouldTranslateSpecialText.Value;
			textText = new NormalTextTranslator(text.ConfigFileName + ".cfg");
			textText.Load();
			gui = CreateNewConfig("GuiText-Translate");
			gui.shouldTranslate = TranslatePlugin.shouldTranslateGui.Value;
			guiText = new NormalTextTranslator(gui.ConfigFileName + ".cfg");
			guiText.Load();
			interactiveTerminalAPI = CreateNewConfig("InteractiveTerminalAPI-Translate");
			interactiveTerminalAPI.shouldTranslate = TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value;
			interactiveTerminalAPIText = new NormalTextTranslator(interactiveTerminalAPI.ConfigFileName + ".cfg");
			interactiveTerminalAPIText.Load();
			normal = CreateNewConfig("Normal-Translate", should: false);
			normal.shouldTranslate = TranslatePlugin.shouldTranslateNormalText.Value;
			normalText = new NormalTextTranslator(normal.ConfigFileName + ".cfg");
			normalText.Load();
			cache = new TextureTranslationCache();
			cache.LoadTranslationFiles();
			foreach (TranslateConfigFile config in TranslateConfigFile.configs)
			{
				if (File.Exists(config.ConfigFilePath))
				{
					_fileLastModifiedTimes[config.ConfigFilePath] = File.GetLastWriteTime(config.ConfigFilePath);
				}
			}
			AsyncTranslationManager.Instance.ClearCache();
		}

		public static void CheckForFileChanges()
		{
			try
			{
				if (DateTime.Now - _lastCheckTime < TimeSpan.FromSeconds(2.0))
				{
					return;
				}
				_lastCheckTime = DateTime.Now;
				foreach (TranslateConfigFile config in TranslateConfigFile.configs)
				{
					if (!config.shouldLoad || !File.Exists(config.ConfigFilePath))
					{
						continue;
					}
					DateTime lastWriteTime = File.GetLastWriteTime(config.ConfigFilePath);
					if (_fileLastModifiedTimes.TryGetValue(config.ConfigFilePath, out var value))
					{
						if (lastWriteTime > value)
						{
							_fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime;
							config.Reload();
							GetModuleTranslator(config)?.Load();
							TextTranslate.ChangeTime++;
						}
					}
					else
					{
						_fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime;
					}
				}
				TextureTranslationCache.CheckForTextureFileChanges();
			}
			catch (Exception ex)
			{
				TranslatePlugin.logger.LogError((object)("Error in CheckForFileChanges: " + ex.Message));
			}
		}

		public static void show(TranslateConfigFile file)
		{
			foreach (string key in file.normal.Keys)
			{
				TranslatePlugin.LogInfo(key + "=" + file.normal[key]);
			}
		}

		private static TranslateConfigFile CreateNewConfig(string fileName, bool should)
		{
			TranslatePlugin.logger.LogInfo((object)("GameTranslator is loading config file call " + fileName));
			return new TranslateConfigFile(fileName, saveOnInit: true, should);
		}

		public static bool IsStringContainsEnglish(string input)
		{
			if (!string.IsNullOrEmpty(input))
			{
				return new Regex("[a-zA-Z]").IsMatch(input);
			}
			return false;
		}

		public static bool IsStringContainsChinese(string input)
		{
			if (!string.IsNullOrEmpty(input))
			{
				return new Regex("[\\u4e00-\\u9fa5]").IsMatch(input);
			}
			return false;
		}

		public static string useRegularExpression(string raw, string pattern, string result)
		{
			return Regex.Replace(raw, pattern, result);
		}

		public static string replaceByMap(string text, TranslateConfigFile file)
		{
			if (file.normal.Count == 0 && file.regexTranslations.Count == 0 && file.splitterRegexTranslations.Count == 0)
			{
				return text;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				if (DateTime.Now - _lastCleanupTime > CLEANUP_INTERVAL)
				{
					CleanupTranslatePairs();
					_lastCleanupTime = DateTime.Now;
				}
				if (!file.shouldTranslate)
				{
					return text;
				}
				if (file.translatePairs.ContainsKey(text))
				{
					file._translatePairLastAccess.TryAdd(text, DateTime.Now);
					file._translatePairLastAccess[text] = DateTime.Now;
					return file.translatePairs[text];
				}
				StringBuffer stringBuffer = new StringBuffer(text);
				NormalTextTranslator moduleTranslator = GetModuleTranslator(file);
				if (moduleTranslator != null)
				{
					foreach (RegexTranslationSplitter splitterRegex in moduleTranslator._splitterRegexes)
					{
						string str = moduleTranslator.SplitterTranslate(stringBuffer.ToString(), splitterRegex, ignoreCase: false);
						stringBuffer.Clear().Append(str);
					}
					foreach (RegexTranslation defaultRegex in moduleTranslator._defaultRegexes)
					{
						if (defaultRegex.CompiledRegex != null && defaultRegex.CompiledRegex.IsMatch(stringBuffer.ToString()))
						{
							string str2 = defaultRegex.CompiledRegex.Replace(stringBuffer.ToString(), defaultRegex.Translation);
							stringBuffer.Clear().Append(str2);
						}
					}
				}
				foreach (KeyValuePair<string, string> item in file.normal.OrderByDescending((KeyValuePair<string, string> kv) => kv.Key.Length))
				{
					stringBuffer.ReplaceFull(item.Key, item.Value);
				}
				string text2 = stringBuffer.ToString();
				file.translatePairs[text] = text2;
				file._translatePairLastAccess.TryAdd(text, DateTime.Now);
				return text2;
			}
			finally
			{
				stopwatch.Stop();
				if (stopwatch.ElapsedMilliseconds > 500)
				{
					string arg = ((text.Length > 50) ? (text.Substring(0, 50) + "...") : text);
					TranslatePlugin.logger.LogWarning((object)$"replaceByMap took {stopwatch.ElapsedMilliseconds}ms for text: {arg}");
				}
			}
		}

		private static NormalTextTranslator GetModuleTranslator(TranslateConfigFile file)
		{
			if (file == normal)
			{
				return normalText;
			}
			if (file == hud)
			{
				return hudText;
			}
			if (file == items)
			{
				return itemsText;
			}
			if (file == terminal)
			{
				return terminalText;
			}
			if (file == text)
			{
				return textText;
			}
			if (file == cmd_py)
			{
				return cmdPyText;
			}
			if (file == cmd_zh)
			{
				return cmdZhText;
			}
			if (file == gui)
			{
				return guiText;
			}
			if (file == interactiveTerminalAPI)
			{
				return interactiveTerminalAPIText;
			}
			return null;
		}

		private static void CleanupTranslatePairs()
		{
			bool flag = GC.GetTotalMemory(forceFullCollection: false) > 104857600;
			foreach (TranslateConfigFile config in TranslateConfigFile.configs)
			{
				int num = 0;
				if (flag)
				{
					num = (int)((float)config.translatePairs.Count * 0.2f);
					num = Math.Max(1, Math.Min(num, config.translatePairs.Count));
				}
				else if (config.translatePairs.Count > 6000)
				{
					num = config.translatePairs.Count - 6000;
				}
				if (num <= 0)
				{
					continue;
				}
				List<string> list = config._translatePairLastAccess.OrderBy((KeyValuePair<string, DateTime> kv) => kv.Value).Take(num).Select(delegate(KeyValuePair<string, DateTime> kv)
				{
					KeyValuePair<string, DateTime> keyValuePair = kv;
					return keyValuePair.Key;
				})
					.ToList();
				foreach (string item in list)
				{
					config.translatePairs.Remove(item);
					config._translatePairLastAccess.TryRemove(item, out var _);
				}
				TranslatePlugin.logger.LogInfo((object)$"Cleaned {list.Count} translate pairs from {config.ConfigFileName}. Remaining: {config.translatePairs.Count}");
			}
		}

		private static TranslateConfigFile CreateNewConfig(string fileName)
		{
			return CreateNewConfig(fileName, should: true);
		}
	}
	[BepInPlugin("GameTranslator", "GameTranslator", "2.0.6")]
	public class TranslatePlugin : BaseUnityPlugin
	{
		public static ManualLogSource logger;

		public static ConfigEntry<int> syncTranslationThreshold;

		public static ConfigEntry<bool> showAvailableText;

		public static ConfigEntry<bool> showOtherDebug;

		public static ConfigEntry<bool> replaceUnsupportedCharacters;

		public static ConfigEntry<bool> enableGrabbableObjectPatch;

		public static ConfigEntry<bool> enableHUDManagerPatch;

		public static ConfigEntry<bool> enableTerminalPatch;

		public static ConfigEntry<string> language;

		public static ConfigEntry<bool> shouldTranslateNormalText;

		public static ConfigEntry<bool> shouldTranslateSpecialText;

		public static ConfigEntry<bool> shouldTranslateTerimal;

		public static ConfigEntry<bool> shouldTranslateInteractiveTerminalAPI;

		public static ConfigEntry<bool> TerimalCanUseChinese;

		public static ConfigEntry<bool> TerimalCanUsePinyinAbbreviation;

		public static ConfigEntry<bool> shouldTranslateGui;

		public static ConfigEntry<bool> shouldTranslateItems;

		public static ConfigEntry<bool> shouldTranslateHUD;

		public static ConfigEntry<bool> changeFont;

		public static ConfigEntry<string> fallbackFontTextMeshPro;

		public static ConfigEntry<string> shouldRemoveChar;

		public static ConfigEntry<bool> changeTexture;

		public static ConfigEntry<bool> cacheTexturesInMemory;

		public static ConfigEntry<bool> loadUnmodifiedTextures;

		public static ConfigEntry<bool> generateTerimalCommand;

		private const string PLUGIN_GUID = "GameTranslator";

		private const string PLUGIN_NAME = "GameTranslator";

		private const string PLUGIN_VERSION = "2.0.6";

		internal static TranslatePlugin Instance;

		private readonly Harmony harmony = new Harmony("GameTranslator");

		public static string DefaultPath;

		public static string TexturesPath;

		public static bool shouldTranslate;

		public static bool CacheTexturesInMemory => cacheTexturesInMemory.Value;

		private void Awake()
		{
			logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator is loaded");
			ConfigFile();
			HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.All, false);
			HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.Sprite, false);
			HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.SpriteRenderer, false);
			ApplyForcedPatches();
			ApplyGrabbableObjectPatch();
			ApplyHUDManagerPatch();
			ApplyTerminalPatch();
			ApplyInteractiveTerminalAPIPatch();
			if (replaceUnsupportedCharacters.Value)
			{
				FontSupportChecker.InitializeFonts();
			}
			AsyncTranslationManager.Instance.Start();
		}

		private void OnDestroy()
		{
			AsyncTranslationManager.Instance.Stop();
		}

		private void Update()
		{
			try
			{
				TranslateConfig.CheckForFileChanges();
				AsyncTranslationManager.Instance.ProcessMainThreadActions();
			}
			catch (Exception ex)
			{
				logger.LogError((object)("Error in Update: " + ex.Message));
			}
		}

		private void ConfigFile()
		{
			syncTranslationThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("ASync", "Sync Translation Threshold", 200, "Define the character threshold to not use async translation");
			showAvailableText = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Available Text", false, "Define whether to show available text");
			showOtherDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Other Debug", false, "Define whether to show other debug");
			replaceUnsupportedCharacters = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Replace Unsupported Characters", false, "Define whether to replace unsupported characters with Unicode character u25A1");
			enableGrabbableObjectPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable GrabbableObject Patch", true, "Define whether to patch GrabbableObject");
			enableHUDManagerPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable HUDManager Patch", true, "Define whether to patch HUDManager");
			enableTerminalPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Terminal Patch", true, "Define whether to patch Terminal");
			language = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "Language", "Default", "Define what language folder is used");
			shouldTranslateNormalText = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Normal Text", true, "Define whether to use Normal Translate method");
			shouldTranslateSpecialText = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Special Text", false, "Define whether to use SpecialText Translate method");
			shouldTranslateTerimal = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Terminal", false, "Define whether translate Terminal");
			shouldTranslateInteractiveTerminalAPI = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate InteractiveTerminalAPI", false, "Define whether translate InteractiveTerminalAPI");
			TerimalCanUseChinese = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Terminal Can Use Non-English Shortcut Commands", false, "Define whether the terminal can use non-English shortcut commands, such as Chinese");
			TerimalCanUsePinyinAbbreviation = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Terminal Can Use Custom Shortcut Commands", false, "Define whether the terminal can use custom shortcut commands");
			shouldTranslateGui = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Gui", false, "Define whether translate Gui");
			shouldTranslateItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Items", false, "Define whether translate Items");
			shouldTranslateHUD = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate HUD", false, "Define whether translate HUD");
			changeFont = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Change Font", false, "Define whether to change the font");
			fallbackFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "FallbackFontTextMeshPro", "", "Define the fallback font file used");
			shouldRemoveChar = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "Custom Characters", "", "Define what vanilla characters will use custom ones");
			changeTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Change Texture", false, "Define whether to change the texture");
			cacheTexturesInMemory = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Cache Textures In Memory", true, "Define whether to cache texture data in memory for faster loading");
			loadUnmodifiedTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Load Unmodified Textures", false, "Define whether to load textures that have not been modified");
			DefaultPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("GameTranslator.cfg", "translations\\" + language.Value + "\\");
			if (!Directory.Exists(DefaultPath))
			{
				logger.LogWarning((object)("Translation path does not exist: " + DefaultPath));
				try
				{
					Directory.CreateDirectory(DefaultPath);
					logger.LogInfo((object)("Created translation directory: " + DefaultPath));
				}
				catch (Exception ex)
				{
					logger.LogError((object)("Failed to create translation directory: " + ex.Message));
					DefaultPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), "translations", "default");
					Directory.CreateDirectory(DefaultPath);
					logger.LogInfo((object)("Using fallback translation directory: " + DefaultPath));
				}
			}
			TexturesPath = DefaultPath + "Texture\\";
			if (!Directory.Exists(TexturesPath))
			{
				Directory.CreateDirectory(TexturesPath);
			}
			TranslateConfig.Load();
			TranslateExtensions.Load();
		}

		public static List<char> getShouldRemoveChars()
		{
			return shouldRemoveChar.Value.ToCharArray().ToList();
		}

		public static void LogInfo(string info)
		{
			if (logger != null)
			{
				logger.LogInfo((object)info);
			}
		}

		private void ApplyForcedPatches()
		{
			try
			{
				Type[] array = new Type[63]
				{
					typeof(GameObjectHook),
					typeof(GuiContentHook),
					typeof(TeshMeshProHook),
					typeof(TeshMeshProUGUIHook),
					typeof(TextArea2DHook),
					typeof(TextFieldHook),
					typeof(TextHook),
					typeof(TextMeshHook),
					typeof(Texture2DHook),
					typeof(TMP_FontAssetHook),
					typeof(TMP_TextHook),
					typeof(CubismRenderer_MainTexture_Hook),
					typeof(CubismRenderer_TryInitialize_Hook),
					typeof(Cursor_SetCursor_Hook),
					typeof(DicingTextures_GetTexture_Hook),
					typeof(Image_material_Hook),
					typeof(Image_overrideSprite_Hook),
					typeof(Image_sprite_Hook),
					typeof(ImageHooks),
					typeof(MaskableGraphic_OnEnable_Hook),
					typeof(Material_mainTexture_Hook),
					typeof(RawImage_texture_Hook),
					typeof(Sprite_texture_Hook),
					typeof(SpriteRenderer_sprite_Hook),
					typeof(UI2DSprite_material_Hook),
					typeof(UI2DSprite_sprite2D_Hook),
					typeof(UIAtlas_spriteMaterial_Hook),
					typeof(UIPanel_clipTexture_Hook),
					typeof(UIRect_OnInit_Hook),
					typeof(UISprite_atlas_Hook),
					typeof(UISprite_material_Hook),
					typeof(UISprite_OnInit_Hook),
					typeof(UITexture_mainTexture_Hook),
					typeof(UITexture_material_Hook),
					typeof(AsyncTranslationManager),
					typeof(ImageTranslationInfo),
					typeof(NormalTextTranslator),
					typeof(RegexTranslation),
					typeof(RegexTranslationSplitter),
					typeof(TextTranslationInfo),
					typeof(TextureDataResult),
					typeof(TextureTranslationCache),
					typeof(TextureTranslationInfo),
					typeof(TranslatedImage),
					typeof(TranslateExtensions),
					typeof(DefaultTextComponentManipulator),
					typeof(FairyGUITextComponentManipulator),
					typeof(ITextComponentManipulator),
					typeof(TextArea2DComponentManipulator),
					typeof(UguiNovelTextComponentManipulator),
					typeof(FontCache),
					typeof(FontHelper),
					typeof(FontSupportChecker),
					typeof(SceneManagerLoader),
					typeof(StringBuffer),
					typeof(TextHelper),
					typeof(TextTranslate),
					typeof(TextureTranslate),
					typeof(TranslationScopeHelper),
					typeof(ITextureLoader),
					typeof(LoadImageImageLoader),
					typeof(TextureLoader),
					typeof(TgaImageLoader)
				};
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					harmony.PatchAll(type);
				}
				ManualLogSource obj = logger;
				if (obj != null)
				{
					obj.LogInfo((object)"Forced patches applied successfully");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource obj2 = logger;
				if (obj2 != null)
				{
					obj2.LogWarning((object)("Error applying forced patches: " + ex.Message));
				}
			}
		}

		private void ApplyGrabbableObjectPatch()
		{
			try
			{
				if (enableGrabbableObjectPatch != null && enableGrabbableObjectPatch.Value)
				{
					harmony.PatchAll(typeof(GrabbableObjectPatcher));
					ManualLogSource obj = logger;
					if (obj != null)
					{
						obj.LogInfo((object)"GrabbableObject patch applied successfully");
					}
				}
				else
				{
					ManualLogSource obj2 = logger;
					if (obj2 != null)
					{
						obj2.LogInfo((object)"GrabbableObject patch disabled by config");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource obj3 = logger;
				if (obj3 != null)
				{
					obj3.LogWarning((object)("Error applying GrabbableObject patch: " + ex.Message));
				}
			}
		}

		private void ApplyHUDManagerPatch()
		{
			try
			{
				if (enableHUDManagerPatch != null && enableHUDManagerPatch.Value)
				{
					harmony.PatchAll(typeof(HUDManagerPatcher));
					ManualLogSource obj = logger;
					if (obj != null)
					{
						obj.LogInfo((object)"HUDManager patch applied successfully");
					}
				}
				else
				{
					ManualLogSource obj2 = logger;
					if (obj2 != null)
					{
						obj2.LogInfo((object)"HUDManager patch disabled by config");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource obj3 = logger;
				if (obj3 != null)
				{
					obj3.LogWarning((object)("Error applying HUDManager patch: " + ex.Message));
				}
			}
		}

		private void ApplyTerminalPatch()
		{
			try
			{
				if (enableTerminalPatch != null && enableTerminalPatch.Value)
				{
					harmony.PatchAll(typeof(TerminalPatch));
					ManualLogSource obj = logger;
					if (obj != null)
					{
						obj.LogInfo((object)"Terminal patch applied successfully");
					}
				}
				else
				{
					ManualLogSource obj2 = logger;
					if (obj2 != null)
					{
						obj2.LogInfo((object)"Terminal patch disabled by config");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource obj3 = logger;
				if (obj3 != null)
				{
					obj3.LogWarning((object)("Error applying Terminal patch: " + ex.Message));
				}
			}
		}

		private void ApplyInteractiveTerminalAPIPatch()
		{
			try
			{
				if (shouldTranslateInteractiveTerminalAPI != null && shouldTranslateInteractiveTerminalAPI.Value)
				{
					harmony.PatchAll(typeof(InteractiveTerminalAPIPatch));
					ManualLogSource obj = logger;
					if (obj != null)
					{
						obj.LogInfo((object)"InteractiveTerminalAPI patch applied successfully");
					}
					InteractiveTerminalAPIPatch.Initialize();
				}
				else
				{
					ManualLogSource obj2 = logger;
					if (obj2 != null)
					{
						obj2.LogInfo((object)"InteractiveTerminalAPI patch disabled by config");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource obj3 = logger;
				if (obj3 != null)
				{
					obj3.LogWarning((object)("Error applying InteractiveTerminalAPI patch: " + ex.Message));
				}
			}
		}
	}
}
namespace GameTranslator.Patches
{
	[HarmonyPatch(typeof(GrabbableObject))]
	internal class GrabbableObjectPatcher
	{
		public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>();

		public static HashSet<string> translatedItems = new HashSet<string>();

		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void start(ref GrabbableObject __instance)
		{
			if (!((Object)(object)__instance.itemProperties != (Object)null) || !TranslatePlugin.shouldTranslateItems.Value || Utility.IsNullOrWhiteSpace(__instance.itemProperties.itemName))
			{
				return;
			}
			if (!translatedItems.Contains(__instance.itemProperties.itemName))
			{
				originToTranslated[__instance.itemProperties.itemName] = TranslateConfig.replaceByMap(__instance.itemProperties.itemName, TranslateConfig.items);
				translatedItems.Add(originToTranslated[__instance.itemProperties.itemName]);
				__instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName];
			}
			if (originToTranslated.ContainsKey(__instance.itemProperties.itemName))
			{
				__instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName];
			}
			ScanNodeProperties componentInChildren = ((Component)__instance).GetComponentInChildren<ScanNodeProperties>();
			if ((Object)(object)componentInChildren != (Object)null && componentInChildren.headerText != null)
			{
				if (!translatedItems.Contains(componentInChildren.headerText))
				{
					originToTranslated[componentInChildren.headerText] = TranslateConfig.replaceByMap(componentInChildren.headerText, TranslateConfig.items);
					translatedItems.Add(originToTranslated[componentInChildren.headerText]);
					componentInChildren.headerText = originToTranslated[componentInChildren.headerText];
				}
				if (originToTranslated.ContainsKey(componentInChildren.headerText))
				{
					componentInChildren.headerText = originToTranslated[componentInChildren.headerText];
				}
			}
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	internal class HUDManagerPatcher
	{
		public static FieldInfo scanNodes = typeof(HUDManager).GetField("scanNodes", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

		public static FieldInfo nodesOnScreen = typeof(HUDManager).GetField("nodesOnScreen", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

		public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>();

		public static HashSet<string> translatedItems = new HashSet<string>();

		private static string lastChat = "";

		private static readonly BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static MethodInfo CanTipDisplay = typeof(HUDManager).GetMethod("CanTipDisplay", All);

		private static MethodInfo StopCoroutine = typeof(HUDManager).GetMethod("StopCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null);

		private static MethodInfo StartCoroutine = typeof(HUDManager).GetMethod("StartCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null);

		private static MethodInfo TipsPanelTimer = typeof(HUDManager).GetMethod("TipsPanelTimer", All);

		private static FieldInfo tipsPanelCoroutine = typeof(HUDManager).GetField("tipsPanelCoroutine", All);

		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void start(ref HUDManager __instance)
		{
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void update(HUDManager __instance)
		{
			if (!TranslatePlugin.shouldTranslateHUD.Value)
			{
				return;
			}
			string text = ((TMP_Text)__instance.chatText).text;
			if (lastChat.Length != text.Length)
			{
				string text2 = TranslateConfig.hudText.TryTranslate(text);
				if (!string.IsNullOrEmpty(text2) && text2 != text)
				{
					((TMP_Text)__instance.chatText).text = text2;
				}
			}
			lastChat = text;
		}

		private static void fadeText(TextMeshProUGUI text, ref bool fade, float duration, float newAlpha)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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)
			float a = ((Graphic)text).color.a;
			float num = 0f;
			fade = true;
			while (num < duration)
			{
				num += Time.deltaTime;
				float num2 = Mathf.Lerp(a, newAlpha, num / duration);
				((Graphic)text).color = new Color(((Graphic)text).color.r, ((Graphic)text).color.g, ((Graphic)text).color.b, num2);
			}
			((Behaviour)text).enabled = false;
			fade = false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("AddChatMessage")]
		private static void changeChatMessage(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped)
		{
			if (!TranslatePlugin.shouldTranslateHUD.Value || (Object)(object)__instance == (Object)null || ((object)__instance).Equals((object?)null))
			{
				return;
			}
			TMP_Text chatText = (TMP_Text)(object)__instance.chatText;
			if ((Object)(object)chatText == (Object)null || ((object)chatText).Equals((object?)null) || !((Component)chatText).gameObject.activeInHierarchy)
			{
				return;
			}
			try
			{
				string text = chatText.text;
				if (!string.IsNullOrEmpty(text))
				{
					string text2 = TranslateConfig.hudText.TryTranslate(text);
					if (!string.IsNullOrEmpty(text2) && !text2.Equals(text))
					{
						chatText.text = text2;
					}
				}
			}
			catch (Exception ex)
			{
				TranslatePlugin.logger.LogError((object)("Error in changeChatMessage: " + ex.Message));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("DisplayTip")]
		[HarmonyPriority(int.MaxValue)]
		private static bool changeTip(HUDManager __instance, ref string headerText, ref string bodyText, bool isWarning = false, bool useSave = false, string prefsKey = "LC_Tip1")
		{
			if (TranslatePlugin.shouldTranslateHUD.Value)
			{
				headerText = TranslateConfig.hudText.TryTranslate(headerText);
				bodyText = TranslateConfig.hudText.TryTranslate(bodyText);
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch("DisplayGlobalNotification")]
		[HarmonyPriority(int.MaxValue)]
		private static bool changeNotification(HUDManager __instance, ref string displayText)
		{
			if (TranslatePlugin.shouldTranslateHUD.Value)
			{
				displayText = TranslateConfig.hudText.TryTranslate(displayText);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Terminal))]
	internal class TerminalPatch
	{
		public class Translator
		{
			public Dictionary<string, TerminalKeyword> keyValuePairs = new Dictionary<string, TerminalKeyword>();

			public Dictionary<string, string> keys = new Dictionary<string, string>();

			public TerminalKeyword getTranslateKey(TerminalKeyword old, bool useC)
			{
				if (!keyValuePairs.ContainsKey(((Object)old).name + old.word))
				{
					if ((useC && !HaveChineseCMD(old.word)) || (!useC && !HavePinyinCMD(old.word)))
					{
						keyValuePairs.Add(((Object)old).name + old.word, old);
						return old;
					}
					string text = (useC ? getChineseCMD(old.word) : getPinyinCMD(old.word));
					if (text == old.word)
					{
						keyValuePairs.Add(((Object)old).name + old.word, old);
						return old;
					}
					TerminalKeyword val = Copy(old);
					val.word = text;
					keyValuePairs.Add(((Object)old).name + old.word, val);
					if (val.compatibleNouns != null)
					{
						List<CompatibleNoun> list = val.compatibleNouns.ToList();
						CompatibleNoun[] compatibleNouns = val.compatibleNouns;
						foreach (CompatibleNoun val2 in compatibleNouns)
						{
							if (!getTranslateKey(val2.noun, useC).word.Equals(val2.noun.word))
							{
								CompatibleNoun val3 = TransReflection<CompatibleNoun>(val2);
								val3.noun = getTranslateKey(val2.noun, useC);
								list.Add(val3);
							}
						}
						val.compatibleNouns = list.ToArray();
					}
					if ((Object)(object)val.defaultVerb != (Object)null && val.defaultVerb.compatibleNouns != null)
					{
						List<CompatibleNoun> list2 = val.defaultVerb.compatibleNouns.ToList();
						CompatibleNoun[] compatibleNouns2 = val.defaultVerb.compatibleNouns;
						foreach (CompatibleNoun val4 in compatibleNouns2)
						{
							if (!getTranslateKey(val4.noun, useC).word.Equals(val4.noun.word))
							{
								CompatibleNoun val5 = TransReflection<CompatibleNoun>(val4);
								val5.noun = getTranslateKey(val4.noun, useC);
								list2.Add(val5);
							}
						}
						val.defaultVerb.compatibleNouns = list2.ToArray();
					}
					return val;
				}
				return keyValuePairs[((Object)old).name + old.word];
			}

			public void getTranslateKey(TerminalKeyword old)
			{
				if (keys.ContainsKey(old.word))
				{
					return;
				}
				keys.Add(old.word, old.word);
				if (old.compatibleNouns != null)
				{
					old.compatibleNouns.ToList();
					CompatibleNoun[] compatibleNouns = old.compatibleNouns;
					foreach (CompatibleNoun val in compatibleNouns)
					{
						getTranslateKey(val.noun);
					}
				}
				if ((Object)(object)old.defaultVerb != (Object)null && old.defaultVerb.compatibleNouns != null)
				{
					old.defaultVerb.compatibleNouns.ToList();
					CompatibleNoun[] compatibleNouns2 = old.defaultVerb.compatibleNouns;
					foreach (CompatibleNoun val2 in compatibleNouns2)
					{
						getTranslateKey(val2.noun);
					}
				}
			}
		}

		public static FieldInfo hasGottenVerb = typeof(Terminal).GetField("hasGottenVerb", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

		public static FieldInfo hasGottenNoun = typeof(Terminal).GetField("hasGottenNoun", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

		public static bool shouldTranslate = false;

		public static bool noText = false;

		public static int texdAdded = 0;

		public static FieldInfo modifyingText = typeof(Terminal).GetField("modifyingText", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

		public static Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();

		public static TMP_InputField screenText = null;

		public static TextTranslationInfo info;

		public static HashSet<object> ig = new HashSet<object>();

		private static int CheckForPlayerNameCommand(string firstWord, string secondWord)
		{
			if (firstWord == "radar")
			{
				return -1;
			}
			if (secondWord.Length <= 2)
			{
				return -1;
			}
			Debug.Log((object)("first word: " + firstWord + "; second word: " + secondWord));
			List<string> list = new List<string>();
			for (int i = 0; i < StartOfRound.Instance.mapScreen.radarTargets.Count; i++)
			{
				list.Add(StartOfRound.Instance.mapScreen.radarTargets[i].name);
				Debug.Log((object)$"name {i}: {list[i]}");
			}
			secondWord = secondWord.ToLower();
			for (int j = 0; j < list.Count; j++)
			{
				if (list[j].ToLower() == secondWord)
				{
					return j;
				}
			}
			Debug.Log((object)$"Target names length: {list.Count}");
			for (int k = 0; k < list.Count; k++)
			{
				Debug.Log((object)"A");
				string text = list[k].ToLower();
				Debug.Log((object)$"Word #{k}: {text}; length: {text.Length}");
				for (int num = secondWord.Length; num > 2; num--)
				{
					Debug.Log((object)$"c: {num}");
					Debug.Log((object)secondWord.Substring(0, num));
					if (text.StartsWith(secondWord.Substring(0, num)))
					{
						return k;
					}
				}
			}
			return -1;
		}

		[HarmonyPostfix]
		[HarmonyPatch("ParseWordOverrideOptions")]
		private static void ParseWordOverrideOptions(string playerWord, CompatibleNoun[] options, ref TerminalNode __result)
		{
			for (int i = 0; i < options.Length; i++)
			{
				for (int num = playerWord.Length; num > 0; num--)
				{
					if (getChineseCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
					{
						__result = options[i].result;
						return;
					}
					if (getPinyinCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
					{
						__result = options[i].result;
						return;
					}
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("CheckForExactSentences")]
		private static void CheckForExactSentences(Terminal __instance, string playerWord, ref TerminalKeyword __result)
		{
			for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++)
			{
				if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
				{
					__result = __instance.terminalNodes.allKeywords[i];
					break;
				}
				if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
				{
					__result = __instance.terminalNodes.allKeywords[i];
					break;
				}
			}
		}

		private static string RemovePunctuation(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in s)
			{
				if (!char.IsPunctuation(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().ToLower();
		}

		[HarmonyPostfix]
		[HarmonyPatch("CallFunctionInAccessibleTerminalObject")]
		private static void CallFunctionInAccessibleTerminalObject(Terminal __instance, string word)
		{
			TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>();
			for (int i = 0; i < array.Length; i++)
			{
				if (getChineseCMD(array[i].objectCode).EqualsIgnoreCase(word) || getPinyinCMD(array[i].objectCode).EqualsIgnoreCase(word))
				{
					Debug.Log((object)"Found accessible terminal object with corresponding string, calling function");
					((object)__instance).GetType().GetField("broadcastedCodeThisFrame", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(TranslateConfig.terminal, true);
					array[i].CallFunctionFromTerminal();
					break;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("ParseWord")]
		private static void ParseWord(Terminal __instance, string playerWord, int specificityRequired, ref TerminalKeyword __result)
		{
			if (!TranslatePlugin.TerimalCanUseChinese.Value && !TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value)
			{
				return;
			}
			if (playerWord.Length < specificityRequired)
			{
				__result = null;
				return;
			}
			TerminalKeyword val = null;
			for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++)
			{
				if (__instance.terminalNodes.allKeywords[i].isVerb && (bool)hasGottenVerb.GetValue(__instance))
				{
					continue;
				}
				bool accessTerminalObjects = __instance.terminalNodes.allKeywords[i].accessTerminalObjects;
				if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
				{
					__result = __instance.terminalNodes.allKeywords[i];
					return;
				}
				if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
				{
					__result = __instance.terminalNodes.allKeywords[i];
					return;
				}
				if (!((Object)(object)val == (Object)null))
				{
					continue;
				}
				for (int num = playerWord.Length; num > specificityRequired; num--)
				{
					if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
					{
						val = __instance.terminalNodes.allKeywords[i];
					}
					if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
					{
						val = __instance.terminalNodes.allKeywords[i];
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				__result = val;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("ParsePlayerSentence")]
		private static void customParser(Terminal __instance, ref TerminalNode __result)
		{
			string[] array = RemovePunctuation(__instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded)).Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
			if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["transmit"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["transmit"]))))
			{
				try
				{
					string text = array[1];
					SignalTranslator val = Object.FindObjectOfType<SignalTranslator>();
					if ((Object)(object)val != (Object)null && Time.realtimeSinceStartup - val.timeLastUsingSignalTranslator > 8f && text.Length > 1)
					{
						if (!((NetworkBehaviour)__instance).IsServer)
						{
							val.timeLastUsingSignalTranslator = Time.realtimeSinceStartup;
						}
						__result = __instance.terminalNodes.specialNodes[22];
						HUDManager.Instance.UseSignalTranslatorServerRpc(text.Substring(0, Mathf.Min(text.Length, 10)));
					}
					return;
				}
				catch (Exception ex)
				{
					TranslatePlugin.logger.LogError((object)ex.Message);
					return;
				}
			}
			if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["switch"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["switch"]))))
			{
				int num = CheckForPlayerNameCommand(array[0], array[1]);
				if (num != -1)
				{
					StartOfRound.Instance.mapScreen.SwitchRadarTargetAndSync(num);
					__result = __instance.terminalNodes.specialNodes[20];
				}
			}
			else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["ping"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["ping"]))))
			{
				int num2 = CheckForPlayerNameCommand(array[0], array[1]);
				if (num2 != -1)
				{
					StartOfRound.Instance.mapScreen.PingRadarBooster(num2);
					__result = __instance.terminalNodes.specialNodes[21];
				}
			}
			else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["flash"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["flash"]))))
			{
				int num3 = CheckForPlayerNameCommand(array[0], array[1]);
				if (num3 != -1)
				{
					StartOfRound.Instance.mapScreen.FlashRadarBooster(num3);
					__result = __instance.terminalNodes.specialNodes[23];
				}
				else if (StartOfRound.Instance.mapScreen.radarTargets[StartOfRound.Instance.mapScreen.targetTransformIndex].isNonPlayer)
				{
					StartOfRound.Instance.mapScreen.FlashRadarBooster(StartOfRound.Instance.mapScreen.targetTransformIndex);
					__result = __instance.terminalNodes.specialNodes[23];
				}
			}
		}

		private static bool HaveChineseCMD(string name)
		{
			return TranslateConfig.cmd_zh.normal.ContainsKey(name);
		}

		private static bool HavePinyinCMD(string name)
		{
			return TranslateConfig.cmd_py.normal.ContainsKey(name);
		}

		private static string getChineseCMD(string name)
		{
			if (TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey(name))
			{
				return TranslateConfig.cmd_zh.normal[name];
			}
			return "";
		}

		private static string getPinyinCMD(string name)
		{
			if (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey(name))
			{
				return TranslateConfig.cmd_py.normal[name];
			}
			return "";
		}

		private static string getOrginByChineseCMD(string name)
		{
			if (TranslateConfig.cmd_zh.special.ContainsKey(name))
			{
				return TranslateConfig.cmd_zh.special[name];
			}
			return name;
		}

		private static string getOrginByPinyinCMD(string name)
		{
			if (TranslateConfig.cmd_py.special.ContainsKey(name))
			{
				return TranslateConfig.cmd_py.special[name];
			}
			return name;
		}

		private static T TransReflection<T>(T tIn)
		{
			T val = Activator.CreateInstance<T>();
			foreach (FieldInfo runtimeField in tIn.GetType().GetRuntimeFields())
			{
				val.GetType().GetRuntimeField(runtimeField.Name).SetValue(val, runtimeField.GetValue(tIn));
			}
			return val;
		}

		public static TerminalKeyword Copy(TerminalKeyword oldOne)
		{
			return TransReflection<TerminalKeyword>(oldOne);
		}

		[HarmonyPostfix]
		[HarmonyPatch("LoadNewNode")]
		private static void changeNewNodeText(Terminal __instance, TerminalNode node)
		{
			if (info != null)
			{
				info.Reset(__instance.screenText.text);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnSubmit")]
		private static void changeSubmit(Terminal __instance)
		{
			if (info != null)
			{
				texdAdded = __instance.currentText.Length - info.OriginalText.Length;
				if (texdAdded != 0)
				{
					info.Reset(__instance.currentText);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void changeUpdateText(Terminal __instance)
		{
			try
			{
				if ((info == null || !info.IsTranslated) && info != null && !info.IsTranslated && TranslatePlugin.shouldTranslateTerimal.Value)
				{
					if (TranslatePlugin.showAvailableText.Value && !string.IsNullOrEmpty(__instance.currentText) && TextTranslate.ShouldOutputDebug("terminal:" + __instance.currentText))
					{
						TranslatePlugin.LogInfo("[Debug] Terminal available text: '" + __instance.currentText + "'");
					}
					string translatedText = TranslateConfig.replaceByMap(__instance.currentText, TranslateConfig.terminal);
					info.SetTranslatedText(translatedText);
					SetText(info.TranslatedText, __instance);
					info.OriginalText = __instance.currentText;
				}
			}
			catch (Exception ex)
			{
				TranslatePlugin.logger.LogWarning((object)ex);
			}
		}

		public static void SetText(string text, Terminal Instance)
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				modifyingText.SetValue(Instance, true);
				((Selectable)Instance.screenText).interactable = true;
				Instance.screenText.text = text;
				Instance.currentText = Instance.screenText.text;
				if ((Object)(object)Instance.screenText.verticalScrollbar != (Object)null)
				{
					Instance.screenText.verticalScrollbar.value = 0f;
				}
			}
		}

		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void startTerminal(Terminal __instance)
		{
			info = __instance.screenText.GetOrCreateTextTranslationInfo();
			ig.Clear();
			foreach (FieldInfo runtimeField in ((object)__instance).GetType().GetRuntimeFields())
			{
				if (runtimeField.GetValue(__instance) != null && UnityTypes.TMP_Text.IsAssignableFrom(runtimeField.GetValue(__instance).GetType()))
				{
					ig.Add(runtimeField.GetValue(__instance));
				}
			}
			info.MustIgnore = true;
		}
	}
}
namespace GameTranslator.Patches.Utils
{
	internal static class FontCache
	{
		private static bool _hasReadFallbackFontTextMeshPro;

		private static Object FallbackFontTextMeshPro;

		public static object GetOrCreateFallbackFontTextMeshPro()
		{
			if (!_hasReadFallbackFontTextMeshPro)
			{
				try
				{
					_hasReadFallbackFontTextMeshPro = true;
					if (string.IsNullOrEmpty(TranslatePlugin.fallbackFontTextMeshPro.Value))
					{
						FallbackFontTextMeshPro = null;
						return null;
					}
					string assetBundle = Path.Combine(TranslatePlugin.DefaultPath, TranslatePlugin.fallbackFontTextMeshPro.Value);
					FallbackFontTextMeshPro = FontHelper.GetTextMeshProFont(assetBundle);
				}
				catch (Exception ex) when (ex.ToString().ToLowerInvariant().Contains("missing") || ex.ToString().ToLowerInvariant().Contains("not found"))
				{
					TranslatePlugin.logger.LogWarning((object)("An error occurred while loading text mesh pro fallback font. This may be due to missing font file. Error: " + ex.Message));
				}
				catch (Exception ex2)
				{
					TranslatePlugin.logger.LogError((object)("An error occurred while loading text mesh pro fallback font: " + TranslatePlugin.fallbackFontTextMeshPro.Value + ". Error: " + ex2.Message));
				}
			}
			return FallbackFontTextMeshPro;
		}
	}
	internal static class FontHelper
	{
		private static readonly List<AssetBundle> _loadedBundles = new List<AssetBundle>();

		public static Object GetTextMeshProFont(string assetBundle)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			if (string.IsNullOrEmpty(assetBundle))
			{
				return null;
			}
			Object val = null;
			string text = Path.Combine(Paths.GameRoot, assetBundle);
			if (File.Exists(text))
			{
				TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from asset bundle: " + text));
				AssetBundle val2 = null;
				if (AssetBundle_Methods.LoadFromFile != null)
				{
					val2 = (AssetBundle)AssetBundle_Methods.LoadFromFile.Invoke((object)null, new object[1] { text });
				}
				else
				{
					if (AssetBundle_Methods.CreateFromFile == null)
					{
						TranslatePlugin.logger.LogError((object)("Could not find an appropriate asset bundle load method while loading font: " + text));
						return null;
					}
					val2 = (AssetBundle)AssetBundle_Methods.CreateFromFile.Invoke((object)null, new object[1] { text });
				}
				if ((Object)(object)val2 == (Object)null)
				{
					TranslatePlugin.logger.LogWarning((object)("Could not load asset bundle while loading font: " + text));
					return null;
				}
				_loadedBundles.Add(val2);
				if (UnityTypes.TMP_FontAsset != null)
				{
					if (AssetBundle_Methods.LoadAllAssets != null)
					{
						val = ((Object[])AssetBundle_Methods.LoadAllAssets.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault();
					}
					else if (AssetBundle_Methods.LoadAll != null)
					{
						val = ((Object[])AssetBundle_Methods.LoadAll.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault();
					}
				}
			}
			else
			{
				TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from internal Resources API: " + text));
				val = Resources.Load(assetBundle);
			}
			if (val != (Object)null)
			{
				CachedProperty version = TMP_FontAsset_Properties.Version;
				string text2 = ((string)((version != null) ? version.Get((object)val) : null)) ?? "Unknown";
				TranslatePlugin.logger.LogInfo((object)("Loaded TextMesh Pro font uses version: " + text2));
				Object.DontDestroyOnLoad(val);
			}
			else
			{
				TranslatePlugin.logger.LogError((object)("Could not find the TextMeshPro font asset: " + assetBundle));
			}
			return val;
		}

		public static string[] GetOSInstalledFontNames()
		{
			return Font.GetOSInstalledFontNames();
		}

		public static void UnloadAllBundles()
		{
			foreach (AssetBundle loadedBundle in _loadedBundles)
			{
				try
				{
					if ((Object)(object)loadedBundle != (Object)null)
					{
						loadedBundle.Unload(true);
					}
				}
				catch (Exception ex)
				{
					TranslatePlugin.logger.LogError((object)("Error unloading bundle: " + ex.Message));
				}
			}
			_loadedBundles.Clear();
		}
	}
	public static class FontSupportChecker
	{
		private static readonly List<TMP_FontAsset> _availableFonts = new List<TMP_FontAsset>();

		private static readonly Dictionary<char, bool> _characterSupportCache = new Dictionary<char, bool>();

		private static readonly LRUCache<string, string> _textCache = new LRUCache<string, string>(1000);

		private static bool _isInitialized = false;

		private static readonly object _lockObject = new object();

		public static void InitializeFonts()
		{
			if (_isInitialized)
			{
				return;
			}
			lock (_lockObject)
			{
				if (_isInitialized)
				{
					return;
				}
				_availableFonts.Clear();
				_characterSupportCache.Clear();
				_textCache.Clear();
				object orCreateFallbackFontTextMeshPro = FontCache.GetOrCreateFallbackFontTextMeshPro();
				TMP_FontAsset val = (TMP_FontAsset)((orCreateFallbackFontTextMeshPro is TMP_FontAsset) ? orCreateFallbackFontTextMeshPro : null);
				if ((Object)(object)val != (Object)null)
				{
					AddFont(val);
				}
				TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
				TMP_FontAsset[] array2 = array;
				foreach (TMP_FontAsset val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null && !_availableFonts.Contains(val2))
					{
						AddFont(val2);
					}
				}
				_isInitialized = true;
				TranslatePlugin.logger.LogInfo((object)$"FontSupportChecker initialized with {_availableFonts.Count} fonts");
			}
		}

		private static void AddFont(TMP_FontAsset font)
		{
			if ((Object)(object)font == (Object)null || _availableFonts.Contains(font))
			{
				return;
			}
			_availableFonts.Add(font);
			if (font.fallbackFontAssetTable == null)
			{
				return;
			}
			foreach (TMP_FontAsset item in font.fallbackFontAssetTable)
			{
				if ((Object)(object)item != (Object)null && !_availableFonts.Contains(item))
				{
					_availableFonts.Add(item);
				}
			}
		}

		public static void RegisterFont(TMP_FontAsset font)
		{
			if ((Object)(object)font == (Object)null)
			{
				return;
			}
			lock (_lockObject)
			{
				AddFont(font);
				_characterSupportCache.Clear();
				_textCache.Clear();
				TranslatePlugin.logger.LogDebug((object)("Registered new font: " + ((Object)font).name));
			}
		}

		public static bool IsCharacterSupported(char character)
		{
			if (!_isInitialized)
			{
				return true;
			}
			if (_characterSupportCache.TryGetValue(character, out var value))
			{
				return value;
			}
			value = _availableFonts.Any((TMP_FontAsset font) => (Object)(object)font != (Object)null && font.HasCharacter(character, false, false));
			_characterSupportCache[character] = value;
			return value;
		}

		public static string ReplaceUnsupportedCharacters(string text)
		{
			if (string.IsNullOrEmpty(text) || !TranslatePlugin.replaceUnsupportedCharacters.Value)
			{
				return text;
			}
			if (!_isInitialized)
			{
				InitializeFonts();
			}
			if (_textCache.TryGetValue(text, out string value))
			{
				return value;
			}
			bool flag = true;
			foreach (char character in text)
			{
				if (!IsCharacterSupported(character))
				{
					flag = false;
					break;
				}
			}
			if (flag)
			{
				_textCache.Add(text, text);
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			bool flag2 = false;
			foreach (char c in text)
			{
				if (IsCharacterSupported(c))
				{
					stringBuilder.Append(c);
					continue;
				}
				stringBuilder.Append('□');
				flag2 = true;
			}
			string text2 = stringBuilder.ToString();
			if (flag2 && TranslatePlugin.showOtherDebug.Value)
			{
				TranslatePlugin.logger.LogInfo((object)("[FontSupport] Replaced unsupported characters in text: '" + text + "' -> '" + text2 + "'"));
			}
			_textCache.Add(text, text2);
			return text2;
		}

		public static void ClearCache()
		{
			lock (_lockObject)
			{
				_characterSupportCache.Clear();
				_textCache.Clear();
				TranslatePlugin.logger.LogDebug((object)"FontSupportChecker cache cleared");
			}
		}

		public static string GetStats()
		{
			return $"Fonts: {_availableFonts.Count}, CharacterCache: {_characterSupportCache.Count}, TextCache: {_textCache.Count}";
		}
	}
	public class LRUCache<TKey, TValue>
	{
		private class CacheItem
		{
			public TKey Key { get; set; }

			public TValue Value { get; set; }
		}

		private readonly int _capacity;

		private readonly Dictionary<TKey, LinkedListNode<CacheItem>> _cacheMap;

		private readonly LinkedList<CacheItem> _lruList;

		public int Count => _cacheMap.Count;

		public LRUCache(int capacity)
		{
			_capacity = capacity;
			_cacheMap = new Dictionary<TKey, LinkedListNode<CacheItem>>(capacity);
			_lruList = new LinkedList<CacheItem>();
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			if (_cacheMap.TryGetValue(key, out LinkedListNode<CacheItem> value2))
			{
				value = value2.Value.Value;
				_lruList.Remove(value2);
				_lruList.AddFirst(value2);
				return true;
			}
			value = default(TValue);
			return false;
		}

		public void Add(TKey key, TValue value)
		{
			if (_cacheMap.TryGetValue(key, out LinkedListNode<CacheItem> value2))
			{
				_lruList.Remove(value2);
			}
			else if (_cacheMap.Count >= _capacity)
			{
				RemoveLeastRecentlyUsed();
			}
			LinkedListNode<CacheItem> linkedListNode = new LinkedListNode<CacheItem>(new CacheItem
			{
				Key = key,
				Value = value
			});
			_lruList.AddFirst(linkedListNode);
			_cacheMap[key] = linkedListNode;
		}

		public void Clear()
		{
			_cacheMap.Clear();
			_lruList.Clear();
		}

		private void RemoveLeastRecentlyUsed()
		{
			LinkedListNode<CacheItem> last = _lruList.Last;
			if (last != null)
			{
				_cacheMap.Remove(last.Value.Key);
				_lruList.RemoveLast();
			}
		}
	}
	internal static class SceneManagerLoader
	{
		public static void EnableSceneLoadScanInternal(Action<int> sceneLoaded)
		{
			Action<int> sceneLoaded2 = sceneLoaded;
			SceneManager.sceneLoaded += delegate(Scene arg1, LoadSceneMode arg2)
			{
				sceneLoaded2(((Scene)(ref arg1)).buildIndex);
			};
		}
	}
	public class StringBuffer
	{
		private char[] value;

		private int length;

		private int capacity;

		private const int DEFAULT_CAPACITY = 16;

		public int Length
		{
			get
			{
				return length;
			}
			set
			{
				if (value < 0 || value > capacity)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				if (value < length)
				{
					Array.Clear(this.value, value, length - value);
				}
				length = value;
			}
		}

		public int Capacity
		{
			get
			{
				return capacity;
			}
			set
			{
				if (value < length)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				if (value != capacity)
				{
					char[] destinationArray = new char[value];
					Array.Copy(this.value, 0, destinationArray, 0, length);
					this.value = destinationArray;
					capacity = value;
				}
			}
		}

		public StringBuffer()
		{
			value = new char[16];
			length = 0;
			capacity = 16;
		}

		public StringBuffer(int capacity)
		{
			if (capacity < 0)
			{
				throw new ArgumentOutOfRangeException("capacity");
			}
			value = new char[capacity];
			length = 0;
			this.capacity = capacity;
		}

		public StringBuffer(string str)
		{
			if (str == null)
			{
				throw new ArgumentNullException("str");
			}
			value = new char[str.Length + 16];
			str.CopyTo(0, value, 0, str.Length);
			length = str.Length;
			capacity = str.Length + 16;
		}

		public void EnsureCapacity(int minimumCapacity)
		{
			if (minimumCapacity < 0)
			{
				throw new ArgumentOutOfRangeException("minimumCapacity");
			}
			if (minimumCapacity > capacity)
			{
				int num = capacity * 2;
				if (num < minimumCapacity)
				{
					num = minimumCapacity;
				}
				Capacity = num;
			}
		}

		public StringBuffer Append(object obj)
		{
			if (obj == null)
			{
				return this;
			}
			return Append(obj.ToString());
		}

		public StringBuffer Append(string str)
		{
			if (str == null)
			{
				return this;
			}
			int num = str.Length;
			EnsureCapacity(length + num);
			str.CopyTo(0, value, length, num);
			length += num;
			return this;
		}

		public StringBuffer Append(char c)
		{
			EnsureCapacity(length + 1);
			value[length] = c;
			length++;
			return this;
		}

		public StringBuffer Insert(int index, object obj)
		{
			if (obj == null)
			{
				return this;
			}
			return Insert(index, obj.ToString());
		}

		public StringBuffer Insert(int index, string str)
		{
			if (index < 0 || index > length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			if (str == null)
			{
				return this;
			}
			int num = str.Length;
			EnsureCapacity(length + num);
			Array.Copy(value, index, value, index + num, length - index);
			str.CopyTo(0, value, index, num);
			length += num;
			return this;
		}

		public StringBuffer Insert(int index, char c)
		{
			if (index < 0 || index > length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			EnsureCapacity(length + 1);
			Array.Copy(value, index, value, index + 1, length - index);
			value[index] = c;
			length++;
			return this;
		}

		public StringBuffer Remove(int startIndex, int length)
		{
			if (startIndex < 0 || startIndex > this.length)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || startIndex + length > this.length)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			Array.Copy(value, startIndex + length, value, startIndex, this.length - startIndex - length);
			Array.Clear(value, this.length - length, length);
			this.length -= length;
			return this;
		}

		public StringBuffer Replace(char oldChar, char newChar)
		{
			for (int i = 0; i < length; i++)
			{
				if (value[i] == oldChar)
				{
					value[i] = newChar;
				}
			}
			return this;
		}

		public StringBuffer Replace(string oldValue, string newValue)
		{
			if (oldValue == null)
			{
				throw new ArgumentNullException("oldValue");
			}
			if (oldValue.Length == 0)
			{
				throw new ArgumentException("oldValue cannot be empty");
			}
			if (newValue == null)
			{
				newValue = string.Empty;
			}
			int num = oldValue.Length;
			int num2 = newValue.Length;
			for (int num3 = IndexOf(oldValue); num3 >= 0; num3 = IndexOf(oldValue, num3 + num2))
			{
				Remove(num3, num);
				Insert(num3, newValue);
			}
			return this;
		}

		public int IndexOf(char c)
		{
			return IndexOf(c, 0, length);
		}

		public int IndexOf(char c, int startIndex)
		{
			return IndexOf(c, startIndex, length - startIndex);
		}

		public int IndexOf(char c, int startIndex, int count)
		{
			if (startIndex < 0 || startIndex > length)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (count < 0 || startIndex + count > length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			return Array.IndexOf(value, c, startIndex, count);
		}

		public int IndexOf(string str)
		{
			return IndexOf(str, 0, length);
		}

		public int IndexOf(string str, int startIndex)
		{
			return IndexOf(str, startIndex, length - startIndex);
		}

		public int IndexOf(string str, int startIndex, int count)
		{
			if (str == null)
			{
				throw new ArgumentNullException("str");
			}
			if (startIndex < 0 || startIndex > length)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (count < 0 || startIndex + count > length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			return value.ToString(startIndex, count).IndexOf(str);
		}

		public string Substring(int startIndex)
		{
			return Substring(startIndex, length - startIndex);
		}

		public string Substring(int startIndex, int length)
		{
			if (startIndex < 0 || startIndex > this.length)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || startIndex + length > this.length)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			return new string(value, startIndex, length);
		}

		public bool Contains(string text)
		{
			return IndexOf(text) >= 0;
		}

		public StringBuffer ReplaceFull(string oldValue, string newValue)
		{
			if (oldValue == null)
			{
				throw new ArgumentNullException("oldValue");
			}
			if (oldValue.Length == 0)
			{
				throw new ArgumentException("oldValue cannot be empty");
			}
			if (newValue == null)
			{
				newValue = string.Empty;
			}
			int num = oldValue.Length;
			int num2 = newValue.Length;
			for (int num3 = IndexOfWord(oldValue); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2))
			{
				Remove(num3, num);
				Insert(num3, newValue);
			}
			return this;
		}

		private int IndexOfWord(string str)
		{
			return IndexOfWord(str, 0, length);
		}

		private int IndexOfWord(string str, int startIndex)
		{
			return IndexOfWord(str, startIndex, length - startIndex);
		}

		public int IndexOfWord(string str, int startIndex, int count)
		{
			if (str == null)
			{
				throw new ArgumentNullException("str");
			}
			if (startIndex < 0 || startIndex > length)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (count < 0 || startIndex + count > length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			int num = str.Length;
			int[] array = new int[num];
			int num2 = 0;
			computeLPSArray(str, num, array);
			int num3 = startIndex;
			while (num3 < startIndex + count)
			{
				if (str[num2] == value[num3])
				{
					num2++;
					num3++;
				}
				if (num2 == num)
				{
					if ((num3 - num2 == 0 || !char.IsLetter(value[num3 - num2 - 1])) && (num3 == length || !char.IsLetter(value[num3])))
					{
						return num3 - num2;
					}
					num2 = array[num2 - 1];
				}
				else if (num3 < startIndex + count && str[num2] != value[num3])
				{
					if (num2 != 0)
					{
						num2 = array[num2 - 1];
					}
					else
					{
						num3++;
					}
				}
			}
			return -1;
		}

		private void computeLPSArray(string str, int M, int[] lps)
		{
			int num = 0;
			int num2 = 1;
			lps[0] = 0;
			while (num2 < M)
			{
				if (str[num2] == str[num])
				{
					num = (lps[num2] = num + 1);
					num2++;
				}
				else if (num != 0)
				{
					num = lps[num - 1];
				}
				else
				{
					lps[num2] = num;
					num2++;
				}
			}
		}

		private static bool IsWordChar(char c)
		{
			if (!char.IsLetter(c))
			{
				return c == '_';
			}
			return true;
		}

		public StringBuffer Clear()
		{
			Length = 0;
			return this;
		}

		public override string ToString()
		{
			return new string(value, 0, length);
		}
	}
	internal static class TextHelper
	{
		public static string Encode(string text)
		{
			return EscapeNewlines(text);
		}

		public static string[] ReadTranslationLineAndDecode(string str)
		{
			if (string.IsNullOrEmpty(str))
			{
				return null;
			}
			string[] array = new string[2];
			int num = 0;
			bool flag = false;
			int length = str.Length;
			StringBuilder stringBuilder = new StringBuilder((int)((double)length / 1.3));
			for (int i = 0; i < length; i++)
			{
				char c = str[i];
				if (flag)
				{
					char c2 = c;
					if (c2 <= '\\')
					{
						if (c2 != '=' && c2 != '\\')
						{
							stringBuilder.Append('\\');
							stringBuilder.Append(c);
							flag = false;
							continue;
						}
						stringBuilder.Append(c);
					}
					else
					{
						switch (c2)
						{
						default:
							stringBuilder.Append('\\');
							stringBuilder.Append(c);
							flag = false;
							continue;
						case 'u':
						{
							if (i + 4 >= length)
							{
								throw new Exception("Found invalid unicode in line: " + str);
							}
							if (i + 1 >= length || i + 2 >= length || i + 3 >= length || i + 4 >= length)
							{
								throw new Exception("Invalid unicode escape sequence at position " + i + " in line: " + str);
							}
							int num2 = int.Parse(new string(new char[4]
							{
								str[i + 1],
								str[i + 2],
								str[i + 3],
								str[i + 4]
							}), NumberStyles.HexNumber);
							stringBuilder.Append((char)num2);
							i += 4;
							break;
						}
						case 'r':
							stringBuilder.Append('\r');
							break;
						case 'n':
							stringBuilder.Append('\n');
							break;
						}
					}
					flag = false;
					continue;
				}
				switch (c)
				{
				case '\\':
					flag = true;
					break;
				case '=':
					if (num > 1)
					{
						return null;
					}
					array[num++] = stringBuilder.ToString();
					stringBuilder.Length = 0;
					break;
				case '%':
					if (i + 2 < length && i + 1 < length && i + 2 < length && str[i + 1] == '3' && str[i + 2] == 'D')
					{
						stringBuilder.Append('=');
						i += 2;
					}
					else
					{
						stringBuilder.Append(c);
					}
					break;
				case '/':
				{
					int num3 = i + 1;
					if (num3 < length && num3 < length && str[num3] == '/')
					{
						array[num++] = stringBuilder.ToString();
						if (num == 2)
						{
							return array;
						}
						return null;
					}
					stringBuilder.Append(c);
					break;
				}
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			if (num != 1)
			{
				return null;
			}
			array[num++] = stringBuilder.ToString();
			return array;
		}

		internal static string EscapeNewlines(string str)
		{
			if (str == null || str.Length == 0)
			{
				return "";
			}
			int length = str.Length;
			StringBuilder stringBuilder = new StringBuilder(length + 4);
			int num = 0;
			while (num < length)
			{
				char c = str[num];
				char c2 = c;
				if (c2 <= '\r')
				{
					switch (c2)
					{
					default:
						stringBuilder.Append(c);
						num++;
						continue;
					case '\r':
						stringBuilder.Append("\\r");
						break;
					case '\n':
						stringBuilder.Append("\\n");
						break;
					}
				}
				else
				{
					switch (c2)
					{
					default:
						stringBuilder.Append(c);
						num++;
						continue;
					case '\\':
						stringBuilder.Append('\\');
						stringBuilder.Append(c);
						break;
					case '=':
						stringBuilder.Append('\\');
						stringBuilder.Append(c);
						break;
					case '/':
					{
						int num2 = num + 1;
						if (num2 < length && str[num2] == '/')
						{
							stringBuilder.Append('\\');
							stringBuilder.Append(c);
							stringBuilder.Append('\\');
							stringBuilder.Append(c);
							num++;
						}
						else
						{
							stringBuilder.Append(c);
						}
						break;
					}
					}
				}
				num++;
			}
			return stringBuilder.ToString();
		}
	}
	internal class TextTranslate
	{
		private static readonly Dictionary<string, DateTime> _debugOutputCache = new Dictionary<string, DateTime>();

		private static readonly TimeSpan _debugOutputInterval = TimeSpan.FromSeconds(10.0);

		private static readonly TimeSpan _cacheCleanupInterval = TimeSpan.FromMinutes(5.0);

		private static DateTime _lastCleanupTime = DateTime.Now;

		public static TextTranslate Instance = new TextTranslate();

		public static long ChangeTime = 0L;

		public static bool ShouldOutputDebug(string text)
		{
			if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value)
			{
				return true;
			}
			DateTime now = DateTime.Now;
			if (now - _lastCleanupTime > _cacheCleanupInterval)
			{
				CleanupDebugCache();
				_lastCleanupTime = now;
			}
			if (_debugOutputCache.TryGetValue(text, out var value) && now - value < _debugOutputInterval)
			{
				return false;
			}
			_debugOutputCache[text] = now;
			return true;
		}

		private static void CleanupDebugCache()
		{
			if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value)
			{
				_debugOutputCache.Clear();
				return;
			}
			DateTime now = DateTime.Now;
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, DateTime> item in _debugOutputCache)
			{
				if (now - item.Value > _cacheCleanupInterval)
				{
					list.Add(item.Key);
				}
			}
			foreach (string item2 in list)
			{
				_debugOutputCache.Remove(item2);
			}
			if (TranslatePlugin.showOtherDebug.Value && list.Count > 0)
			{
				TranslatePlugin.logger.LogInfo((object)$"[Debug] Cleaned up {list.Count} old debug cache entries");
			}
		}

		internal void Hook_TextChanged(object ui)
		{
			if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value)
			{
				try
				{
					Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator");
					if (type != null)
					{
						FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public);
						if (field != null && field.GetValue(null) is HashSet<object> hashSet && hashSet.Contains(ui))
						{
							return;
						}
					}
				}
				catch
				{
				}
			}
			TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo();
			bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo);
			if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value)
			{
				string text = ui.GetText(orCreateTextTranslationInfo);
				string text2 = TranslateOrQueue(ui, text, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState);
				if (!string.IsNullOrEmpty(text2) && !text2.Equals(text) && IsUIObjectValid(ui))
				{
					SetText(ui, text2, isTranslated: true, text, orCreateTextTranslationInfo);
				}
			}
		}

		internal void Hook_TextChanged(object ui, ref string value)
		{
			if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value)
			{
				try
				{
					Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator");
					if (type != null)
					{
						FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public);
						if (field != null && field.GetValue(null) is HashSet<object> hashSet && hashSet.Contains(ui))
						{
							return;
						}
					}
				}
				catch
				{
				}
			}
			TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo();
			bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo);
			if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value)
			{
				string text = TranslateOrQueue(ui, value, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState);
				if (!string.IsNullOrEmpty(text) && !text.Equals(value) && IsUIObjectValid(ui))
				{
					value = text;
				}
			}
		}

		public string TranslateOrQueue(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState)
		{
			if (info != null && (info.IsCurrentlySettingText || info.MustIgnore))
			{
				return null;
			}
			text = text ?? ui.GetText(info);
			if (Utility.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			if (info != null && info.IsTranslated)
			{
				if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime)
				{
					info.Reset(text);
				}
				else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text))
				{
					return info.TranslatedText;
				}
			}
			string cachedTranslation = AsyncTranslationManager.Instance.GetCachedTranslation(text, config);
			if (cachedTranslation != null)
			{
				if (TranslatePlugin.showOtherDebug.Value && ShouldOutputDebug("cached:" + text))
				{
					TranslatePlugin.logger.LogInfo((object)("[Debug] Cached translation found for: '" + text + "' -> '" + cachedTranslation + "'"));
				}
				if (info != null)
				{
					info.OriginalText = text;
					info.SetTranslatedText(cachedTranslation);
				}
				if (!IsUIObjectValid(ui))
				{
					return null;
				}
				try
				{
					if (info != null)
					{
						info.IsCurrentlySettingText = true;
					}
					ui.SetText(cachedTranslation, info);
				}
				catch (NullReferenceException)
				{
				}
				catch (IndexOutOfRangeException ex2)
				{
					TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in cached translation: " + ex2.Message));
				}
				catch (Exception ex3)
				{
					TranslatePlugin.logger.LogError((object)("Exception in cached translation: " + ex3.Message));
				}
				finally
				{
					if (info != null)
					{
						info.IsCurrentlySettingText = false;
					}
				}
				return cachedTranslation;
			}
			if (normalText == null || normalText.IsTranslatable(text, isToken: false))
			{
				if (text.Length <= TranslatePlugin.syncTranslationThreshold.Value)
				{
					string text2 = TranslateImmediate(ui, text, info, normalText, config, ignoreComponentState);
					if (text2 != null)
					{
						AsyncTranslationManager.Instance.GetCachedTranslation(text, config);
						return text2;
					}
				}
				else
				{
					if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("queued:" + text))
					{
						TranslatePlugin.logger.LogInfo((object)("[Debug] Queued available text: '" + text + "'"));
					}
					AsyncTranslationManager.Instance.QueueTranslation(ui, text, info, normalText, config, ignoreComponentState);
					if (info != null && info.IsTranslated && info.TranslatedText != null)
					{
						return info.TranslatedText;
					}
				}
			}
			return null;
		}

		public string TranslateImmediate(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState)
		{
			if (info != null && (info.IsCurrentlySettingText || info.MustIgnore))
			{
				return null;
			}
			text = text ?? ui.GetText(info);
			if (Utility.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			if (info != null && info.IsTranslated)
			{
				if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime)
				{
					info.Reset(text);
				}
				else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text))
				{
					return info.TranslatedText;
				}
			}
			string text2 = null;
			if ((normalText == null || normalText.IsTranslatable(text, isToken: false)) && (ignoreComponentState || ui.IsComponentActive()))
			{
				if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value)
				{
					if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("available:" + text))
					{
						TranslatePlugin.logger.LogInfo((object)("[Debug] Found available text: '" + text + "'"));
					}
					text2 = normalText.TryTranslate(text);
				}
				if (text2 != null)
				{
					text2 = TranslateConfig.replaceByMap(text2, config);
					SetTranslatedText(ui, text2, text, info);
				}
			}
			return text2;
		}

		internal void SetTranslatedText(object ui, string translatedText, string originalText, TextTranslationInfo info)
		{
			if (info != null)
			{
				info.OriginalText = originalText;
				info.SetTranslatedText(translatedText);
			}
			if (!IsUIObjectValid(ui))
			{
				return;
			}
			try
			{
				if (info != null)
				{
					info.IsCurrentlySettingText = true;
				}
				ui.SetText(translatedText, info);
			}
			catch (NullReferenceException)
			{
			}
			catch (IndexOutOfRangeException ex2)
			{
				TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in SetTranslatedText: " + ex2.Message));
			}
			catch (Exception ex3)
			{
				TranslatePlugin.logger.LogError((object)("Exception in SetTranslatedText: " + ex3.Message));
			}
			finally
			{
				if (info != null)
				{
					info.IsCurrentlySettingText = false;
				}
			}
		}

		private void SetText(object ui, string text, bool isTranslated, string originalText, TextTranslationInfo info)
		{
			if ((info != null && info.IsCurrentlySettingText) || !IsUIObjectValid(ui))
			{
				return;
			}
			try
			{
				if (info != null)
				{
					info.IsCurrentlySettingText = true;
				}
				ui.SetText(text, info);
			}
			catch (NullReferenceException)
			{
			}
			catch (IndexOutOfRangeException ex2)
			{
				TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in SetText: " + ex2.Message));
			}
			catch (Exception ex3)
			{
				TranslatePlugin.logger.LogError((object)("Exception in SetText: " + ex3.Message));
			}
			finally
			{
				if (info != null)
				{
					info.IsCurrentlySettingText = false;
				}
			}
		}

		private bool IsUIObjectValid(object ui)
		{
			if (ui == null)
			{
				return false;
			}
			try
			{
				Component val = (Component)((ui is Component) ? ui : null);
				if (val != null && Object.op_Implicit((Object)(object)val))
				{
					GameObject gameObject = val.gameObject;
					if (Object.op_Implicit((Object)(object)gameObject))
					{
						Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null);
						if (val2 != null)
						{
							return gameObject.activeInHierarchy && val2.enabled;
						}
						return gameObject.activeInHierarchy;
					}
				}
				return ui != null;
			}
			catch
			{
				return false;
			}
		}

		public bool DiscoverComponent(object ui, TextTranslationInfo info)
		{
			if (info != null && TranslatePlugin.changeFont.Value)
			{
				try
				{
					bool flag = ui.IsComponentActive();
					if (TranslatePlugin.fallbackFontTextMeshPro.Value != null && flag)
					{
						info.ChangeFont(ui);
						return true;
					}
					return flag;
				}
				catch (Exception ex)
				{
					ManualLogSource logger = TranslatePlugin.logger;
					string text = "An error occurred while processing the UI.";
					string newLine = Environment.NewLine;
					logger.LogWarning((object)(text + newLine + ex));
				}
				return false;
			}
			return true;
		}
	}
	public class TextureTranslate
	{
		public static TextureTranslate Instance = new TextureTranslate();

		public static bool ImageHooksEnabled = true;

		internal void Hook_ImageChangedOnComponent(object source, ref Texture2D texture, bool isPrefixHooked, bool onEnable = false)
		{
			if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && source.IsKnownImageType())
			{
				Sprite sprite = null;
				HandleImage(source, ref sprite, ref texture, isPrefixHooked);
			}
		}

		internal void Hook_ImageChangedOnComponent(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked, bool onEnable)
		{
			if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && source.IsKnownImageType())
			{
				HandleImage(source, ref sprite, ref texture, isPrefixHooked);
			}
		}

		internal void Hook_ImageChanged(ref Texture2D texture, bool isPrefixHooked)
		{
			if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && !((Object)(object)texture == (Object)null))
			{
				Sprite sprite = null;
				HandleImage(null, ref sprite, ref texture, isPrefixHooked);
			}
		}

		public void HandleImage(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked)
		{
			try
			{
				if (ShouldProcessTexture(source, texture))
				{
					TranslateTexture(source, ref sprite, ref texture, isPrefixHooked);
				}
			}
			catch (Exception ex)
			{
				XuaLogger.AutoTranslator.Error(ex, "An error occurred while translating texture.");
			}
		}

		private void TranslateTexture(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked)
		{
			try
			{
				ImageHooksEnabled = false;
				Texture2D val = texture;
				texture = texture ?? source.GetTexture();
				if ((Object)(object)texture == (Object)null)
				{
					return;
				}
				TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo();
				string key = orCreateTextureTranslationInfo.GetKey();
				if (string.IsNullOrEmpty(key))
				{
					return;
				}
				if (TranslateConfig.cache != null)
				{
					TranslateConfig.cache.UpdateTextureStatistics(key);
				}
				if (TranslateConfig.cache.TryGetTranslatedImage(key, out byte[] data, out TranslatedImage image))
				{
					bool flag = texture.IsCompatible(image.ImageFormat);
					if (!orCreateTextureTranslationInfo.IsTranslated)
					{
						try
						{
							if (flag)
							{
								texture.LoadImageEx(data, image.ImageFormat, null);
							}
							else
							{
								orCreateTextureTranslationInfo.CreateTranslatedTexture(data, image.ImageFormat);
							}
						}
						finally
						{
							orCreateTextureTranslationInfo.IsTranslated = true;
						}
					}
					if (source != null && !orCreateTextureTranslationInfo.IsTranslated)
					{
						if (!flag)
						{
							Sprite val2 = source.SetTexture(orCreateTextureTranslationInfo.Translated, sprite, isPrefixHooked);
							if ((Object)(object)val2 != (Object)null)
							{
								orCreateTextureTranslationInfo.TranslatedSprite = val2;
								if (isPrefixHooked && (Object)(object)sprite != (Object)null)
								{
									sprite = val2;
								}
							}
						}
						if (!isPrefixHooked)
						{
							source.SetAllDirtyEx();
						}
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					texture = null;
				}
				else if (orCreateTextureTranslationInfo.UsingReplacedTexture)
				{
					if (orCreateTextureTranslationInfo.IsTranslated)
					{
						Texture2D translated = orCreateTextureTranslationInfo.Translated;
						if ((Object)(object)translated != (Object)null)
						{
							texture = translated;
						}
					}
					else
					{
						Texture2D target = orCreateTextureTranslationInfo.Original.Target;
						if ((Object)(object)target != (Object)null)
						{
							texture = target;
						}
					}
				}
				else
				{
					texture = val;
				}
			}
			catch (FileNotFoundException ex)
			{
				XuaLogger.AutoTranslator.Warn("Texture file not found: " + ex.FileName);
			}
			catch (FormatException ex2)
			{
				XuaLogger.AutoTranslator.Error((Exception)ex2, "Invalid image format.");
			}
			catch (Exception ex3)
			{
				XuaLogger.AutoTranslator.Error(ex3, "An unexpected error occurred while translating texture.");
			}
			finally
			{
				ImageHooksEnabled = true;
			}
		}

		private bool ShouldProcessTexture(object source, Texture2D texture)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected I4, but got Unknown
			if ((Object)(object)texture == (Object)null && source == null)
			{
				return false;
			}
			if ((Object)(object)texture != (Object)null)
			{
				TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo();
				if (orCreateTextureTranslationInfo.IsTranslated && (Object)(object)orCreateTextureTranslationInfo.Translated != (Object)null)
				{
					return false;
				}
				int num = (int)texture.format;
				if (num == 1 || num == 9 || num == 63)
				{
					return false;
				}
			}
			return true;
		}

		private bool IsTextureFormatCompatible(Texture2D texture, TranslateExtensions.ImageFormat format)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			TextureFormat format2 = texture.format;
			switch (format)
			{
			case TranslateExtensions.ImageFormat.TGA:
				if ((int)format2 != 5 && (int)format2 != 4)
				{
					return (int)format2 == 3;
				}
				return true;
			default:
				return false;
			case TranslateExtensions.ImageFormat.PNG:
				return true;
			}
		}
	}
	internal static class TranslationScopeHelper
	{
		internal static class TranslationScopes
		{
			public const int None = -1;
		}

		public static bool EnableTranslationScoping = true;

		public static int GetScope(object ui)
		{
			if (EnableTranslationScoping)
			{
				try
				{
					Component val = (Component)((ui is Component) ? ui : null);
					if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val))
					{
						return GetScopeFromComponent(val);
					}
					if (ui is GUIContent)
					{
						return -1;
					}
					return GetActiveSceneId();
				}
				catch (MissingMemberException ex)
				{
					XuaLogger.AutoTranslator.Error((Exception)ex, "A 'missing member' error occurred while retriving translation scope. Disabling translation scopes.");
					EnableTranslationScoping = false;
				}
				return -1;
			}
			return -1;
		}

		private static int GetScopeFromComponent(Component component)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Scene scene = component.gameObject.scene;
			return ((Scene)(ref scene)).buildIndex;
		}

		public static int GetActiveSceneId()
		{
			if (UnityFeatures.SupportsSceneManager)
			{
				return GetActiveSceneIdBySceneManager();
			}
			return GetActiveSceneIdByApplication();
		}

		private static int GetActiveSceneIdBySceneManager()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).buildIndex;
		}

		public static void RegisterSceneLoadCallback(Action<int> sceneLoaded)
		{
			Action<int> sceneLoaded2 = sceneLoaded;
			SceneManager_Methods.add_sceneLoaded(delegate(Scene scene, LoadSceneMode mode)
			{
				sceneLoaded2(((Scene)(ref scene)).buildIndex);
			});
			SceneManagerLoader.EnableSceneLoadScanInternal(sceneLoaded2);
		}

		private static int GetActiveSceneIdByApplication()
		{
			return Application.loadedLevel;
		}
	}
}
namespace GameTranslator.Patches.Utils.Textures
{
	internal interface ITextureLoader
	{
		void Load(Texture2D texture, byte[] data);

		bool Verify();
	}
	internal class LoadImageImageLoader : ITextureLoader
	{
		public void Load(Texture2D texture, byte[] data)
		{
			if (ImageConversion_Methods.LoadImage != null)
			{
				ImageConversion_Methods.LoadImage(texture, data, arg3: false);
			}
			else if (Texture2D_Methods.LoadImage != null)
			{
				Texture2D_Methods.LoadImage(texture, data);
			}
		}

		public bool Verify()
		{
			if (Texture2D_Methods.LoadImage == null)
			{
				return ImageConversion_Methods.LoadImage != null;
			}
			return true;
		}
	}
	internal static class TextureLoader
	{
		private static readonly Dictionary<TranslateExtensions.ImageFormat, ITextureLoader> Loaders;

		static TextureLoader()
		{
			Loaders = new Dictionary<TranslateExtensions.ImageFormat, ITextureLoader>();
			Register(TranslateExtensions.ImageFormat.PNG, new LoadImageImageLoader());
			Register(TranslateExtensions.ImageFormat.TGA, new TgaImageLoader());
		}

		public static bool Register(TranslateExtensions.ImageFormat format, ITextureLoader loader)
		{
			try
			{
				if (loader.Verify())
				{
					Loaders[format] = loader;
					return true;
				}
			}
			catch (Exception ex)
			{
				XuaLogger.AutoTranslator.Warn(ex, "An image loader could not be registered.");
			}
			return false;
		}

		public static void Load(Texture2D texture, byte[] data, TranslateExtensions.ImageFormat imageFormat)
		{
			if (Loaders.TryGetValue(imageFormat, out ITextureLoader value))
			{
				value.Load(texture, data);
			}
		}
	}
	internal class TgaImageLoader : ITextureLoader
	{
		public void Load(Texture2D texture, byte[] data)
		{
			//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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)texture == (Object)null && data == null)
			{
				return;
			}
			TextureFormat format = texture.format;
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			binaryReader.BaseStream.Seek(12L, SeekOrigin.Begin);
			short num = binaryReader.ReadInt16();
			short num2 = binaryReader.ReadInt16();
			int num3 = binaryReader.ReadByte();
			binaryReader.BaseStream.Seek(1L, SeekOrigin.Current);
			Color32[] array = (Color32[])(object)new Color32[num * num2];
			if ((int)format == 3)
			{
				if (num3 == 32)
				{
					for (int i = 0; i < num * num2; i++)
					{
						byte b = binaryReader.ReadByte();
						byte b2 = binaryReader.ReadByte();
						byte b3 = binaryReader.ReadByte();
						binaryReader.ReadByte();
						array[i] = new Color32(b3, b2, b, byte.MaxValue);
					}
				}
				else
				{
					for (int j = 0; j < num * num2; j++)
					{
						byte b4 = binaryReader.ReadByte();
						byte b5 = binaryReader.ReadByte();
						byte b6 = binaryReader.ReadByte();
						array[j] = new Color32(b6, b5, b4, byte.MaxValue);
					}
				}
			}
			else if (num3 == 32)
			{
				for (int k = 0; k < num * num2; k++)
				{
					byte b7 = binaryReader.ReadByte();
					byte b8 = binaryReader.ReadByte();
					byte b9 = binaryReader.ReadByte();
					byte b10 = binaryReader.ReadByte();
					array[k] = new Color32(b9, b8, b7, b10);
				}
			}
			else
			{
				for (int l = 0; l < num * num2; l++)
				{
					byte b11 = binaryReader.ReadByte();
					byte b12 = binaryReader.ReadByte();
					byte b13 = binaryReader.ReadByte();
					array[l] = new Color32(b13, b12, b11, byte.MaxValue);
				}
			}
			texture.SetPixels32(array);
			texture.Apply();
		}

		public bool Verify()
		{
			Load(null, null);
			return true;
		}
	}
}
namespace GameTranslator.Patches.Translatons
{
	internal class AsyncTranslationManager
	{
		private class TranslationTask
		{
			public object UI { get; set; }

			public string OriginalText { get; set; }

			public TextTranslationInfo Info { get; set; }