Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of GameTranslator v2.2.6
BepInEx\core\XUnity.Common.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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.InputSystem; using UnityEngine.InputSystem.Controls; 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: AssemblyCopyright("Copyright © 2018 / MIT License")] [assembly: AssemblyDescription("Common dependencies shared between XUnity Auto Translator and Resource Redirector.")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4+7f1f3b9e8fc7d93a97734773804ba9c8fdf57714")] [assembly: AssemblyProduct("XUnity.Common")] [assembly: AssemblyTitle("XUnity.Common")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.4.0")] [module: UnverifiableCode] namespace XUnity.Common { internal static class GeneratedInfo { public const string PROJECT_VERSION = "1.0.4"; } } 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 { [CompilerGenerated] private sealed class <IterateAllPairs>d__11 : IEnumerable<KeyValuePair<object, object>>, IEnumerable, IEnumerator<KeyValuePair<object, object>>, IDisposable, IEnumerator { private int <>1__state; private KeyValuePair<object, object> <>2__current; private int <>l__initialThreadId; private IEnumerator<KeyValuePair<object, object>> <>7__wrap1; private KeyValuePair<object, object> <kvp>5__3; private Dictionary<Type, object>.Enumerator <>7__wrap3; KeyValuePair<object, object> IEnumerator<KeyValuePair<object, object>>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <IterateAllPairs>d__11(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Thread.CurrentThread.ManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if ((uint)(num - -4) <= 1u || (uint)(num - 1) <= 1u) { try { if (num == -4 || num == 1) { try { } finally { <>m__Finally2(); } } } finally { <>m__Finally1(); } } <>7__wrap1 = null; <kvp>5__3 = default(KeyValuePair<object, object>); <>7__wrap3 = default(Dictionary<Type, object>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = WeakDynamicFields.GetEnumerator(); <>1__state = -3; goto IL_0107; case 1: <>1__state = -4; goto IL_00bb; case 2: { <>1__state = -3; goto IL_00fb; } IL_0107: if (<>7__wrap1.MoveNext()) { <kvp>5__3 = <>7__wrap1.Current; if (<kvp>5__3.Value is Dictionary<Type, object> dictionary) { <>7__wrap3 = dictionary.GetEnumerator(); <>1__state = -4; goto IL_00bb; } <>2__current = <kvp>5__3; <>1__state = 2; return true; } <>m__Finally1(); <>7__wrap1 = null; return false; IL_00bb: if (<>7__wrap3.MoveNext()) { KeyValuePair<Type, object> current = <>7__wrap3.Current; <>2__current = new KeyValuePair<object, object>(<kvp>5__3.Key, current.Value); <>1__state = 1; return true; } <>m__Finally2(); <>7__wrap3 = default(Dictionary<Type, object>.Enumerator); goto IL_00fb; IL_00fb: <kvp>5__3 = default(KeyValuePair<object, object>); goto IL_0107; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } private void <>m__Finally2() { <>1__state = -3; ((IDisposable)<>7__wrap3).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Thread.CurrentThread.ManagedThreadId) { <>1__state = 0; return this; } return new <IterateAllPairs>d__11(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<KeyValuePair<object, object>>)this).GetEnumerator(); } } 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() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <IterateAllPairs>d__11(-2); } } 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")).GetValueOrDefault()) ? 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")).GetValueOrDefault()) ? 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; } internal class UnityInput { private static IInputSystem _current; public static IInputSystem Current { get { if (_current == null) { try { try { _current = new LegacyInputSystem(); XuaLogger.AutoTranslator.Debug("[UnityInput] Using LegacyInputSystem"); } catch { _current = new NewInputSystem(); XuaLogger.AutoTranslator.Debug("[UnityInput] Using NewInputSystem"); } } catch (Exception ex) { _current = new NullInputSystem(); XuaLogger.AutoTranslator.Warn("[UnityInput] Failed to detect available input systems - " + ex); } } return _current; } } public bool LegacyInputSystemAvailable => Current is LegacyInputSystem; } public interface IInputSystem { Vector3 mousePosition { get; } Vector2 mouseScrollDelta { get; } bool mousePresent { get; } bool anyKey { get; } bool anyKeyDown { get; } IEnumerable<KeyCode> SupportedKeyCodes { get; } bool GetKey(string name); bool GetKey(KeyCode key); bool GetKeyDown(string name); bool GetKeyDown(KeyCode key); bool GetKeyUp(string name); bool GetKeyUp(KeyCode key); bool GetMouseButton(int button); bool GetMouseButtonDown(int button); bool GetMouseButtonUp(int button); void ResetInputAxes(); } internal class NullInputSystem : IInputSystem { public Vector3 mousePosition => Vector3.zero; public Vector2 mouseScrollDelta => Vector2.zero; public bool mousePresent => false; public bool anyKey => false; public bool anyKeyDown => false; public IEnumerable<KeyCode> SupportedKeyCodes { get; } = Enumerable.Empty<KeyCode>(); public bool GetKey(string name) { return false; } public bool GetKey(KeyCode key) { return false; } public bool GetKeyDown(string name) { return false; } public bool GetKeyDown(KeyCode key) { return false; } public bool GetKeyUp(string name) { return false; } public bool GetKeyUp(KeyCode key) { return false; } public bool GetMouseButton(int button) { return false; } public bool GetMouseButtonDown(int button) { return false; } public bool GetMouseButtonUp(int button) { return false; } public void ResetInputAxes() { } } internal class NewInputSystem : IInputSystem { public Vector3 mousePosition => Vector2.op_Implicit(((InputControl<Vector2>)(object)((Pointer)Mouse.current).position).ReadValue()); public Vector2 mouseScrollDelta => ((InputControl<Vector2>)(object)Mouse.current.scroll).ReadValue(); public bool mousePresent => ((InputDevice)Mouse.current).enabled; public bool anyKey { get { if (((ButtonControl)Keyboard.current.anyKey).isPressed) { return true; } Mouse current = Mouse.current; if (!current.leftButton.isPressed && !current.rightButton.isPressed && !current.forwardButton.isPressed && !current.backButton.isPressed) { return current.middleButton.isPressed; } return true; } } public bool anyKeyDown { get { if (((ButtonControl)Keyboard.current.anyKey).wasPressedThisFrame) { return true; } Mouse current = Mouse.current; if (!current.leftButton.wasPressedThisFrame && !current.rightButton.wasPressedThisFrame && !current.forwardButton.wasPressedThisFrame && !current.backButton.wasPressedThisFrame) { return current.middleButton.wasPressedThisFrame; } return true; } } public IEnumerable<KeyCode> SupportedKeyCodes { get; } = (from KeyCode x in Enum.GetValues(typeof(KeyCode)) where GetControl(x, silent: true) != null select x).ToList(); [MethodImpl(MethodImplOptions.NoInlining)] public NewInputSystem() { GetKeyDown((KeyCode)97); } public bool GetKey(string name) { ButtonControl control = GetControl(name); if (control == null) { return false; } return control.isPressed; } public bool GetKey(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ButtonControl control = GetControl(key); if (control == null) { return false; } return control.isPressed; } public bool GetKeyDown(string name) { ButtonControl control = GetControl(name); if (control == null) { return false; } return control.wasPressedThisFrame; } public bool GetKeyDown(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ButtonControl control = GetControl(key); if (control == null) { return false; } return control.wasPressedThisFrame; } public bool GetKeyUp(string name) { ButtonControl control = GetControl(name); if (control == null) { return false; } return control.wasReleasedThisFrame; } public bool GetKeyUp(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ButtonControl control = GetControl(key); if (control == null) { return false; } return control.wasReleasedThisFrame; } public bool GetMouseButton(int button) { ButtonControl control = GetControl((KeyCode)(323 + button)); if (control == null) { return false; } return control.isPressed; } public bool GetMouseButtonDown(int button) { ButtonControl control = GetControl((KeyCode)(323 + button)); if (control == null) { return false; } return control.wasPressedThisFrame; } public bool GetMouseButtonUp(int button) { ButtonControl control = GetControl((KeyCode)(323 + button)); if (control == null) { return false; } return control.wasReleasedThisFrame; } public void ResetInputAxes() { } private static ButtonControl GetControl(string name) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) return GetControl((KeyCode)Enum.Parse(typeof(KeyCode), name, ignoreCase: true)); } private static ButtonControl GetControl(KeyCode key, bool silent = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Expected I4, but got Unknown //IL_0cf0: Unknown result type (might be due to invalid IL or missing references) switch ((int)key) { case 8: return (ButtonControl)(object)Keyboard.current.backspaceKey; case 127: return (ButtonControl)(object)Keyboard.current.deleteKey; case 9: return (ButtonControl)(object)Keyboard.current.tabKey; case 13: return (ButtonControl)(object)Keyboard.current.enterKey; case 19: return (ButtonControl)(object)Keyboard.current.pauseKey; case 27: return (ButtonControl)(object)Keyboard.current.escapeKey; case 32: return (ButtonControl)(object)Keyboard.current.spaceKey; case 256: return (ButtonControl)(object)Keyboard.current.numpad0Key; case 257: return (ButtonControl)(object)Keyboard.current.numpad1Key; case 258: return (ButtonControl)(object)Keyboard.current.numpad2Key; case 259: return (ButtonControl)(object)Keyboard.current.numpad3Key; case 260: return (ButtonControl)(object)Keyboard.current.numpad4Key; case 261: return (ButtonControl)(object)Keyboard.current.numpad5Key; case 262: return (ButtonControl)(object)Keyboard.current.numpad6Key; case 263: return (ButtonControl)(object)Keyboard.current.numpad7Key; case 264: return (ButtonControl)(object)Keyboard.current.numpad8Key; case 265: return (ButtonControl)(object)Keyboard.current.numpad9Key; case 266: return (ButtonControl)(object)Keyboard.current.numpadPeriodKey; case 267: return (ButtonControl)(object)Keyboard.current.numpadDivideKey; case 268: return (ButtonControl)(object)Keyboard.current.numpadMultiplyKey; case 269: return (ButtonControl)(object)Keyboard.current.numpadMinusKey; case 270: return (ButtonControl)(object)Keyboard.current.numpadPlusKey; case 271: return (ButtonControl)(object)Keyboard.current.numpadEnterKey; case 272: return (ButtonControl)(object)Keyboard.current.numpadEqualsKey; case 273: return (ButtonControl)(object)Keyboard.current.upArrowKey; case 274: return (ButtonControl)(object)Keyboard.current.downArrowKey; case 275: return (ButtonControl)(object)Keyboard.current.rightArrowKey; case 276: return (ButtonControl)(object)Keyboard.current.leftArrowKey; case 277: return (ButtonControl)(object)Keyboard.current.insertKey; case 278: return (ButtonControl)(object)Keyboard.current.homeKey; case 279: return (ButtonControl)(object)Keyboard.current.endKey; case 280: return (ButtonControl)(object)Keyboard.current.pageUpKey; case 281: return (ButtonControl)(object)Keyboard.current.pageDownKey; case 282: return (ButtonControl)(object)Keyboard.current.f1Key; case 283: return (ButtonControl)(object)Keyboard.current.f2Key; case 284: return (ButtonControl)(object)Keyboard.current.f3Key; case 285: return (ButtonControl)(object)Keyboard.current.f4Key; case 286: return (ButtonControl)(object)Keyboard.current.f5Key; case 287: return (ButtonControl)(object)Keyboard.current.f6Key; case 288: return (ButtonControl)(object)Keyboard.current.f7Key; case 289: return (ButtonControl)(object)Keyboard.current.f8Key; case 290: return (ButtonControl)(object)Keyboard.current.f9Key; case 291: return (ButtonControl)(object)Keyboard.current.f10Key; case 292: return (ButtonControl)(object)Keyboard.current.f11Key; case 293: return (ButtonControl)(object)Keyboard.current.f12Key; case 48: return (ButtonControl)(object)Keyboard.current.digit0Key; case 49: return (ButtonControl)(object)Keyboard.current.digit1Key; case 50: return (ButtonControl)(object)Keyboard.current.digit2Key; case 51: return (ButtonControl)(object)Keyboard.current.digit3Key; case 52: return (ButtonControl)(object)Keyboard.current.digit4Key; case 53: return (ButtonControl)(object)Keyboard.current.digit5Key; case 54: return (ButtonControl)(object)Keyboard.current.digit6Key; case 55: return (ButtonControl)(object)Keyboard.current.digit7Key; case 56: return (ButtonControl)(object)Keyboard.current.digit8Key; case 57: return (ButtonControl)(object)Keyboard.current.digit9Key; case 39: return (ButtonControl)(object)Keyboard.current.quoteKey; case 43: return (ButtonControl)(object)Keyboard.current.numpadPlusKey; case 44: return (ButtonControl)(object)Keyboard.current.commaKey; case 45: return (ButtonControl)(object)Keyboard.current.minusKey; case 46: return (ButtonControl)(object)Keyboard.current.periodKey; case 47: return (ButtonControl)(object)Keyboard.current.slashKey; case 59: return (ButtonControl)(object)Keyboard.current.semicolonKey; case 61: return (ButtonControl)(object)Keyboard.current.equalsKey; case 91: return (ButtonControl)(object)Keyboard.current.leftBracketKey; case 92: return (ButtonControl)(object)Keyboard.current.backslashKey; case 93: return (ButtonControl)(object)Keyboard.current.rightBracketKey; case 96: return (ButtonControl)(object)Keyboard.current.backquoteKey; case 97: return (ButtonControl)(object)Keyboard.current.aKey; case 98: return (ButtonControl)(object)Keyboard.current.bKey; case 99: return (ButtonControl)(object)Keyboard.current.cKey; case 100: return (ButtonControl)(object)Keyboard.current.dKey; case 101: return (ButtonControl)(object)Keyboard.current.eKey; case 102: return (ButtonControl)(object)Keyboard.current.fKey; case 103: return (ButtonControl)(object)Keyboard.current.gKey; case 104: return (ButtonControl)(object)Keyboard.current.hKey; case 105: return (ButtonControl)(object)Keyboard.current.iKey; case 106: return (ButtonControl)(object)Keyboard.current.jKey; case 107: return (ButtonControl)(object)Keyboard.current.kKey; case 108: return (ButtonControl)(object)Keyboard.current.lKey; case 109: return (ButtonControl)(object)Keyboard.current.mKey; case 110: return (ButtonControl)(object)Keyboard.current.nKey; case 111: return (ButtonControl)(object)Keyboard.current.oKey; case 112: return (ButtonControl)(object)Keyboard.current.pKey; case 113: return (ButtonControl)(object)Keyboard.current.qKey; case 114: return (ButtonControl)(object)Keyboard.current.rKey; case 115: return (ButtonControl)(object)Keyboard.current.sKey; case 116: return (ButtonControl)(object)Keyboard.current.tKey; case 117: return (ButtonControl)(object)Keyboard.current.uKey; case 118: return (ButtonControl)(object)Keyboard.current.vKey; case 119: return (ButtonControl)(object)Keyboard.current.wKey; case 120: return (ButtonControl)(object)Keyboard.current.xKey; case 121: return (ButtonControl)(object)Keyboard.current.yKey; case 122: return (ButtonControl)(object)Keyboard.current.zKey; case 300: return (ButtonControl)(object)Keyboard.current.numLockKey; case 301: return (ButtonControl)(object)Keyboard.current.capsLockKey; case 302: return (ButtonControl)(object)Keyboard.current.scrollLockKey; case 303: return (ButtonControl)(object)Keyboard.current.rightShiftKey; case 304: return (ButtonControl)(object)Keyboard.current.leftShiftKey; case 305: return (ButtonControl)(object)Keyboard.current.rightCtrlKey; case 306: return (ButtonControl)(object)Keyboard.current.leftCtrlKey; case 307: return (ButtonControl)(object)Keyboard.current.rightAltKey; case 308: return (ButtonControl)(object)Keyboard.current.leftAltKey; case 310: return (ButtonControl)(object)Keyboard.current.leftCommandKey; case 311: return (ButtonControl)(object)Keyboard.current.leftWindowsKey; case 309: return (ButtonControl)(object)Keyboard.current.rightCommandKey; case 312: return (ButtonControl)(object)Keyboard.current.rightWindowsKey; case 316: return (ButtonControl)(object)Keyboard.current.printScreenKey; case 319: return (ButtonControl)(object)Keyboard.current.contextMenuKey; case 323: return Mouse.current.leftButton; case 324: return Mouse.current.rightButton; case 325: return Mouse.current.middleButton; case 326: return Mouse.current.backButton; case 327: return Mouse.current.forwardButton; default: if (!silent) { XuaLogger.AutoTranslator.Warn(string.Format("[{0}] Unsupported key: {1}", "NewInputSystem", key)); } return null; } } } internal class LegacyInputSystem : IInputSystem { public Vector3 mousePosition => Input.mousePosition; public Vector2 mouseScrollDelta => Input.mouseScrollDelta; public bool mousePresent => Input.mousePresent; public bool anyKey => Input.anyKey; public bool anyKeyDown => Input.anyKeyDown; public IEnumerable<KeyCode> SupportedKeyCodes { get; } = (KeyCode[])Enum.GetValues(typeof(KeyCode)); [MethodImpl(MethodImplOptions.NoInlining)] public LegacyInputSystem() { Input.GetKeyDown((KeyCode)97); } public bool GetKey(string name) { return Input.GetKey(name); } public bool GetKey(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Input.GetKey(key); } public bool GetKeyDown(string name) { return Input.GetKeyDown(name); } public bool GetKeyDown(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Input.GetKeyDown(key); } public bool GetKeyUp(string name) { return Input.GetKeyUp(name); } public bool GetKeyUp(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Input.GetKeyUp(key); } public bool GetMouseButton(int button) { return Input.GetMouseButton(button); } public bool GetMouseButtonDown(int button) { return Input.GetMouseButtonDown(button); } public bool GetMouseButtonUp(int button) { return Input.GetMouseButtonUp(button); } public void ResetInputAxes() { Input.ResetInputAxes(); } } 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 { [CompilerGenerated] private sealed class <GetEnumerator>d__14 : IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, IEnumerator { private int <>1__state; private KeyValuePair<TKey, TValue> <>2__current; public WeakDictionary<TKey, TValue> <>4__this; private Dictionary<object, TValue>.Enumerator <>7__wrap1; KeyValuePair<TKey, TValue> IEnumerator<KeyValuePair<TKey, TValue>>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetEnumerator>d__14(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = default(Dictionary<object, TValue>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; WeakDictionary<TKey, TValue> weakDictionary = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = weakDictionary.dictionary.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap1.MoveNext()) { KeyValuePair<object, TValue> current = <>7__wrap1.Current; WeakReference<TKey> obj = (WeakReference<TKey>)current.Key; TValue value = current.Value; TKey target = obj.Target; if (obj.IsAlive) { <>2__current = new KeyValuePair<TKey, TValue>(target, value); <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap1 = default(Dictionary<object, TValue>.Enumerator); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetEnumerator>d__14(0) { <>4__this = this }; } 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 { [CompilerGenerated] private sealed class <GetEnumerator>d__8 : IEnumerator<T>, IDisposable, IEnumerator { private int <>1__state; private T <>2__current; public Collection<T> <>4__this; private IEnumerator<KeyValuePair<TKey, TValue>> <>7__wrap1; T IEnumerator<T>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetEnumerator>d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; Collection<T> collection = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = collection.dictionary.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } if (<>7__wrap1.MoveNext()) { KeyValuePair<TKey, TValue> current = <>7__wrap1.Current; <>2__current = collection.GetItem(current); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetEnumerator>d__8(0) { <>4__this = this }; } 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, StringComparison.InvariantCulture); if (num != -1) { num += strStart.Length; int num2 = strSource.IndexOf(strEnd, num, StringComparison.InvariantCulture); 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; SupportsMouseScrollDe
BepInEx\plugins\GameTranslator.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; 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.TextCore; 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: AssemblyCompany("CoolLKK_Group")] [assembly: AssemblyDescription("A Lethal Company translator plugin")] [assembly: AssemblyFileVersion("2.2.6.0")] [assembly: AssemblyInformationalVersion("2.2.6")] [assembly: AssemblyProduct("GameTranslator")] [assembly: AssemblyTitle("GameTranslator")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("2.2.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GameTranslator { internal class TranslateConfig { internal class TranslateConfigFile { public string ConfigFilePath; public string ConfigFileName; public bool shouldTranslate; public bool shouldLoad = true; public bool needsParseFile; public IDictionary<string, string> normal = new ConcurrentDictionary<string, string>(); public static HashSet<TranslateConfigFile> configs = new HashSet<TranslateConfigFile>(); public ConcurrentDictionary<string, string> translatePairs = new ConcurrentDictionary<string, string>(); internal readonly object _fileLock = new object(); internal List<RegexTranslation> regexTranslations = new List<RegexTranslation>(); internal readonly ConcurrentDictionary<string, DateTime> _translatePairLastAccess = new ConcurrentDictionary<string, DateTime>(); internal KeyValuePair<string, string>[] _normalOrdered = Array.Empty<KeyValuePair<string, string>>(); internal int shouldTranslateMinLength = 300; internal int shouldTranslateMaxLength; public TranslateConfigFile(string configName, bool shouldLoad, bool needsParseFile = false) { ConfigFileName = configName; ConfigFilePath = Path.GetFullPath(TranslatePlugin.DefaultPath + configName + ".cfg"); this.shouldLoad = shouldLoad; this.needsParseFile = needsParseFile; if (this.shouldLoad && this.needsParseFile && File.Exists(ConfigFilePath)) { Reload(isLoad: true); } else if (!File.Exists(ConfigFilePath)) { Touch(); } configs.Add(this); } public void Reload(bool isLoad = false) { List<string> list = null; lock (_fileLock) { translatePairs.Clear(); _translatePairLastAccess.Clear(); if (needsParseFile) { normal.Clear(); regexTranslations.Clear(); list = ParseTranslationFile(ConfigFilePath, isLoad); } } if (list != null && list.Count > 0) { File.AppendAllLines(Path.Combine(Path.GetDirectoryName(ConfigFilePath), ConfigFileName + "_errors.log"), list); } } private List<string> ParseTranslationFile(string filePath, bool isLoad = false) { if (isLoad) { TranslatePlugin.logger.LogInfo((object)("Loading text file: " + Path.GetFileNameWithoutExtension(filePath) + ".")); } else { TranslatePlugin.logger.LogInfo((object)("Reloading text file: " + Path.GetFileNameWithoutExtension(filePath) + ".")); } Dictionary<string, int> dictionary = new Dictionary<string, int>(); List<string> list = new List<string>(); string[] array = File.ReadAllLines(filePath); for (int i = 0; i < array.Length; i++) { string str = array[i]; string[] array2 = TextHelper.ReadTranslationLineAndDecode(str); if (array2 == null) { continue; } string text = array2[0]; string text2 = array2[1]; if (text.StartsWith("r:")) { try { RegexTranslation item = new RegexTranslation(text, text2); regexTranslations.Add(item); } catch (Exception ex) { string text3 = text + "=" + text2; list.Add("Invalid regex: " + text3 + " - " + ex.Message); TranslatePlugin.logger.LogWarning((object)("Failed to parse regex: " + text3 + ". Error: " + ex.Message)); } continue; } if (normal.ContainsKey(text)) { normal[text] = text2; } else { normal.Add(text, text2); dictionary[text] = i; } if (text.Length < shouldTranslateMinLength) { shouldTranslateMinLength = text.Length; } if (text.Length > shouldTranslateMaxLength) { shouldTranslateMaxLength = text.Length; } } GetNormalOrderedByLength(dictionary); return list; } public void Touch() { string directoryName = Path.GetDirectoryName(ConfigFilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(ConfigFilePath)) { File.Create(ConfigFilePath).Close(); } } private void GetNormalOrderedByLength(Dictionary<string, int> lineOrder) { _normalOrdered = (from kv in normal orderby kv.Key.Length descending, (!lineOrder.TryGetValue(kv.Key, out var value)) ? int.MaxValue : value select kv).ToArray(); } } private static SafeFileWatcher _fileWatcher; private static ConcurrentDictionary<string, DateTime> _fileLastModifiedTimes = new ConcurrentDictionary<string, DateTime>(); private static Timer _pollingTimer; private static readonly object _updateLock = new object(); public static TranslateConfigFile normal; public static TranslateConfigFile terminal; public static TranslateConfigFile interactiveTerminalAPI; public static TranslateConfigFile cmd_zh; public static TranslateConfigFile cmd_py; public static TranslateConfigFile gui; public static TextureTranslationCache cache; public static NormalTextTranslator normalText; public static NormalTextTranslator guiText; private static DateTime _lastCleanupTime = DateTime.Now; private static readonly TimeSpan CLEANUP_INTERVAL = TimeSpan.FromMinutes(30.0); private const long TRANSLATE_PAIR_MEMORY_PRESSURE = 536870912L; private const float TRANSLATE_PAIR_EVICT_RATIO = 0.2f; private const int TRANSLATE_PAIR_MAX = 6000; private const int TRANSLATE_PAIR_EVICT_MIN = 100; public static void Load() { if (TranslatePlugin.shouldTranslateNormalText.Value) { normal = CreateNewConfig("Normal-Translate", should: true); normal.shouldTranslate = true; normalText = new NormalTextTranslator(normal.ConfigFileName + ".cfg"); normalText.Load(isLoad: true); } if (TranslatePlugin.shouldTranslateTerimal.Value) { terminal = CreateNewConfig("Terminal-Translate", should: true, needsParseFile: true); terminal.shouldTranslate = true; } if (TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value) { interactiveTerminalAPI = CreateNewConfig("InteractiveTerminalAPI-Translate", should: true, needsParseFile: true); interactiveTerminalAPI.shouldTranslate = true; } if (TranslatePlugin.TerimalCanUseShortCutOne.Value) { cmd_zh = CreateNewConfig("CMD-ZH-Translate", should: true, needsParseFile: true); } if (TranslatePlugin.TerimalCanUseShortCutTwo.Value) { cmd_py = CreateNewConfig("CMD-PY-Translate", should: true, needsParseFile: true); } if (TranslatePlugin.shouldTranslateGui.Value) { gui = CreateNewConfig("GuiText-Translate", should: true); gui.shouldTranslate = true; guiText = new NormalTextTranslator(gui.ConfigFileName + ".cfg"); guiText.Load(isLoad: true); } if (TranslatePlugin.changeTexture.Value) { cache = new TextureTranslationCache(); cache.LoadTranslationFiles(); } string fullPath = Path.GetFullPath(TranslatePlugin.DefaultPath); ConfigEntry<bool> enableFileWatcher = TranslatePlugin.enableFileWatcher; if (enableFileWatcher != null && enableFileWatcher.Value) { _fileWatcher = new SafeFileWatcher(fullPath); _fileWatcher.DirectoryUpdated += OnDirectoryUpdated; TranslatePlugin.logger.LogInfo((object)("Tracking path " + fullPath)); } foreach (TranslateConfigFile config in TranslateConfigFile.configs) { if (File.Exists(config.ConfigFilePath)) { _fileLastModifiedTimes[config.ConfigFilePath] = File.GetLastWriteTime(config.ConfigFilePath); } } AsyncTranslationManager.Instance.ClearCache(); DefaultTextComponentManipulator.ClearCache(); ConfigEntry<bool> enablePollingCheck = TranslatePlugin.enablePollingCheck; if (enablePollingCheck != null && enablePollingCheck.Value) { _pollingTimer = new Timer(delegate { OnDirectoryUpdated(); }, null, TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(10.0)); TranslatePlugin.logger.LogInfo((object)("Polling check tracking path " + fullPath)); } } public static void Unload() { _fileWatcher?.Dispose(); _fileWatcher = null; cache?.Dispose(); cache = null; _pollingTimer?.Dispose(); _pollingTimer = null; AsyncTranslationManager.Instance.ClearCache(); DefaultTextComponentManipulator.ClearCache(); } private static void OnDirectoryUpdated() { lock (_updateLock) { try { bool flag = false; 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)) { continue; } _fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime; for (int i = 0; i < 3; i++) { try { config.Reload(); GetModuleTranslator(config)?.Load(); TextTranslate.ChangeTime++; flag = true; } catch (IOException) when (i < 2) { Thread.Sleep(100 * (i + 1)); continue; } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Unexpected error reloading config " + config.ConfigFileName + ": " + ex2.Message)); } break; } } else { _fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime; } } if (flag) { AsyncTranslationManager.Instance.ClearCache(); DefaultTextComponentManipulator.ClearCache(); TranslatePlugin.logger.LogInfo((object)"Translate files reloaded due to file changes."); } } catch (Exception ex3) { TranslatePlugin.logger.LogError((object)("Error in OnDirectoryUpdated: " + ex3.Message)); } } } private static TranslateConfigFile CreateNewConfig(string fileName, bool should, bool needsParseFile = false) { TranslatePlugin.logger.LogInfo((object)(">>> Loading " + fileName + " file")); return new TranslateConfigFile(fileName, should, needsParseFile); } public static void show(TranslateConfigFile file) { if (file == null) { return; } foreach (string key in file.normal.Keys) { TranslatePlugin.logger.LogInfo((object)(key + "=" + file.normal[key])); } NormalTextTranslator moduleTranslator = GetModuleTranslator(file); if (moduleTranslator == null) { return; } foreach (KeyValuePair<string, string> translation in moduleTranslator._translations) { TranslatePlugin.logger.LogInfo((object)(translation.Key + "=" + translation.Value)); } } public static string replaceByMap(string text, TranslateConfigFile file) { if (file == null) { return text; } if (file.normal.Count == 0 && file.regexTranslations.Count == 0) { return text; } Stopwatch stopwatch = null; if (TranslatePlugin.showOtherDebug.Value) { 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[text] = DateTime.Now; return file.translatePairs[text]; } StringBuffer stringBuffer = new StringBuffer(text); if (file.regexTranslations.Count > 0) { RegexTranslation[] array; lock (file._fileLock) { array = file.regexTranslations.ToArray(); } RegexTranslation[] array2 = array; foreach (RegexTranslation regexTranslation in array2) { if (regexTranslation.CompiledRegex.IsMatch(stringBuffer.ToString())) { string str = regexTranslation.CompiledRegex.Replace(stringBuffer.ToString(), regexTranslation.Translation); stringBuffer.Clear().Append(str); } } } KeyValuePair<string, string>[] normalOrdered = file._normalOrdered; for (int j = 0; j < normalOrdered.Length; j++) { KeyValuePair<string, string> keyValuePair = normalOrdered[j]; stringBuffer.ReplaceFull(keyValuePair.Key, keyValuePair.Value); } string text2 = stringBuffer.ToString(); file.translatePairs[text] = text2; file._translatePairLastAccess.TryAdd(text, DateTime.Now); return text2; } finally { if (stopwatch != null) { stopwatch.Stop(); if (stopwatch.ElapsedMilliseconds > 500) { string arg = ((text.Length > 50) ? (text.Substring(0, 50) + "...") : text); try { TranslatePlugin.logger.LogWarning((object)$"replaceByMap took {stopwatch.ElapsedMilliseconds}ms for text: {arg}"); } catch (IndexOutOfRangeException) { } } } } } internal static NormalTextTranslator GetModuleTranslator(TranslateConfigFile file) { if (file == normal) { return normalText; } if (file == gui) { return guiText; } return null; } private static void CleanupTranslatePairs() { bool flag = GC.GetTotalMemory(forceFullCollection: false) > 536870912; foreach (TranslateConfigFile config in TranslateConfigFile.configs) { if (!config.needsParseFile) { continue; } int num = 0; string text = null; if (flag) { if (config.translatePairs.Count >= 100) { num = (int)((float)config.translatePairs.Count * 0.2f); num = Math.Max(1, Math.Min(num, config.translatePairs.Count)); } text = "memory pressure"; } else if (config.translatePairs.Count > 6000) { num = config.translatePairs.Count - 6000; text = "over limit"; } 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.TryRemove(item, out var _); config._translatePairLastAccess.TryRemove(item, out var _); } TranslatePlugin.logger.LogInfo((object)$"Cleaned {list.Count} translate pairs from {config.ConfigFileName}. Remaining: {config.translatePairs.Count} (reason: {text})"); } } } [BepInPlugin("GameTranslator", "GameTranslator", "2.2.6")] public class TranslatePlugin : BaseUnityPlugin { private class TranslationUpdater : MonoBehaviour { private void Update() { try { AsyncTranslationManager.Instance.ProcessMainThreadActions(); } catch (Exception ex) { ManualLogSource logger = TranslatePlugin.logger; if (logger != null) { logger.LogError((object)("Error in TranslationUpdater Update: " + ex.Message)); } } } } private readonly Harmony harmony = new Harmony("GameTranslator"); private const string PLUGIN_GUID = "GameTranslator"; internal const string PLUGIN_NAME = "GameTranslator"; internal const string PLUGIN_VERSION = "2.2.6"; internal const string PLUGIN_VERSION_FULL = "2.2.6.0"; public static ManualLogSource logger; public static ConfigEntry<int> syncTranslationThreshold; public static ConfigEntry<bool> showAvailableText; public static ConfigEntry<bool> showOtherDebug; public static ConfigEntry<bool> enableFileWatcher; public static ConfigEntry<bool> enablePollingCheck; public static ConfigEntry<bool> replaceUnsupportedCharacters; public static ConfigEntry<bool> enableTypingTranslation; public static ConfigEntry<bool> enableAsyncDuringTyping; public static ConfigEntry<bool> cacheUnmodifiedTextures; public static ConfigEntry<bool> enableTextureDumping; public static ConfigEntry<int> stabilizationMinTextLength; public static ConfigEntry<float> stabilizationDelay; public static ConfigEntry<int> stabilizationMaxRetries; public static ConfigEntry<bool> enableTerminalPatch; public static ConfigEntry<bool> changeFont; public static ConfigEntry<bool> enableDynamicFont; public static ConfigEntry<bool> scaleFallbackEffects; public static ConfigEntry<float> fallbackEffectScale; public static ConfigEntry<string> fallbackFontTextMeshPro; public static ConfigEntry<string> shouldRemoveChar; public static ConfigEntry<string> language; public static ConfigEntry<bool> shouldTranslateNormalText; public static ConfigEntry<bool> shouldTranslateTerimal; public static ConfigEntry<bool> shouldTranslateInteractiveTerminalAPI; public static ConfigEntry<bool> TerimalCanUseShortCutOne; public static ConfigEntry<bool> TerimalCanUseShortCutTwo; public static ConfigEntry<bool> shouldTranslateGui; public static ConfigEntry<bool> changeTexture; public static ConfigEntry<bool> cacheTexturesInMemory; public static ConfigEntry<bool> disableDuplicateTextureCheck; public static ConfigEntry<string> ignoredTextureNames; internal static TranslatePlugin Instance; internal static string DefaultPath; internal static string TexturesPath; internal static string DumpPath; private void Awake() { logger = ((BaseUnityPlugin)this).Logger; Instance = this; ((Component)this).gameObject.AddComponent<TranslationUpdater>(); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); ConfigFile(); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.All, false); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.Sprite, false); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.SpriteRenderer, false); ApplyBasicPatches(); ApplyTerminalPatch(); ApplyInteractiveTerminalAPIPatch(); if (replaceUnsupportedCharacters.Value) { FontSupportChecker.InitializeFonts(); } AsyncTranslationManager.Instance.Start(); SceneManager.activeSceneChanged += delegate(Scene from, Scene to) { if (showAvailableText.Value) { logger.LogInfo((object)$"[Scope] Active scene changed: '{((Scene)(ref to)).name}' (buildIndex={((Scene)(ref to)).buildIndex})"); } }; ((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator is loaded"); } private void OnDestroy() { try { AsyncTranslationManager.Instance.Stop(); } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Error in OnDestroy: " + ex.Message)); } } TranslateConfig.Unload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator destroyed"); } private void ConfigFile() { syncTranslationThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("ASync", "Sync Translation Threshold", 300, "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"); enableFileWatcher = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable File Watcher", false, "If true, enable file system watcher for file updates"); enablePollingCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Polling Check", false, "If true, enable the 10-seconds polling fallback for file updates"); replaceUnsupportedCharacters = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Replace Unsupported Characters", false, "Define whether to replace unsupported characters with Unicode character u25A1"); enableTypingTranslation = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable TextWindow Typing Translation", false, "Define whether to display translated text letter-by-letter during the textwindow typing animation instead of waiting for the animation to complete"); enableAsyncDuringTyping = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Async During Typing Translation", false, "Define whether to allow async translation during typing animation which terminating the animation when async translation completes"); cacheUnmodifiedTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Cache Unmodified Textures", false, "Define whether to cache textures that have not been modified"); enableTextureDumping = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Texture Dumping", false, "Define whether to dump original textures to disk for debug purposes"); stabilizationMinTextLength = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Stabilization Min Text Length", 100, "Define minimum text length to trigger stabilization. Set to 0 to disable stabilization"); stabilizationDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "Stabilization Delay", 0.9f, "Define delay in seconds between stabilization checks. Must be greater than 0"); stabilizationMaxRetries = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Stabilization Max Retries", 60, "Define maximum retries for text stabilization safeguard. Set to 0 for unlimited retries"); enableTerminalPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Terminal Patch", true, "Define whether to patch Terminal"); changeFont = ((BaseUnityPlugin)this).Config.Bind<bool>("Font", "Change Font", false, "Define whether to change the font"); enableDynamicFont = ((BaseUnityPlugin)this).Config.Bind<bool>("Font", "Enable Dynamic Font", false, "Define whether to dynamically add missing characters to fallback fonts at runtime"); scaleFallbackEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Font", "Scale Fallback Effects", false, "Define whether to proportionally scale SDF effects on fallback fonts"); fallbackEffectScale = ((BaseUnityPlugin)this).Config.Bind<float>("Font", "Fallback Effect Scale", 1f, "Define the scale multiplier for fallback font SDF effects (lower = lighter effects)"); fallbackFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind<string>("Font", "FallbackFontTextMeshPro", "", "Define the fallback font asset bundle(s) used"); shouldRemoveChar = ((BaseUnityPlugin)this).Config.Bind<string>("Font", "Custom Characters", "", "Define what vanilla characters will use custom ones"); language = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "Default", "Define what language folder is used"); shouldTranslateNormalText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Normal Text", true, "Define whether to use Normal Translate method"); shouldTranslateTerimal = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Terminal", false, "Define whether translate Terminal"); shouldTranslateInteractiveTerminalAPI = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate InteractiveTerminalAPI", false, "Define whether translate InteractiveTerminalAPI"); TerimalCanUseShortCutOne = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Terminal Can Use Shortcut Commands Category ZH", false, "Define whether the terminal can use category ZH shortcut commands"); TerimalCanUseShortCutTwo = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Terminal Can Use Shortcut Commands Category PY", false, "Define whether the terminal can use category PY shortcut commands"); shouldTranslateGui = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Gui", false, "Define whether translate Gui"); changeTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Change Texture", false, "Define whether to change the texture"); cacheTexturesInMemory = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Cache Textures In Memory", true, "Define whether to cache texture data in memory for faster loading"); disableDuplicateTextureCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Disable Duplicate Texture Check", true, "Define whether to disable duplicate texture name check"); ignoredTextureNames = ((BaseUnityPlugin)this).Config.Bind<string>("Texture", "Ignored Texture Names", "", "Define what texture names to skip duplicate check"); 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); } DumpPath = DefaultPath + "Dump\\"; if (enableTextureDumping.Value && !Directory.Exists(DumpPath)) { Directory.CreateDirectory(DumpPath); } TranslateConfig.Load(); TranslateExtensions.Load(); } private void ApplyBasicPatches() { try { logger.LogInfo((object)"Applying basic patches..."); Type[] array = new Type[13] { typeof(GameObjectHook), typeof(GuiContentHook), typeof(TeshMeshProHook), typeof(TeshMeshProUGUIHook), typeof(TextHook), typeof(TextMeshHook), typeof(TMP_FallbackMaterialHook), typeof(TMP_FallbackMaterialHook_AtlasIndex), typeof(TMP_FontAssetHook), typeof(TMP_GetTextElementHook), typeof(TMP_TextHook), typeof(TextElement_text_Hook), typeof(Texture2DHook) }; List<string> list = array.Select((Type t) => t.Name).ToList(); logger.LogDebug((object)string.Format("Found {0} basic patch types: {1}", list.Count, string.Join(", ", list))); int num = 0; List<string> list2 = new List<string>(); Type[] array2 = array; foreach (Type type in array2) { try { harmony.PatchAll(type); num++; list2.Add(type.Name); logger.LogDebug((object)("Applied basic patch: " + type.Name)); } catch (Exception ex) { logger.LogWarning((object)("Failed to apply basic patch " + type.Name + ": " + ex.Message)); } } logger.LogInfo((object)$"Basic patches applied. Successfully applied {num}/{array.Length} patches."); if (list2.Count > 0) { logger.LogDebug((object)("Successfully applied patches: " + string.Join(", ", list2))); } if (num < array.Length) { List<string> list3 = list.Except(list2).ToList(); logger.LogWarning((object)string.Format("Failed to apply {0} patches: {1}", list3.Count, string.Join(", ", list3))); } } catch (Exception ex2) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Error applying basic patches: " + ex2.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) { InteractiveTerminalAPIPatch.Initialize(harmony); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"InteractiveTerminalAPI patch applied successfully"); } } 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(Terminal))] internal class TerminalPatch { private static TextTranslationInfo info; public static HashSet<object> ig = new HashSet<object>(); private static FieldInfo hasGottenVerb = typeof(Terminal).GetField("hasGottenVerb", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); private static FieldInfo modifyingText = typeof(Terminal).GetField("modifyingText", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); 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]}"); } string text = secondWord.ToLower(); for (int j = 0; j < list.Count; j++) { if (list[j].ToLower() == text) { return j; } } Debug.Log((object)$"Target names length: {list.Count}"); for (int k = 0; k < list.Count; k++) { Debug.Log((object)"A"); string text2 = list[k].ToLower(); Debug.Log((object)$"Word #{k}: {text2}; length: {text2.Length}"); for (int num = secondWord.Length; num > 2; num--) { Debug.Log((object)$"c: {num}"); Debug.Log((object)secondWord.Substring(0, num)); if (text2.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 (GetCmd(options[i].noun.word, useC: true).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()) || GetCmd(options[i].noun.word, useC: false).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 (GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: true).EqualsIgnoreCase(playerWord) || GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: false).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 (GetCmd(array[i].objectCode, useC: true).EqualsIgnoreCase(word) || GetCmd(array[i].objectCode, useC: false).EqualsIgnoreCase(word)) { Debug.Log((object)"Found accessible terminal object with corresponding string, calling function"); FieldInfo field = ((object)__instance).GetType().GetField("broadcastedCodeThisFrame", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(__instance, true); } array[i].CallFunctionFromTerminal(); break; } } } [HarmonyPostfix] [HarmonyPatch("ParseWord")] private static void ParseWord(Terminal __instance, string playerWord, int specificityRequired, ref TerminalKeyword __result) { if (!TranslatePlugin.TerimalCanUseShortCutOne.Value && !TranslatePlugin.TerimalCanUseShortCutTwo.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; } if (GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: true).EqualsIgnoreCase(playerWord) || GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: false).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; return; } if (!((Object)(object)val == (Object)null)) { continue; } for (int num = playerWord.Length; num > specificityRequired; num--) { if (GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: true).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()) || GetCmd(__instance.terminalNodes.allKeywords[i].word, useC: false).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.TerimalCanUseShortCutOne.Value && TranslateConfig.cmd_zh.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["transmit"])) || (TranslatePlugin.TerimalCanUseShortCutTwo.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.TerimalCanUseShortCutOne.Value && TranslateConfig.cmd_zh.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["switch"])) || (TranslatePlugin.TerimalCanUseShortCutTwo.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.TerimalCanUseShortCutOne.Value && TranslateConfig.cmd_zh.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["ping"])) || (TranslatePlugin.TerimalCanUseShortCutTwo.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.TerimalCanUseShortCutOne.Value && TranslateConfig.cmd_zh.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["flash"])) || (TranslatePlugin.TerimalCanUseShortCutTwo.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 string GetCmd(string name, bool useC) { if (useC) { if (TranslatePlugin.TerimalCanUseShortCutOne.Value && TranslateConfig.cmd_zh.normal.ContainsKey(name)) { return TranslateConfig.cmd_zh.normal[name]; } } else if (TranslatePlugin.TerimalCanUseShortCutTwo.Value && TranslateConfig.cmd_py.normal.ContainsKey(name)) { return TranslateConfig.cmd_py.normal[name]; } return ""; } [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 && __instance.currentText.Length - info.OriginalText.Length != 0) { info.Reset(__instance.currentText); } } [HarmonyPostfix] [HarmonyPatch("Update")] private static void changeUpdateText(Terminal __instance) { try { if (info == null || !TranslatePlugin.shouldTranslateTerimal.Value || info.IsTranslated) { return; } if (TranslatePlugin.showAvailableText.Value && !string.IsNullOrEmpty(__instance.currentText) && TextTranslate.ShouldOutputDebug("terminal:" + __instance.currentText)) { try { TranslatePlugin.logger.LogInfo((object)("[Debug] Terminal available text: '" + __instance.currentText + "'")); } catch (IndexOutOfRangeException) { } } string currentText = __instance.currentText; string translatedText = TranslateConfig.replaceByMap(currentText, TranslateConfig.terminal); info.OriginalText = currentText; info.SetTranslatedText(translatedText); SetText(info.TranslatedText, __instance); } catch (Exception ex2) { TranslatePlugin.logger.LogWarning((object)ex2); } } private 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 ComponentExtensions { private static bool _guiContentCheckFailed; public static bool SupportsStabilization(this object ui) { if (ui == null) { return false; } if (!_guiContentCheckFailed) { return !IsGUIContentSafe(ui); } return true; } private static bool IsGUIContentSafe(object ui) { try { return ui is GUIContent; } catch { _guiContentCheckFailed = true; } return false; } } internal static class FontCache { private static bool _hasReadFallbackFontTextMeshPro; private static List<Object> FallbackFontsTextMeshPro; public static List<Object> GetOrCreateFallbackFontTextMeshPro() { if (!_hasReadFallbackFontTextMeshPro) { _hasReadFallbackFontTextMeshPro = true; try { if (string.IsNullOrEmpty(TranslatePlugin.fallbackFontTextMeshPro.Value)) { FallbackFontsTextMeshPro = new List<Object>(); return FallbackFontsTextMeshPro; } FallbackFontsTextMeshPro = new List<Object>(); string value = TranslatePlugin.fallbackFontTextMeshPro.Value; if (!value.Contains(",")) { string text = Path.Combine(TranslatePlugin.DefaultPath, value.Trim()); if (File.Exists(text)) { LoadFontFile(text); return FallbackFontsTextMeshPro; } if (Directory.Exists(text)) { TranslatePlugin.logger.LogInfo((object)("Loading fallback fonts from directory: " + text)); foreach (string item in from f in Directory.GetFiles(text, "*") orderby f select f) { LoadFontFile(item); } return FallbackFontsTextMeshPro; } return FallbackFontsTextMeshPro; } string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); if (!string.IsNullOrEmpty(text3)) { string fontPath = Path.Combine(TranslatePlugin.DefaultPath, text3); LoadFontFile(fontPath); } } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An error occurred while loading fallback fonts. Error: " + ex.Message)); } } return FallbackFontsTextMeshPro; } private static void LoadFontFile(string fontPath) { try { List<Object> textMeshProFonts = FontHelper.GetTextMeshProFonts(fontPath); if (textMeshProFonts.Count <= 0) { return; } FallbackFontsTextMeshPro.AddRange(textMeshProFonts); foreach (Object item in textMeshProFonts) { TMP_FontAsset val = (TMP_FontAsset)(object)((item is TMP_FontAsset) ? item : null); if ((Object)(object)val != (Object)null) { FontDynamicLoader.RegisterDynamicFont(val); } } } 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: " + fontPath + ". Error: " + ex2.Message)); } } } internal static class FontDynamicLoader { private static readonly HashSet<uint> _processedChars = new HashSet<uint>(); private static readonly HashSet<TMP_FontAsset> _dynamicFonts = new HashSet<TMP_FontAsset>(); private static bool _warned; internal static void RegisterDynamicFont(TMP_FontAsset font) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)font != (Object)null && (int)font.atlasPopulationMode != 0 && TranslatePlugin.changeFont.Value && TranslatePlugin.enableDynamicFont.Value) { _dynamicFonts.Add(font); } } internal static void TryAddCharacterOnDemand(uint unicode) { if (_dynamicFonts.Count == 0 || !TranslatePlugin.changeFont.Value || !TranslatePlugin.enableDynamicFont.Value || !_processedChars.Add(unicode)) { return; } string text = char.ConvertFromUtf32((int)unicode); foreach (TMP_FontAsset dynamicFont in _dynamicFonts) { try { if (dynamicFont.TryAddCharacters(text, false)) { return; } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("[DynamicFont] Failed: " + ex.Message)); } } if (!_warned) { _warned = true; TranslatePlugin.logger.LogWarning((object)"[DynamicFont] Cannot add character. Atlas may be full or character unsupported."); } } } internal static class FontHelper { private static readonly List<AssetBundle> _loadedBundles = new List<AssetBundle>(); public static List<Object> GetTextMeshProFonts(string assetBundle) { //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) List<Object> list = new List<Object>(); if (string.IsNullOrEmpty(assetBundle)) { return list; } 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 val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { TranslatePlugin.logger.LogWarning((object)("Could not load asset bundle while loading font: " + text)); return list; } _loadedBundles.Add(val); TMP_FontAsset[] array = val.LoadAllAssets<TMP_FontAsset>(); if (array != null) { TMP_FontAsset[] array2 = array; foreach (TMP_FontAsset val2 in array2) { if (!((Object)(object)val2 != (Object)null)) { continue; } string text2 = (((Object)(object)((TMP_Asset)val2).material != (Object)null && (Object)(object)((TMP_Asset)val2).material.shader != (Object)null) ? ((Object)((TMP_Asset)val2).material.shader).name : "Unknown"); int num = ((val2.atlasTextures != null) ? val2.atlasTextures.Length : 0); string text3; if (num > 0) { StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < num; j++) { if ((Object)(object)val2.atlasTextures[j] != (Object)null) { stringBuilder.Append(((Texture)val2.atlasTextures[j]).width + "x" + ((Texture)val2.atlasTextures[j]).height); } else { stringBuilder.Append("null"); } if (j < num - 1) { stringBuilder.Append(", "); } } text3 = num + " atlas(es): " + stringBuilder; } else { text3 = "0 atlas"; } ManualLogSource logger = TranslatePlugin.logger; object[] obj = new object[6] { ((Object)val2).name, val2.version, text2, text3, null, null }; FaceInfo faceInfo = val2.faceInfo; obj[4] = ((FaceInfo)(ref faceInfo)).pointSize; obj[5] = val2.atlasPadding; logger.LogInfo((object)string.Format("Loaded TextMesh Pro font '{0}' version={1}, shader={2}, {3}, pointSize={4}, padding={5}", obj)); list.Add((Object)(object)val2); } } } else { TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from internal Resources API: " + assetBundle)); Object val3 = Resources.Load(assetBundle); if (val3 != (Object)null) { list.Add(val3); } } if (list.Count == 0) { TranslatePlugin.logger.LogError((object)("Could not find any TextMeshPro font assets: " + assetBundle)); } return list; } 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(); } } internal static class FontSupportChecker { private static readonly ConcurrentDictionary<TMP_FontAsset, bool> _availableFonts = new ConcurrentDictionary<TMP_FontAsset, bool>(); 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(); internal static void InitializeFonts() { if (_isInitialized) { return; } lock (_lockObject) { if (_isInitialized) { return; } _availableFonts.Clear(); _characterSupportCache.Clear(); _textCache.Clear(); if (TranslatePlugin.changeFont.Value) { List<Object> orCreateFallbackFontTextMeshPro = FontCache.GetOrCreateFallbackFontTextMeshPro(); foreach (Object item in orCreateFallbackFontTextMeshPro) { TMP_FontAsset val = (TMP_FontAsset)(object)((item is TMP_FontAsset) ? item : 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.ContainsKey(val2)) { AddFont(val2); } } if (_availableFonts.Count > 0) { _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.ContainsKey(font)) { return; } _availableFonts.TryAdd(font, value: true); if (font.fallbackFontAssetTable == null) { return; } foreach (TMP_FontAsset item in font.fallbackFontAssetTable) { if ((Object)(object)item != (Object)null && !_availableFonts.ContainsKey(item)) { _availableFonts.TryAdd(item, value: true); } } } internal static void RegisterFont(TMP_FontAsset font) { if ((Object)(object)font == (Object)null) { return; } lock (_lockObject) { AddFont(font); _isInitialized = true; _characterSupportCache.Clear(); _textCache.Clear(); TranslatePlugin.logger.LogDebug((object)("Registered new font: " + ((Object)font).name)); } } private static bool IsCharacterSupported(char character) { if (!_isInitialized) { return true; } if (_characterSupportCache.TryGetValue(character, out var value)) { return value; } value = _availableFonts.Keys.Any((TMP_FontAsset font) => (Object)(object)font != (Object)null && font.HasCharacter(character, true, true)); _characterSupportCache[character] = value; return value; } internal static string ReplaceUnsupportedCharacters(string text, TMP_Text textComponent = null) { if (string.IsNullOrEmpty(text) || !TranslatePlugin.replaceUnsupportedCharacters.Value) { return text; } if (!_isInitialized) { InitializeFonts(); } if (_textCache.TryGetValue(text, out var 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 (char.IsControl(c)) { stringBuilder.Append(c); continue; } if (IsCharacterSupported(c)) { stringBuilder.Append(c); continue; } stringBuilder.Append('□'); flag2 = true; } string text2 = stringBuilder.ToString(); if (flag2 && TranslatePlugin.showOtherDebug.Value) { try { TranslatePlugin.logger.LogInfo((object)("[FontSupport] Replaced unsupported characters for text: '" + text + "' -> '" + text2 + "'")); } catch (IndexOutOfRangeException) { } } _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}"; } } internal 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 var 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 var 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 sealed class SafeFileWatcher : IDisposable { private FileSystemWatcher _watcher; private bool _disposed; private int _counter; private object _sync = new object(); private Timer _timer; private readonly string _directory; public event Action DirectoryUpdated; public SafeFileWatcher(string directory) { _directory = directory; _timer = new Timer(RaiseEvent, null, -1, -1); EnableWatcher(); } public void EnableWatcher() { if (_watcher == null) { _watcher = new FileSystemWatcher(_directory); _watcher.Changed += Watcher_Changed; _watcher.Created += Watcher_Created; _watcher.Deleted += Watcher_Deleted; _watcher.EnableRaisingEvents = true; } } public void Disable() { int num = Interlocked.Increment(ref _counter); UpdateRaisingEvents(num == 0); } public void Enable() { int num = Interlocked.Decrement(ref _counter); UpdateRaisingEvents(num == 0); } public void DisableWatcher() { if (_watcher != null) { _watcher.EnableRaisingEvents = false; _watcher.Dispose(); _watcher = null; } } private void UpdateRaisingEvents(bool enabled) { lock (_sync) { if (enabled) { EnableWatcher(); } else { DisableWatcher(); } } } public void RaiseEvent(object state) { this.DirectoryUpdated?.Invoke(); } private void Watcher_Deleted(object sender, FileSystemEventArgs e) { _timer.Change(1000, -1); } private void Watcher_Created(object sender, FileSystemEventArgs e) { FileInfo file = new FileInfo(e.FullPath); WaitForFile(file); _timer.Change(1000, -1); } private void Watcher_Changed(object sender, FileSystemEventArgs e) { _timer.Change(1000, -1); } private void WaitForFile(FileInfo file) { while (IsFileLocked(file)) { Thread.Sleep(100); } } private bool IsFileLocked(FileInfo file) { try { using (file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } } catch (IOException) { return true; } return false; } private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { _watcher?.Dispose(); _watcher = null; _timer.Dispose(); } _disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal class StringBuffer { private char[] value; private int length; private int capacity; 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(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(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 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 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 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; int[] lps = new int[num]; computeLPSArray(oldValue, num, lps); for (int num3 = IndexOfWord(oldValue, 0, length, lps); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2, length - (num3 + num2), lps)) { Remove(num3, num); Insert(num3, newValue); } return this; } 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[] lps = new int[num]; computeLPSArray(str, num, lps); return IndexOfWord(str, startIndex, count, lps); } private int IndexOfWord(string str, int startIndex, int count, int[] lps) { int num = str.Length; int num2 = 0; int num3 = startIndex; while (num3 < startIndex + count) { if (str[num2] == value[num3]) { num2++; num3++; } if (num2 == num) { if ((num3 - num2 == 0 || !IsWordChar(value[num3 - num2 - 1])) && (num3 == length || !IsWordChar(value[num3]))) { return num3 - num2; } num2 = lps[num2 - 1]; } else if (num3 < startIndex + count && str[num2] != value[num3]) { if (num2 != 0) { num2 = lps[num2 - 1]; } else { num3++; } } } return -1; static bool IsWordChar(char c) { if (!char.IsLetterOrDigit(c)) { return c == '_'; } return true; } } 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++; } } } public StringBuffer Clear() { Length = 0; return this; } public override string ToString() { return new string(value, 0, length); } } internal static class TextHelper { 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("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 && 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 && 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 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.showAvailableText.Value && !TranslatePlugin.showOtherDebug.Value) { return false; } 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.showAvailableText.Value && !TranslatePlugin.showOtherDebug.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 (list.Count > 0) { TranslatePlugin.logger.LogInfo((object)$"[Debug] Cleaned up {list.Count} old debug cache entries"); } } private static bool IsTerminalIgnoredUI(object ui) { if (TranslatePlugin.enableTerminalPatch == null || !TranslatePlugin.enableTerminalPatch.Value) { return false; } 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 true; } } } catch { } return false; } private bool TryTranslateChangedText(object ui, ref string text, out string translated, out TextTranslationInfo info) { translated = null; info = null; if (IsTerminalIgnoredUI(ui)) { return false; } info = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = DiscoverComponent(ui, info); if (!TranslatePlugin.shouldTranslateNormalText.Value) { return false; } if (text == null) { text = ui.GetText(info); } translated = TranslateOrQueue(ui, text, info, TranslateConfig.normalText, TranslateConfig.normal, ignoreComponentState); if (!string.IsNullOrEmpty(translated) && !translated.Equals(text)) { return IsUIObjectValid(ui); } return false; } internal void OnComponentTextChanged(object ui) { if (!DefaultTextComponentManipulator.IsTextWindowTextMesh(ui)) { string text = null; if (TryTranslateChangedText(ui, ref text, out var translated, out var info)) { SetText(ui, translated, info); } } } internal void OnTranslateIncomingText(object ui, ref string value) { if (DefaultTextComponentManipulator.IsTextWindowTextMesh(ui)) { DefaultTextComponentManipulator.HandleTextWindowText(ui, ref value); return; } string text = value; if (TryTranslateChangedText(ui, ref text, out var translated, out var _)) { value = translated; } } public string TranslateOrQueue(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { bool shouldContinue; string result = GuardAndPrepareText(ui, ref text, info, out shouldContinue, ignoreComponentState); if (!shouldContinue) { return result; } string text2 = normalText?.TryGetCachedTranslation(text, TranslationScopeHelper.GetScope(ui)); if (text2 != null) { if (!TranslatePlugin.showAvailableText.Value && TranslatePlugin.showOtherDebug.Value && ShouldOutputDebug("cached-result:" + text)) { try { TranslatePlugin.logger.LogInfo((object)("[Debug] Cached translation found for text: '" + text + "' -> '" + text2 + "'")); } catch (IndexOutOfRangeException) { } } else if (TranslatePlugin.showAvailableText.Value && TranslatePlugin.showOtherDebug.Value && ShouldOutputDebug("cached:" + text)) { try { TranslatePlugin.logger.LogInfo((object)("[Debug] Cached translation hit for text: '" + text + "'")); } catch (IndexOutOfRangeException) { } } if (info != null) { info.OriginalText = text; info.SetTranslatedText(text2); } if (!IsUIObjectValid(ui)) { return null; } return text2; } if (normalText == null || normalText.IsTranslatable(text, isToken: false, TranslationScopeHelper.GetScope(ui))) { if (text.Length <= TranslatePlugin.syncTranslationThreshold.Value) { string text3 = TranslateImmediate(ui, text, info, normalText, config, ignoreComponentState); if (text3 != null) { return text3; } } else { if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("queued:" + text)) { try { TranslatePlugin.logger.LogInfo((object)("[Debug] Queued available text: '" + text + "'")); } catch (IndexOutOfRangeException) { } } 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) { bool shouldContinue; string result = GuardAndPrepareText(ui, ref text, info, out shouldContinue, ignoreComponentState); if (!shouldContinue) { return result; } string text2 = null; int scope = TranslationScopeHelper.GetScope(ui); if (normalText == null || normalText.IsTranslatable(text, isToken: false, scope)) { if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value) { if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("available:" + text)) { try { TranslatePlugin.logger.LogInfo((object)("[Debug] Found available text: '" + text + "'")); } catch (IndexOutOfRangeException) { } } text2 = normalText.TryTranslate(text, scope); } if (text2 != null && info != null) { info.OriginalText = text; info.SetTranslatedText(text2); } } return text2; } private static string GuardAndPrepareText(object ui, ref string text, TextTranslationInfo info, out bool shouldContinue, bool ignoreComponentState = false) { shouldContinue = false; if (!ignoreComponentState && !ui.IsComponentActive()) { return null; } if (info != null && (info.IsCurrentlySettingText || info.MustIgnore || info.ShouldIgnore)) { return null; } text = text ?? ui.GetText(info); if (Utility.IsNullOrWhiteSpace(text)) { return null; } if (info != null && info.IsTranslated) { if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text)) { if (info.ChangeTime == ChangeTime) { return info.TranslatedText; } info.Reset(text); } else { info.Reset(text); } } shouldContinue = true; return null; } 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, 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; } } } internal static 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 true; } 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; } } internal class TextureTranslate { public static TextureTranslate Instance = new TextureTranslate(); public static bool ImageHooksEnabled = true; public static long ChangeTime = 0L; internal void Hook_ImageChangedOnComponent(object source, ref Texture2D texture, bool isPrefixHooked, bool onEnable = false) { if (ImageHooksEnabled && (TranslatePlugin.changeTexture.Value || TranslatePlugin.enableTextureDumping.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 || TranslatePlugin.enableTextureDumping.Value) && source.IsKnownImageType()) { HandleImage(source, ref sprite, ref texture, isPrefixHooked); } } internal void Hook_ImageChanged(ref Texture2D texture, bool isPrefixHooked) { if (ImageHooksEnabled && (TranslatePlugin.changeTexture.Value || TranslatePlugin.enableTextureDumping.Value) && !((Object)(object)texture == (Object)null)) { Sprite sprite = null; HandleImage(null, ref sprite, ref texture, isPrefixHooked); } } private void HandleImage(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked) { try { if (TranslatePlugin.enableTextureDumping.Value) { DumpTexture(source, texture); } if (TranslatePlugin.changeTexture.Value && 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 DumpTexture(object source, Texture2D texture) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected I4, but got Unknown try { ImageHooksEnabled = false; texture = texture ?? source.GetTexture(); if ((Object)(object)texture == (Object)null) { return; } int num = (int)texture.format; if (num == 1 || num == 9 || num == 63) { return; } TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo(); if (!orCreateTextureTranslationInfo.IsDumped) { string key = orCreateTextureTranslationInfo.GetKey(); if (!string.IsNullOrEmpty(key)) { string textureName = texture.GetTextureName("Unnamed"); byte[] orCreateOriginalData = orCreateTextureTranslationInfo.GetOrCreateOriginalData(); DumpImageToDisk(textureName, key, orCreateOriginalData); orCreateTextureTranslationInfo.IsDumped = true; } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while dumping texture."); } finally { ImageHooksEnabled = true; } } private static void DumpImageToDisk(string textureName, string key, byte[] data) { Directory.CreateDirectory(TranslatePlugin.DumpPath); string text = StringExtensions.SanitizeForFileSystem(textureName); string text2 = TextureTranslationCache.HashHelper.Compute(data); string text3 = ((!(key == text2)) ? (text + " [" + key + "-" + text2 + "].png") : (text + " [" + key + "].png")); string path = Path.Combine(TranslatePlugin.DumpPath, text3); File.WriteAllBytes(path, data); XuaLogger.AutoTranslator.Info("Dumped texture file: " + text3); } 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 var data, out var 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 ((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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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) { if (orCreateTextureTranslationInfo.ChangeTime == ChangeTime) { return false; } orCreateTextureTranslationInfo.Reset(); } int num = (int)texture.format; if (num == 1 || num == 9 || num == 63) { return false; } } return true; } } internal static class TranslationScopeHelper { public static bool EnableTranslationScoping = true; public static int GetScope(object ui) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (EnableTranslationScoping) { try { Component val = (Component)((ui is Component) ? ui : null); Scene val2; if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val)) { val2 = val.gameObject.scene; return ((Scene)(ref val2)).buildIndex; } if (ui is GUIContent) { return -1; } val2 = SceneManager.GetActiveScene(); return ((Scene)(ref val2)).buildIndex; } 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; } } } 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 var 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 readonly TranslationManager _translationManager; private readonly ConcurrentQueue<Action> _mainThreadActions; private readonly ConcurrentDictionary<string, byte> _immediatelyTranslating; private readonly ConcurrentDictionary<string, TextStabilizationContext> _stabilizationContexts; private readonly ConcurrentDictionary<string, ConcurrentDictionary<object, byte>> _pendingStabilizationUIs; public static AsyncTranslationManager Instance { get; } = new AsyncTranslationManager(); private AsyncTranslationManager() { _translationManager = new TranslationManager(); _mainThreadActions = new ConcurrentQueue<Action>(); _immediatelyTranslating = new ConcurrentDictionary<string, byte>(); _stabilizationContexts = new ConcurrentDictionary<string, TextStabilizationContext>(); _pendingStabilizationUIs = new ConcurrentDictionary<string, ConcurrentDictionary<object, byte>>(); _translationManager.JobCompleted += OnTranslationJobCompleted; _translationManager.JobFailed += OnTranslationJobFailed; TranslationEndpointManager translationEndpointManager = new TranslationEndpointManager(); _translationManager.RegisterEndpoint(translationEndpointManager); } public void Start() { } public void Stop() { _translationManager?.ClearAllJobs(); _stabilizationContexts.Clear(); } public void QueueTranslation(object ui, string originalText, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { if (string.IsNullOrWhiteSpace(originalText)) { return; } int scope = TranslationScopeHelper.GetScope(ui); try { if (info != null) { if (info.IsTranslated) { info.Reset(originalText); } else { info.OriginalText = originalText; } } string cachedTranslation = normalText?.TryGetCachedTranslation(originalText, scope); if (cachedTranslation != null) { if (TextTranslate.IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, cachedTranslation, originalText, info, TextTranslate.ChangeTime); }); } } else if (originalText.Length <= TranslatePlugin.syncTranslationThreshold.Value) { string translatedText = TranslationEndpointManager.TranslateText(originalText, normalText, config, scope); if (!string.IsNullOrEmpty(translatedText) && !translatedText.Equals(originalText) && TextTranslate.IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, translatedText, originalText, info, TextTranslate.ChangeTime); }); } } else { if (_translationManager?.PrimaryEndpoint == null) { return; } bool isTranslatable = normalText?.IsTranslatable(originalText, isToken: false, scope) ?? true; if (ShouldStabilizeText(ui, originalText)) { string text = TranslationEndpointManager.BuildKey(originalText, config, scope); if (_immediatelyTranslating.TryAdd(text, 0)) { StartTextStabilization(ui, originalText, info, normalText, config, text, scope); return; } string cached = normalText?.TryGetCachedTranslation(originalText, scope); if (cached != null && TextTranslate.IsUIObjectValid(ui)) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, cached, originalText, info, TextTranslate.ChangeTime); }); } else if (cached == null) { _pendingStabilizationUIs.GetOrAdd(text, (string _) => new ConcurrentDictionary<object, byte>()).TryAdd(ui, 0); } } else { _translationManager.PrimaryEndpoint.EnqueueTranslation(ui, originalText, info, normalText, config, isTranslatable); } } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("An unexpected error occurred in QueueTranslation: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private bool ShouldStabilizeText(object ui, string text) { if (ui == null) { return false; } if (!ui.SupportsStabilization()) { return false; } int num = TranslatePlugin.stabilizationMinTextLength?.Value ?? 100; if (num == 0) { return false; } return text.Length > num; } private void StartTextStabilization(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, string immKey, int scope) { TextStabilizationContext obj = new TextStabilizationContext { UI = ui, OriginalText = text, Info = info, NormalText = normalText, Config = config, StartTime = Time.realtimeSinceStartup, MaxTries = (((TranslatePlugin.stabilizationMaxRetries?.Value ?? 60) == 0) ? int.MaxValue : (TranslatePlugin.stabilizationMaxRetries?.Value ?? 60)), CurrentTries = 0 }; ConfigEntry<float> stabilizationDelay = TranslatePlugin.stabilizationDelay; obj.Delay = ((stabilizationDelay != null && stabilizationDelay.Value > 0f) ? TranslatePlugin.stabilizationDelay.Value : 0.9f); TextStabilizationContext context = obj; string stabilizationKey = GetStabilizationKey(ui, text); _stabilizationContexts[stabilizationKey] = context; object obj2 = ui; MonoBehaviour val = (MonoBehaviour)((obj2 is MonoBehaviour) ? obj2 : null); if (val != null) { ((MonoBehaviour)TranslatePlugin.Instance).StartCoroutine(WaitForTextStablization(ui, info, context.Delay, context.MaxTries, 0, delegate(string stabilizedText) { OnTextStabilized(context, stabilizedText); try { _stabilizationContexts.TryRemove(GetStabilizationKey(ui, context.OriginalText), out var _); _immediatelyTranslating.TryRemove(immKey, out var _); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error cleaning up stabilization context: " + ex.Message)); } }, delegate { OnStabilizationFailed(context); try { _stabilizationContexts.TryRemove(GetStabilizationKey(ui, context.OriginalText), out var _); _immediatelyTranslating.TryRemove(immKey, out var _); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error cleaning up stabilization context: " + ex.Message)); } })); } else { _stabilizationContexts.TryRemove(stabilizationKey, out var _); _immediatelyTranslating.TryRemove(immKey, out var _); } } private IEnumerator WaitForTextStablization(object ui, TextTranslationInfo info, float delay, int maxTries, int currentTries, Action<string> onTextStabilized, Action onMaxTriesExceeded) { yield return null; bool succeeded = false; while (currentTries < maxTries) { string beforeText; try { beforeText = GetUIText(ui, info); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error getting before text during stabilization: " + ex.Message)); break; } float realtimeSinceStartup = Time.realtimeSinceStartup; float end = realtimeSinceStartup + delay; while (Time.realtimeSinceStartup < end) { yield return null; } string uIText; try { uIText = GetUIText(ui, info); } catch (Exception ex2) { TranslatePlugin.logger.LogError((object)("Error getting after text during stabilization: " + ex2.Message)); break; } if (beforeText == uIText) { onTextStabilized(uIText); succeeded = true; break; } currentTries++; } if (!succeeded) { onMaxTriesExceeded(); } } private void OnTextStabilized(TextStabilizationContext context, string stabilizedText) { TextTranslationInfo info = context.Info; if (info != null && info.IsTranslated) { return; } context.Info?.Reset(stabilizedText); if (!string.IsNullOrWhiteSpace(stabilizedText)) { string text = context.NormalText?.TryGetCachedTranslation(stabilizedText, TranslationScopeHelper.GetScope(context.UI)); if (text != null) { SafeUpdateUI(context.UI, text, stabilizedText, context.Info, context.Info?.ChangeTime ?? TextTranslate.ChangeTime); return; } bool isTranslatable = context.NormalText == null || context.NormalText.IsTranslatable(stabilizedText, isToken: false); _translationManager.PrimaryEndpoint.EnqueueTranslation(context.UI, stabilizedText, context.Info, context.NormalText, context.Config, isTranslatable); } } private void OnStabilizationFailed(TextStabilizationContext context) { context.Info?.Reset(context.OriginalText); } public void ProcessMainThreadActions() { Action result; while (_mainThreadActions.TryDequeue(out result)) { try { result(); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error in main thread action: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } } private void OnTranslationJobCompleted(TranslationJob job) { try { string key = TranslationEndpointManager.BuildKey(job.OriginalText, job.Config, job.Scope); _immediatelyTranslating.TryRemove(key, out var _); if (string.IsNullOrEmpty(job.TranslatedText)) { return; } string final = job.TranslatedText; foreach (object ui in job.AssociatedUIs.Keys) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(ui, final, job.OriginalText, job.TranslationInfo, job.StartVersion); }); } if (!_pendingStabilizationUIs.TryRemove(key, out var value2)) { return; } foreach (KeyValuePair<object, byte> kvp in value2) { _mainThreadActions.Enqueue(delegate { SafeUpdateUI(kvp.Key, final, job.OriginalText, job.TranslationInfo, job.StartVersion); }); } } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error handling translation job completion: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private void OnTranslationJobFailed(TranslationJob job) { try { string key = TranslationEndpointManager.BuildKey(job.OriginalText, job.Config, job.Scope); _immediatelyTranslating.TryRemove(key, out var _); _pendingStabilizationUIs.TryRemove(key, out var _); TranslatePlugin.logger.LogWarning((object)("Translation failed for '" + job.OriginalText + "': " + job.ErrorMessage)); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)("Error handling translation job failure: " + ex.Message)); TranslatePlugin.logger.LogError((object)ex); } } private void SafeUpdateUI(object ui, string translatedText, string originalText, object translationInfo, long expectedVersion) { if (!TextTranslate.IsUIObjectValid(ui)) { return; } try { TextTranslationInfo textTranslationInfo = translationInfo as TextTranslationInfo; if (textTranslationInfo != null) { if (textTranslationInfo.IsCurrentlySettingText) { return; } string uIText = GetUIText(ui, textTranslationInfo); if ((textTranslationInfo.OriginalText != null && textTranslationInfo.OriginalText != originalText) || (uIText != originalText && uIText != textTranslationInfo.TranslatedText) || (textTranslationInfo.ChangeTime != expectedVersion && uIText != originalText)) { return; } } TextTranslate.Instance.SetTranslatedText(ui, translatedText, originalText, textTranslationInfo); } catch (NullReferenceException) { } catch (Exception ex2) { try { TranslatePlugin.logger.LogError((object)("Failed to safely update UI for text '" + originalText + "': " + ex2.Message)); } catch (IndexOutOfRangeException) { } TranslatePlugin.logger.LogError((object)ex2); } } private string GetUIText(object ui, TextTranslationInfo info) { try { if (ui == null) { return string.Emp