Decompiled source of Lethal Company Simplified Chinese Localization v1.8.33
BepInEx/core/XUnity.Common.dll
Decompiled 9 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Utils; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using XUnity.Common.Constants; using XUnity.Common.Extensions; using XUnity.Common.Logging; using XUnity.Common.Utilities; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("XUnity.AutoTranslator.Plugin.Core")] [assembly: AssemblyCompany("gravydevsupreme")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Common dependencies shared between XUnity Auto Translator and Resource Redirector.")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: AssemblyProduct("XUnity.Common")] [assembly: AssemblyTitle("XUnity.Common")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] namespace XUnity.Common.Utilities { public static class ArrayHelper { public static T[] Null<T>() { return null; } } public static class CabHelper { private static string CreateRandomCab() { return "CAB-" + Guid.NewGuid().ToString("N"); } public static void RandomizeCab(byte[] assetBundleData) { string @string = Encoding.ASCII.GetString(assetBundleData, 0, Math.Min(1024, assetBundleData.Length - 4)); int num = @string.IndexOf("CAB-", StringComparison.Ordinal); if (num >= 0) { int num2 = @string.Substring(num).IndexOf('\0'); if (num2 >= 0 && num2 <= 36) { string s = CreateRandomCab(); Buffer.BlockCopy(Encoding.ASCII.GetBytes(s), 36 - num2, assetBundleData, num, num2); } } } public static void RandomizeCabWithAnyLength(byte[] assetBundleData) { FindAndReplaceCab("CAB-", 0, assetBundleData, 2048); } private static void FindAndReplaceCab(string ansiStringToStartWith, byte byteToEndWith, byte[] data, int maxIterations = -1) { int num = Math.Min(data.Length, maxIterations); if (num == -1) { num = data.Length; } int num2 = 0; int length = ansiStringToStartWith.Length; string text = Guid.NewGuid().ToString("N"); int num3 = 0; for (int i = 0; i < num; i++) { char c = (char)data[i]; if (num2 == length) { while (data[i] != byteToEndWith && i < num) { if (num3 >= text.Length) { num3 = 0; text = Guid.NewGuid().ToString("N"); } data[i++] = (byte)text[num3++]; } break; } num2 = ((c == ansiStringToStartWith[num2]) ? (num2 + 1) : 0); } } } internal static class CecilFastReflectionHelper { private static readonly Type[] DynamicMethodDelegateArgs = new Type[2] { typeof(object), typeof(object[]) }; public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs); ILProcessor iLProcessor = val.GetILProcessor(); ParameterInfo[] parameters = method.GetParameters(); bool flag = true; if (!method.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); if (method.DeclaringType.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, method.DeclaringType); } } for (int i = 0; i < parameters.Length; i++) { Type type = parameters[i].ParameterType; bool isByRef = type.IsByRef; if (isByRef) { type = type.GetElementType(); } bool isValueType = type.IsValueType; if (isByRef && isValueType && !directBoxValueAccess) { iLProcessor.Emit(OpCodes.Ldarg_1); iLProcessor.Emit(OpCodes.Ldc_I4, i); } iLProcessor.Emit(OpCodes.Ldarg_1); iLProcessor.Emit(OpCodes.Ldc_I4, i); if (isByRef && !isValueType) { Extensions.Emit(iLProcessor, OpCodes.Ldelema, typeof(object)); continue; } iLProcessor.Emit(OpCodes.Ldelem_Ref); if (!isValueType) { continue; } if (!isByRef || !directBoxValueAccess) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, type); if (isByRef) { Extensions.Emit(iLProcessor, OpCodes.Box, type); iLProcessor.Emit(OpCodes.Dup); Extensions.Emit(iLProcessor, OpCodes.Unbox, type); if (flag) { flag = false; val.Definition.Body.Variables.Add(new VariableDefinition((TypeReference)new PinnedType((TypeReference)new PointerType(((MemberReference)val.Definition).Module.TypeSystem.Void)))); } iLProcessor.Emit(OpCodes.Stloc_0); iLProcessor.Emit(OpCodes.Stelem_Ref); iLProcessor.Emit(OpCodes.Ldloc_0); } } else { Extensions.Emit(iLProcessor, OpCodes.Unbox, type); } } if (method.IsConstructor) { Extensions.Emit(iLProcessor, OpCodes.Newobj, (MethodBase)(method as ConstructorInfo)); } else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall) { Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)(method as MethodInfo)); } else { Extensions.Emit(iLProcessor, OpCodes.Callvirt, (MethodBase)(method as MethodInfo)); } Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType); if ((object)type2 != typeof(void)) { if (type2.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Box, type2); } } else { iLProcessor.Emit(OpCodes.Ldnull); } iLProcessor.Emit(OpCodes.Ret); return (FastReflectionDelegate)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(FastReflectionDelegate)); } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if ((object)fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match return type."); } if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">", typeof(F), new Type[1] { typeof(T) }); ILProcessor iLProcessor = val.GetILProcessor(); if (!fieldInfo.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType); } Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo); if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType); } iLProcessor.Emit(OpCodes.Ret); return (Func<T, F>)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(Func<T, F>)); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) if ((object)fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match argument type."); } if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } DynamicMethodDefinition val = new DynamicMethodDefinition("FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">", (Type)null, new Type[2] { typeof(T), typeof(F) }); ILProcessor iLProcessor = val.GetILProcessor(); if (!fieldInfo.IsStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.DeclaringType); } iLProcessor.Emit(OpCodes.Ldarg_1); if ((object)fieldInfo.FieldType != typeof(F)) { if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { if (fieldInfo.FieldType.IsValueType) { Extensions.Emit(iLProcessor, OpCodes.Unbox_Any, fieldInfo.FieldType); } else { Extensions.Emit(iLProcessor, OpCodes.Box, fieldInfo.FieldType); } } else { Extensions.Emit(iLProcessor, OpCodes.Castclass, fieldInfo.FieldType); } } Extensions.Emit(iLProcessor, fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo); iLProcessor.Emit(OpCodes.Ret); return (Action<T, F>)Extensions.CreateDelegate((MethodBase)val.Generate(), typeof(Action<T, F>)); } } public static class CustomFastReflectionHelper { private struct FastReflectionDelegateKey { public MethodBase Method { get; } public bool DirectBoxValueAccess { get; } public bool ForceNonVirtCall { get; } public FastReflectionDelegateKey(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { Method = method; DirectBoxValueAccess = directBoxValueAccess; ForceNonVirtCall = forceNonVirtCall; } public override bool Equals(object obj) { if (obj is FastReflectionDelegateKey fastReflectionDelegateKey && EqualityComparer<MethodBase>.Default.Equals(Method, fastReflectionDelegateKey.Method) && DirectBoxValueAccess == fastReflectionDelegateKey.DirectBoxValueAccess) { return ForceNonVirtCall == fastReflectionDelegateKey.ForceNonVirtCall; } return false; } public override int GetHashCode() { return ((1017116076 * -1521134295 + EqualityComparer<MethodBase>.Default.GetHashCode(Method)) * -1521134295 + DirectBoxValueAccess.GetHashCode()) * -1521134295 + ForceNonVirtCall.GetHashCode(); } } private static readonly Dictionary<FastReflectionDelegateKey, FastReflectionDelegate> MethodCache = new Dictionary<FastReflectionDelegateKey, FastReflectionDelegate>(); public static FastReflectionDelegate CreateFastDelegate(this MethodBase method, bool directBoxValueAccess = true, bool forceNonVirtCall = false) { FastReflectionDelegateKey key = new FastReflectionDelegateKey(method, directBoxValueAccess, forceNonVirtCall); if (MethodCache.TryGetValue(key, out var value)) { return value; } value = (((object)ClrTypes.DynamicMethodDefinition == null) ? GetFastDelegateForSRE(method, directBoxValueAccess, forceNonVirtCall) : GetFastDelegateForCecil(method, directBoxValueAccess, forceNonVirtCall)); MethodCache.Add(key, value); return value; } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { if ((object)ClrTypes.DynamicMethodDefinition != null) { return CreateFastFieldGetterForCecil<T, F>(fieldInfo); } return CreateFastFieldGetterForSRE<T, F>(fieldInfo); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { if ((object)ClrTypes.DynamicMethodDefinition != null) { return CreateFastFieldSetterForCecil<T, F>(fieldInfo); } return CreateFastFieldSetterForSRE<T, F>(fieldInfo); } private static FastReflectionDelegate GetFastDelegateForCecil(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { try { return CecilFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e) { try { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e2) { XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (object target, object[] args) => method.Invoke(target, args); } } } private static Func<T, F> CreateFastFieldGetterForCecil<T, F>(FieldInfo fieldInfo) { try { return CecilFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e) { try { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e2) { XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (T target) => (F)fieldInfo.GetValue(target); } } } private static Action<T, F> CreateFastFieldSetterForCecil<T, F>(FieldInfo fieldInfo) { try { return CecilFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e) { try { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with cecil. Retrying with reflection emit..."); return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e2) { XuaLogger.Common.Warn(e2, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return delegate(T target, F value) { fieldInfo.SetValue(target, value); }; } } } private static FastReflectionDelegate GetFastDelegateForSRE(MethodBase method, bool directBoxValueAccess, bool forceNonVirtCall) { try { return ReflectionEmitFastReflectionHelper.CreateFastDelegate(method, directBoxValueAccess, forceNonVirtCall); } catch (Exception e) { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (object target, object[] args) => method.Invoke(target, args); } } private static Func<T, F> CreateFastFieldGetterForSRE<T, F>(FieldInfo fieldInfo) { try { return ReflectionEmitFastReflectionHelper.CreateFastFieldGetter<T, F>(fieldInfo); } catch (Exception e) { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return (T target) => (F)fieldInfo.GetValue(target); } } private static Action<T, F> CreateFastFieldSetterForSRE<T, F>(FieldInfo fieldInfo) { try { return ReflectionEmitFastReflectionHelper.CreateFastFieldSetter<T, F>(fieldInfo); } catch (Exception e) { XuaLogger.Common.Warn(e, "Failed creating fast reflection delegate through with reflection emit. Falling back to standard reflection..."); return delegate(T target, F value) { fieldInfo.SetValue(target, value); }; } } } public static class DiacriticHelper { public static string RemoveAllDiacritics(this string input) { return new string((from c in input.SafeNormalize(NormalizationForm.FormD) where CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark select c).ToArray()).SafeNormalize(); } private static string SafeNormalize(this string input, NormalizationForm normalizationForm = NormalizationForm.FormC) { return ReplaceNonCharacters(input, '?').Normalize(normalizationForm); } private static string ReplaceNonCharacters(string input, char replacement) { StringBuilder stringBuilder = new StringBuilder(input.Length); for (int i = 0; i < input.Length; i++) { if (char.IsSurrogatePair(input, i)) { int num = char.ConvertToUtf32(input, i); i++; if (IsValidCodePoint(num)) { stringBuilder.Append(char.ConvertFromUtf32(num)); } else { stringBuilder.Append(replacement); } } else { char c = input[i]; if (IsValidCodePoint(c)) { stringBuilder.Append(c); } else { stringBuilder.Append(replacement); } } } return stringBuilder.ToString(); } private static bool IsValidCodePoint(int point) { if (point >= 64976) { if (point >= 65008 && (point & 0xFFFF) != 65535 && (point & 0xFFFE) != 65534) { return point <= 1114111; } return false; } return true; } } public static class ExpressionHelper { public static Delegate CreateTypedFastInvoke(MethodBase method) { if ((object)method == null) { throw new ArgumentNullException("method"); } return CreateTypedFastInvokeUnchecked(method); } public static Delegate CreateTypedFastInvokeUnchecked(MethodBase method) { if ((object)method == null) { return null; } if (method.IsGenericMethod) { throw new ArgumentException("The provided method must not be generic.", "method"); } if (method is MethodInfo methodInfo) { Expression[] arguments; if (method.IsStatic) { ParameterExpression[] array = (from p in methodInfo.GetParameters() select Expression.Parameter(p.ParameterType, p.Name)).ToArray(); arguments = array; return Expression.Lambda(Expression.Call(null, methodInfo, arguments), array).Compile(); } List<ParameterExpression> list = (from p in methodInfo.GetParameters() select Expression.Parameter(p.ParameterType, p.Name)).ToList(); list.Insert(0, Expression.Parameter(methodInfo.DeclaringType, "instance")); ParameterExpression instance = list[0]; arguments = list.Skip(1).ToArray(); return Expression.Lambda(Expression.Call(instance, methodInfo, arguments), list.ToArray()).Compile(); } if (method is ConstructorInfo constructorInfo) { ParameterExpression[] array2 = (from p in constructorInfo.GetParameters() select Expression.Parameter(p.ParameterType, p.Name)).ToArray(); Expression[] arguments = array2; return Expression.Lambda(Expression.New(constructorInfo, arguments), array2).Compile(); } throw new ArgumentException("method", "This method only supports MethodInfo and ConstructorInfo."); } } public static class ExtensionDataHelper { private static readonly object Sync; private static readonly WeakDictionary<object, object> WeakDynamicFields; public static int WeakReferenceCount { get { lock (Sync) { return WeakDynamicFields.Count; } } } static ExtensionDataHelper() { Sync = new object(); WeakDynamicFields = new WeakDictionary<object, object>(); MaintenanceHelper.AddMaintenanceFunction(Cull, 12); } public static void SetExtensionData<T>(this object obj, T t) { lock (Sync) { if (WeakDynamicFields.TryGetValue(obj, out var value)) { if (value is Dictionary<Type, object> dictionary) { dictionary[typeof(T)] = t; return; } Dictionary<Type, object> dictionary2 = new Dictionary<Type, object>(); dictionary2.Add(value.GetType(), value); dictionary2[typeof(T)] = t; WeakDynamicFields[obj] = dictionary2; } else { WeakDynamicFields[obj] = t; } } } public static T GetOrCreateExtensionData<T>(this object obj) where T : new() { if (obj == null) { return default(T); } lock (Sync) { if (WeakDynamicFields.TryGetValue(obj, out var value)) { if (value is Dictionary<Type, object> dictionary) { if (dictionary.TryGetValue(typeof(T), out value)) { return (T)value; } T val = new T(); dictionary[typeof(T)] = val; return val; } if (!(value is T result)) { Dictionary<Type, object> dictionary2 = new Dictionary<Type, object>(); dictionary2.Add(value.GetType(), value); T val2 = new T(); dictionary2[typeof(T)] = val2; WeakDynamicFields[obj] = dictionary2; return val2; } return result; } T val3 = new T(); WeakDynamicFields[obj] = val3; return val3; } } public static T GetExtensionData<T>(this object obj) { if (obj == null) { return default(T); } lock (Sync) { if (WeakDynamicFields.TryGetValue(obj, out var value)) { if (value is Dictionary<Type, object> dictionary && dictionary.TryGetValue(typeof(T), out value)) { if (!(value is T result)) { return default(T); } return result; } if (!(value is T result2)) { return default(T); } return result2; } } return default(T); } public static void Cull() { lock (Sync) { WeakDynamicFields.RemoveCollectedEntries(); } } public static List<KeyValuePair<object, object>> GetAllRegisteredObjects() { lock (Sync) { return IterateAllPairs().ToList(); } } public static void Remove(object obj) { lock (Sync) { WeakDynamicFields.Remove(obj); } } private static IEnumerable<KeyValuePair<object, object>> IterateAllPairs() { foreach (KeyValuePair<object, object> kvp in WeakDynamicFields) { if (kvp.Value is Dictionary<Type, object> dictionary) { foreach (KeyValuePair<Type, object> item in dictionary) { yield return new KeyValuePair<object, object>(kvp.Key, item.Value); } } else { yield return kvp; } } } } public delegate object FastReflectionDelegate(object target, params object[] args); public static class HookingHelper { private static readonly MethodInfo PatchMethod12; private static readonly MethodInfo PatchMethod20; private static readonly object Harmony; private static bool _loggedHarmonyError; static HookingHelper() { PatchMethod12 = ClrTypes.HarmonyInstance?.GetMethod("Patch", new Type[4] { ClrTypes.MethodBase, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod }); PatchMethod20 = ClrTypes.Harmony?.GetMethod("Patch", new Type[5] { ClrTypes.MethodBase, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod }); _loggedHarmonyError = false; try { if ((object)ClrTypes.HarmonyInstance != null) { Harmony = ClrTypes.HarmonyInstance.GetMethod("Create", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[1] { "xunity.common.hookinghelper" }); } else if ((object)ClrTypes.Harmony != null) { Harmony = ClrTypes.Harmony.GetConstructor(new Type[1] { typeof(string) }).Invoke(new object[1] { "xunity.common.hookinghelper" }); } else { XuaLogger.Common.Error("An unexpected exception occurred during harmony initialization, likely caused by unknown Harmony version. Harmony hooks will be unavailable!"); } } catch (Exception e) { XuaLogger.Common.Error(e, "An unexpected exception occurred during harmony initialization. Harmony hooks will be unavailable!"); } } public static void PatchAll(IEnumerable<Type> types, bool forceExternHooks) { foreach (Type type in types) { PatchType(type, forceExternHooks); } } public static void PatchAll(IEnumerable<Type[]> types, bool forceMonoModHooks) { foreach (Type[] type in types) { for (int i = 0; i < type.Length && !PatchType(type[i], forceMonoModHooks); i++) { } } } public static bool PatchType(Type type, bool forceExternHooks) { MethodBase methodBase = null; IntPtr intPtr = IntPtr.Zero; try { if (Harmony == null && !_loggedHarmonyError) { _loggedHarmonyError = true; XuaLogger.Common.Warn("Harmony is not loaded or could not be initialized. Using fallback hooks instead."); } BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo method = type.GetMethod("Prepare", bindingAttr); if ((object)method == null || (bool)method.Invoke(null, new object[1] { Harmony })) { try { methodBase = (MethodBase)(type.GetMethod("TargetMethod", bindingAttr)?.Invoke(null, new object[1] { Harmony })); } catch { } try { intPtr = ((IntPtr?)type.GetMethod("TargetMethodPointer", bindingAttr)?.Invoke(null, null)) ?? IntPtr.Zero; } catch { } if ((object)methodBase == null && intPtr == IntPtr.Zero) { if ((object)methodBase != null) { XuaLogger.Common.Warn("Could not hook '" + methodBase.DeclaringType.FullName + "." + methodBase.Name + "'. Likely due differences between different versions of the engine or text framework."); } else { XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework."); } return false; } MethodInfo method2 = type.GetMethod("Prefix", bindingAttr); MethodInfo method3 = type.GetMethod("Postfix", bindingAttr); MethodInfo method4 = type.GetMethod("Finalizer", bindingAttr); if ((object)methodBase == null || forceExternHooks || Harmony == null || ((object)method2 == null && (object)method3 == null && (object)method4 == null)) { return PatchWithExternHooks(type, methodBase, intPtr, forced: true); } if ((object)methodBase != null) { try { int? priority = type.GetCustomAttributes(typeof(HookingHelperPriorityAttribute), inherit: false).OfType<HookingHelperPriorityAttribute>().FirstOrDefault()?.priority; object obj3 = (((object)method2 != null) ? CreateHarmonyMethod(method2, priority) : null); object obj4 = (((object)method3 != null) ? CreateHarmonyMethod(method3, priority) : null); object obj5 = (((object)method4 != null) ? CreateHarmonyMethod(method4, priority) : null); if ((object)PatchMethod12 != null) { PatchMethod12.Invoke(Harmony, new object[4] { methodBase, obj3, obj4, null }); } else { PatchMethod20.Invoke(Harmony, new object[5] { methodBase, obj3, obj4, null, obj5 }); } XuaLogger.Common.Debug("Hooked " + methodBase.DeclaringType.FullName + "." + methodBase.Name + " through Harmony hooks."); return true; } catch (Exception e) when (((Func<bool>)delegate { // Could not convert BlockContainer to single expression System.Runtime.CompilerServices.Unsafe.SkipInit(out int num); if (e.FirstInnerExceptionOfType<PlatformNotSupportedException>() == null) { ArgumentException ex = e.FirstInnerExceptionOfType<ArgumentException>(); num = ((ex != null && ex.Message?.Contains("no body") == true) ? 1 : 0); } else { num = 1; } return num != 0; }).Invoke()) { return PatchWithExternHooks(type, methodBase, intPtr, forced: false); } } XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework."); } } catch (Exception e2) { if ((object)methodBase != null) { XuaLogger.Common.Warn(e2, "An error occurred while patching property/method '" + methodBase.DeclaringType.FullName + "." + methodBase.Name + "'. Failing hook: '" + type.Name + "'."); } else { XuaLogger.Common.Warn(e2, "An error occurred while patching property/method. Failing hook: '" + type.Name + "'."); } } return false; } private static bool PatchWithExternHooks(Type type, MethodBase original, IntPtr originalPtr, bool forced) { BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if ((object)ClrTypes.Imports != null) { if (originalPtr == IntPtr.Zero) { XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework."); return false; } IntPtr? intPtr = type.GetMethod("ML_Detour", bindingAttr)?.MethodHandle.GetFunctionPointer(); if (intPtr.HasValue && intPtr.Value != IntPtr.Zero) { ClrTypes.Imports.GetMethod("Hook", bindingAttr).Invoke(null, new object[2] { originalPtr, intPtr.Value }); XuaLogger.Common.Debug("Hooked " + type.Name + " through MelonMod Imports.Hook method."); return true; } XuaLogger.Common.Warn("Could not hook '" + type.Name + "' because no detour method was found."); } else { if ((object)original == null) { XuaLogger.Common.Warn("Cannot hook '" + type.Name + "'. Could not locate the original method. Failing hook: '" + type.Name + "'."); return false; } if ((object)ClrTypes.Hook == null || (object)ClrTypes.NativeDetour == null) { XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. MonoMod hooks is not supported in this runtime as MonoMod is not loaded. Failing hook: '" + type.Name + "'."); return false; } object obj = type.GetMethod("Get_MM_Detour", bindingAttr)?.Invoke(null, null) ?? type.GetMethod("MM_Detour", bindingAttr); if (obj != null) { string text = "(managed)"; object obj2; try { obj2 = ClrTypes.Hook.GetConstructor(new Type[2] { typeof(MethodBase), typeof(MethodInfo) }).Invoke(new object[2] { original, obj }); obj2.GetType().GetMethod("Apply").Invoke(obj2, null); } catch (Exception e) when (((Func<bool>)delegate { // Could not convert BlockContainer to single expression System.Runtime.CompilerServices.Unsafe.SkipInit(out int num); if (e.FirstInnerExceptionOfType<NullReferenceException>() == null) { NotSupportedException ex = e.FirstInnerExceptionOfType<NotSupportedException>(); num = ((ex != null && ex.Message?.Contains("Body-less") == true) ? 1 : 0); } else { num = 1; } return num != 0; }).Invoke()) { text = "(native)"; obj2 = ClrTypes.NativeDetour.GetConstructor(new Type[2] { typeof(MethodBase), typeof(MethodBase) }).Invoke(new object[2] { original, obj }); obj2.GetType().GetMethod("Apply").Invoke(obj2, null); } type.GetMethod("MM_Init", bindingAttr)?.Invoke(null, new object[1] { obj2 }); if (forced) { XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through forced MonoMod hooks. " + text); } else { XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through MonoMod hooks. " + text); } return true; } if (forced) { XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'."); } else { XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'."); } } return false; } private static object CreateHarmonyMethod(MethodInfo method, int? priority) { object obj = ClrTypes.HarmonyMethod.GetConstructor(new Type[1] { typeof(MethodInfo) }).Invoke(new object[1] { method }); if (priority.HasValue) { (ClrTypes.HarmonyMethod.GetField("priority", BindingFlags.Instance | BindingFlags.Public) ?? ClrTypes.HarmonyMethod.GetField("prioritiy", BindingFlags.Instance | BindingFlags.Public)).SetValue(obj, priority.Value); } return obj; } } public class HookingHelperPriorityAttribute : Attribute { public int priority; public HookingHelperPriorityAttribute(int priority) { this.priority = priority; } } public static class HookPriority { public const int Last = 0; public const int VeryLow = 100; public const int Low = 200; public const int LowerThanNormal = 300; public const int Normal = 400; public const int HigherThanNormal = 500; public const int High = 600; public const int VeryHigh = 700; public const int First = 800; } public static class ListExtensions { public static void BinarySearchInsert<T>(this List<T> items, T item) where T : IComparable<T> { int num = items.BinarySearch(item); if (num < 0) { items.Insert(~num, item); } else { items.Insert(num, item); } } } public static class MaintenanceHelper { private class ActionRegistration { public Action Action { get; } public int Filter { get; } public ActionRegistration(Action action, int filter) { Action = action; Filter = filter; } } private static readonly object Sync = new object(); private static readonly List<ActionRegistration> RegisteredActions = new List<ActionRegistration>(); private static bool _initialized; public static void AddMaintenanceFunction(Action action, int filter) { lock (Sync) { if (!_initialized) { _initialized = true; StartMaintenance(); } ActionRegistration item = new ActionRegistration(action, filter); RegisteredActions.Add(item); } } private static void StartMaintenance() { Thread thread = new Thread(MaintenanceLoop); thread.IsBackground = true; thread.Start(); } private static void MaintenanceLoop(object state) { int num = 0; while (true) { lock (Sync) { foreach (ActionRegistration registeredAction in RegisteredActions) { if (num % registeredAction.Filter == 0) { try { registeredAction.Action(); } catch (Exception e) { XuaLogger.Common.Error(e, "An unexpected error occurred during maintenance."); } } } } num++; Thread.Sleep(5000); } } } public static class Paths { private static string _gameRoot; public static string GameRoot { get { return _gameRoot ?? GetAndSetGameRoot(); } set { _gameRoot = value; } } public static void Initialize() { GetAndSetGameRoot(); } private static string GetAndSetGameRoot() { return _gameRoot = new DirectoryInfo(Application.dataPath).Parent.FullName; } } public static class ReflectionCache { private struct MemberLookupKey { public Type Type { get; set; } public string MemberName { get; set; } public MemberLookupKey(Type type, string memberName) { Type = type; MemberName = memberName; } public override bool Equals(object obj) { if (obj is MemberLookupKey memberLookupKey) { if ((object)Type == memberLookupKey.Type) { return MemberName == memberLookupKey.MemberName; } return false; } return false; } public override int GetHashCode() { return Type.GetHashCode() + MemberName.GetHashCode(); } } private static Dictionary<MemberLookupKey, CachedMethod> Methods = new Dictionary<MemberLookupKey, CachedMethod>(); private static Dictionary<MemberLookupKey, CachedProperty> Properties = new Dictionary<MemberLookupKey, CachedProperty>(); private static Dictionary<MemberLookupKey, CachedField> Fields = new Dictionary<MemberLookupKey, CachedField>(); public static CachedMethod CachedMethod(this Type type, string name) { return type.CachedMethod(name, (Type[])null); } public static CachedMethod CachedMethod(this Type type, string name, params Type[] types) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Methods.TryGetValue(key, out var value)) { Type type2 = type; MethodInfo methodInfo = null; while ((object)methodInfo == null && (object)type2 != null) { methodInfo = ((types != null && types.Length != 0) ? type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) : type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); type2 = type2.BaseType; } if ((object)methodInfo != null) { value = new CachedMethod(methodInfo); } Methods[key] = value; } return value; } public static CachedProperty CachedProperty(this Type type, string name) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Properties.TryGetValue(key, out var value)) { Type type2 = type; PropertyInfo propertyInfo = null; while ((object)propertyInfo == null && (object)type2 != null) { propertyInfo = type2.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } if ((object)propertyInfo != null) { value = new CachedProperty(propertyInfo); } Properties[key] = value; } return value; } public static CachedField CachedField(this Type type, string name) { MemberLookupKey key = new MemberLookupKey(type, name); if (!Fields.TryGetValue(key, out var value)) { Type type2 = type; FieldInfo fieldInfo = null; while ((object)fieldInfo == null && (object)type2 != null) { fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } if ((object)fieldInfo != null) { value = new CachedField(fieldInfo); } Fields[key] = value; } return value; } public static CachedField CachedFieldByIndex(this Type type, int index, Type fieldType, BindingFlags flags) { FieldInfo[] array = (from x in type.GetFields(flags) where (object)x.FieldType == fieldType select x).ToArray(); if (index < array.Length) { return new CachedField(array[index]); } return null; } } public class CachedMethod { private static readonly object[] Args0 = new object[0]; private static readonly object[] Args1 = new object[1]; private static readonly object[] Args2 = new object[2]; private FastReflectionDelegate _invoke; internal CachedMethod(MethodInfo method) { _invoke = method.CreateFastDelegate(); } public object Invoke(object instance, object[] arguments) { return _invoke(instance, arguments); } public object Invoke(object instance) { return _invoke(instance, Args0); } public object Invoke(object instance, object arg1) { try { Args1[0] = arg1; return _invoke(instance, Args1); } finally { Args1[0] = null; } } public object Invoke(object instance, object arg1, object arg2) { try { Args2[0] = arg1; Args2[1] = arg2; return _invoke(instance, Args2); } finally { Args2[0] = null; Args2[1] = null; } } } public class CachedProperty { private static readonly object[] Args0 = new object[0]; private static readonly object[] Args1 = new object[1]; private FastReflectionDelegate _set; private FastReflectionDelegate _get; public Type PropertyType { get; } internal CachedProperty(PropertyInfo propertyInfo) { if (propertyInfo.CanRead) { _get = propertyInfo.GetGetMethod(nonPublic: true).CreateFastDelegate(); } if (propertyInfo.CanWrite) { _set = propertyInfo.GetSetMethod(nonPublic: true).CreateFastDelegate(); } PropertyType = propertyInfo.PropertyType; } public void Set(object instance, object[] arguments) { if (_set != null) { _set(instance, arguments); } } public void Set(object instance, object arg1) { if (_set == null) { return; } try { Args1[0] = arg1; _set(instance, Args1); } finally { Args1[0] = null; } } public object Get(object instance, object[] arguments) { if (_get == null) { return null; } return _get(instance, arguments); } public object Get(object instance) { if (_get == null) { return null; } return _get(instance, Args0); } } public class CachedField { private Func<object, object> _get; private Action<object, object> _set; public Type FieldType { get; } internal CachedField(FieldInfo fieldInfo) { _get = CustomFastReflectionHelper.CreateFastFieldGetter<object, object>(fieldInfo); _set = CustomFastReflectionHelper.CreateFastFieldSetter<object, object>(fieldInfo); FieldType = fieldInfo.FieldType; } public void Set(object instance, object value) { if (_set != null) { _set(instance, value); } } public object Get(object instance) { if (_get == null) { return null; } return _get(instance); } } internal static class ReflectionEmitFastReflectionHelper { private static readonly Type[] DynamicMethodDelegateArgs = new Type[2] { typeof(object), typeof(object[]) }; public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall) { DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs, method.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); ParameterInfo[] parameters = method.GetParameters(); bool flag = true; if (!method.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); if (method.DeclaringType.IsValueType) { iLGenerator.Emit(OpCodes.Unbox_Any, method.DeclaringType); } } for (int i = 0; i < parameters.Length; i++) { Type type = parameters[i].ParameterType; bool isByRef = type.IsByRef; if (isByRef) { type = type.GetElementType(); } bool isValueType = type.IsValueType; if (isByRef && isValueType && !directBoxValueAccess) { iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Ldc_I4, i); } iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Ldc_I4, i); if (isByRef && !isValueType) { iLGenerator.Emit(OpCodes.Ldelema, typeof(object)); continue; } iLGenerator.Emit(OpCodes.Ldelem_Ref); if (!isValueType) { continue; } if (!isByRef || !directBoxValueAccess) { iLGenerator.Emit(OpCodes.Unbox_Any, type); if (isByRef) { iLGenerator.Emit(OpCodes.Box, type); iLGenerator.Emit(OpCodes.Dup); iLGenerator.Emit(OpCodes.Unbox, type); if (flag) { flag = false; throw new NotImplementedException("No idea how to implement this..."); } iLGenerator.Emit(OpCodes.Stloc_0); iLGenerator.Emit(OpCodes.Stelem_Ref); iLGenerator.Emit(OpCodes.Ldloc_0); } } else { iLGenerator.Emit(OpCodes.Unbox, type); } } if (method.IsConstructor) { iLGenerator.Emit(OpCodes.Newobj, method as ConstructorInfo); } else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall) { iLGenerator.Emit(OpCodes.Call, method as MethodInfo); } else { iLGenerator.Emit(OpCodes.Callvirt, method as MethodInfo); } Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType); if ((object)type2 != typeof(void)) { if (type2.IsValueType) { iLGenerator.Emit(OpCodes.Box, type2); } } else { iLGenerator.Emit(OpCodes.Ldnull); } iLGenerator.Emit(OpCodes.Ret); return (FastReflectionDelegate)dynamicMethod.CreateDelegate(typeof(FastReflectionDelegate)); } public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo) { if ((object)fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match return type."); } if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">", typeof(F), new Type[1] { typeof(T) }, fieldInfo.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); if (!fieldInfo.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); } iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo); if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType); } iLGenerator.Emit(OpCodes.Ret); return (Func<T, F>)dynamicMethod.CreateDelegate(typeof(Func<T, F>)); } public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo) { if ((object)fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match argument type."); } if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">", null, new Type[2] { typeof(T), typeof(F) }, fieldInfo.DeclaringType.Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); if (!fieldInfo.IsStatic) { iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); } iLGenerator.Emit(OpCodes.Ldarg_1); if ((object)fieldInfo.FieldType != typeof(F)) { if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType) { if (fieldInfo.FieldType.IsValueType) { iLGenerator.Emit(OpCodes.Unbox, fieldInfo.FieldType); } else { iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType); } } else { iLGenerator.Emit(OpCodes.Castclass, fieldInfo.FieldType); } } iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo); iLGenerator.Emit(OpCodes.Ret); return (Action<T, F>)dynamicMethod.CreateDelegate(typeof(Action<T, F>)); } } public static class TimeHelper { public static float realtimeSinceStartup => Time.realtimeSinceStartup; } public class UnityObjectReferenceComparer : IEqualityComparer<object> { public static readonly UnityObjectReferenceComparer Default = new UnityObjectReferenceComparer(); public new bool Equals(object x, object y) { return x == y; } public int GetHashCode(object obj) { return obj.GetHashCode(); } } public class WeakReference<T> : WeakReference where T : class { public new T Target => (T)base.Target; public static WeakReference<T> Create(T target) { if (target == null) { return WeakNullReference<T>.Singleton; } return new WeakReference<T>(target); } protected WeakReference(T target) : base(target, trackResurrection: false) { } } internal class WeakNullReference<T> : WeakReference<T> where T : class { public static readonly WeakNullReference<T> Singleton = new WeakNullReference<T>(); public override bool IsAlive => true; private WeakNullReference() : base((T)null) { } } internal sealed class WeakKeyReference<T> : WeakReference<T> where T : class { public readonly int HashCode; public WeakKeyReference(T key, WeakKeyComparer<T> comparer) : base(key) { HashCode = comparer.GetHashCode(key); } } internal sealed class WeakKeyComparer<T> : IEqualityComparer<object> where T : class { private IEqualityComparer<T> comparer; internal WeakKeyComparer(IEqualityComparer<T> comparer) { if (comparer == null) { comparer = EqualityComparer<T>.Default; } this.comparer = comparer; } public int GetHashCode(object obj) { if (obj is WeakKeyReference<T> weakKeyReference) { return weakKeyReference.HashCode; } return comparer.GetHashCode((T)obj); } public new bool Equals(object x, object y) { bool isDead; T target = GetTarget(x, out isDead); bool isDead2; T target2 = GetTarget(y, out isDead2); if (isDead) { if (!isDead2) { return false; } return x == y; } if (isDead2) { return false; } return comparer.Equals(target, target2); } private static T GetTarget(object obj, out bool isDead) { T result; if (obj is WeakKeyReference<T> weakKeyReference) { result = weakKeyReference.Target; isDead = !weakKeyReference.IsAlive; } else { result = (T)obj; isDead = false; } return result; } } public sealed class WeakDictionary<TKey, TValue> : BaseDictionary<TKey, TValue> where TKey : class { private Dictionary<object, TValue> dictionary; private WeakKeyComparer<TKey> comparer; public override int Count => dictionary.Count; public WeakDictionary() : this(0, (IEqualityComparer<TKey>)null) { } public WeakDictionary(int capacity) : this(capacity, (IEqualityComparer<TKey>)null) { } public WeakDictionary(IEqualityComparer<TKey> comparer) : this(0, comparer) { } public WeakDictionary(int capacity, IEqualityComparer<TKey> comparer) { this.comparer = new WeakKeyComparer<TKey>(comparer); dictionary = new Dictionary<object, TValue>(capacity, this.comparer); } public override void Add(TKey key, TValue value) { if (key == null) { throw new ArgumentNullException("key"); } WeakReference<TKey> key2 = new WeakKeyReference<TKey>(key, comparer); dictionary.Add(key2, value); } public override bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } public override bool Remove(TKey key) { return dictionary.Remove(key); } public override bool TryGetValue(TKey key, out TValue value) { if (dictionary.TryGetValue(key, out value)) { return true; } value = default(TValue); return false; } protected override void SetValue(TKey key, TValue value) { WeakReference<TKey> key2 = new WeakKeyReference<TKey>(key, comparer); dictionary[key2] = value; } public override void Clear() { dictionary.Clear(); } public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { foreach (KeyValuePair<object, TValue> item in dictionary) { WeakReference<TKey> obj = (WeakReference<TKey>)item.Key; TValue value = item.Value; TKey target = obj.Target; if (obj.IsAlive) { yield return new KeyValuePair<TKey, TValue>(target, value); } } } public void RemoveCollectedEntries() { List<object> list = null; foreach (KeyValuePair<object, TValue> item in dictionary) { WeakReference<TKey> weakReference = (WeakReference<TKey>)item.Key; if (!weakReference.IsAlive) { if (list == null) { list = new List<object>(); } list.Add(weakReference); } } if (list == null) { return; } foreach (object item2 in list) { dictionary.Remove(item2); } } } [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")] public abstract class BaseDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable { private abstract class Collection<T> : ICollection<T>, IEnumerable<T>, IEnumerable { protected readonly IDictionary<TKey, TValue> dictionary; public int Count => dictionary.Count; public bool IsReadOnly => true; protected Collection(IDictionary<TKey, TValue> dictionary) { this.dictionary = dictionary; } public void CopyTo(T[] array, int arrayIndex) { BaseDictionary<TKey, TValue>.Copy((ICollection<T>)this, array, arrayIndex); } public virtual bool Contains(T item) { using (IEnumerator<T> enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { T current = enumerator.Current; if (EqualityComparer<T>.Default.Equals(current, item)) { return true; } } } return false; } public IEnumerator<T> GetEnumerator() { foreach (KeyValuePair<TKey, TValue> item in dictionary) { yield return GetItem(item); } } protected abstract T GetItem(KeyValuePair<TKey, TValue> pair); public bool Remove(T item) { throw new NotSupportedException("Collection is read-only."); } public void Add(T item) { throw new NotSupportedException("Collection is read-only."); } public void Clear() { throw new NotSupportedException("Collection is read-only."); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryKeyCollectionDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")] private class KeyCollection : Collection<TKey> { public KeyCollection(IDictionary<TKey, TValue> dictionary) : base(dictionary) { } protected override TKey GetItem(KeyValuePair<TKey, TValue> pair) { return pair.Key; } public override bool Contains(TKey item) { return dictionary.ContainsKey(item); } } [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy("System.Collections.Generic.Mscorlib_DictionaryValueCollectionDebugView`2,mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089")] private class ValueCollection : Collection<TValue> { public ValueCollection(IDictionary<TKey, TValue> dictionary) : base(dictionary) { } protected override TValue GetItem(KeyValuePair<TKey, TValue> pair) { return pair.Value; } } private const string PREFIX = "System.Collections.Generic.Mscorlib_"; private const string SUFFIX = ",mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"; private KeyCollection keys; private ValueCollection values; public abstract int Count { get; } public bool IsReadOnly => false; public ICollection<TKey> Keys { get { if (keys == null) { keys = new KeyCollection(this); } return keys; } } public ICollection<TValue> Values { get { if (values == null) { values = new ValueCollection(this); } return values; } } public TValue this[TKey key] { get { if (!TryGetValue(key, out var value)) { throw new KeyNotFoundException(); } return value; } set { SetValue(key, value); } } public abstract void Clear(); public abstract void Add(TKey key, TValue value); public abstract bool ContainsKey(TKey key); public abstract bool Remove(TKey key); public abstract bool TryGetValue(TKey key, out TValue value); public abstract IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator(); protected abstract void SetValue(TKey key, TValue value); public void Add(KeyValuePair<TKey, TValue> item) { Add(item.Key, item.Value); } public bool Contains(KeyValuePair<TKey, TValue> item) { if (!TryGetValue(item.Key, out var value)) { return false; } return EqualityComparer<TValue>.Default.Equals(value, item.Value); } public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { Copy(this, array, arrayIndex); } public bool Remove(KeyValuePair<TKey, TValue> item) { if (!Contains(item)) { return false; } return Remove(item.Key); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private static void Copy<T>(ICollection<T> source, T[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException("array"); } if (arrayIndex < 0 || arrayIndex > array.Length) { throw new ArgumentOutOfRangeException("arrayIndex"); } if (array.Length - arrayIndex < source.Count) { throw new ArgumentException("Destination array is not large enough. Check array.Length and arrayIndex."); } foreach (T item in source) { array[arrayIndex++] = item; } } } } namespace XUnity.Common.MonoMod { public static class DetourExtensions { public static T GenerateTrampolineEx<T>(this object detour) { return (T)(from x in detour.GetType().GetMethods() where x.Name == "GenerateTrampoline" && x.IsGenericMethod select x).FirstOrDefault().MakeGenericMethod(typeof(T)).Invoke(detour, null); } } } namespace XUnity.Common.Logging { internal class ConsoleLogger : XuaLogger { public ConsoleLogger(string source) : base(source) { } protected override void Log(LogLevel level, string message) { Console.WriteLine(GetDefaultPrefix(level) + " " + message); } } public enum LogLevel { Debug, Info, Warn, Error } internal class ModLoaderSpecificLogger : XuaLogger { public static class BepInExLogLevel { public const int None = 0; public const int Fatal = 1; public const int Error = 2; public const int Warning = 4; public const int Message = 8; public const int Info = 16; public const int Debug = 32; public const int All = 63; } private static Action<LogLevel, string> _logMethod; public ModLoaderSpecificLogger(string source) : base(source) { if (_logMethod != null) { return; } BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public; BindingFlags bindingAttr2 = BindingFlags.Instance | BindingFlags.Public; Type type = Type.GetType("BepInEx.Logging.LogLevel, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.LogLevel, BepInEx.Core", throwOnError: false); if ((object)type != null) { if ((object)(Type.GetType("BepInEx.Logging.ManualLogSource, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.ManualLogSource, BepInEx.Core", throwOnError: false)) != null) { MethodInfo method = (Type.GetType("BepInEx.Logging.Logger, BepInEx", throwOnError: false) ?? Type.GetType("BepInEx.Logging.Logger, BepInEx.Core", throwOnError: false)).GetMethod("CreateLogSource", bindingAttr, null, new Type[1] { typeof(string) }, null); object logInstance2 = method.Invoke(null, new object[1] { base.Source }); MethodInfo method2 = logInstance2.GetType().GetMethod("Log", bindingAttr2, null, new Type[2] { type, typeof(object) }, null); FastReflectionDelegate log2 = method2.CreateFastDelegate(); _logMethod = delegate(LogLevel level, string msg) { int num2 = Convert(level); log2(logInstance2, num2, msg); }; } else { Type type2 = Type.GetType("BepInEx.Logger, BepInEx", throwOnError: false); object logInstance = type2.GetProperty("CurrentLogger", bindingAttr).GetValue(null, null); MethodInfo method3 = logInstance.GetType().GetMethod("Log", bindingAttr2, null, new Type[2] { type, typeof(object) }, null); FastReflectionDelegate log = method3.CreateFastDelegate(); _logMethod = delegate(LogLevel level, string msg) { int num = Convert(level); log(logInstance, num, msg); }; } } else { Type type3 = Type.GetType("MelonLoader.MelonLogger, MelonLoader.ModHandler", throwOnError: false); if ((object)type3 != null) { MethodInfo method4 = type3.GetMethod("Log", bindingAttr, null, new Type[2] { typeof(ConsoleColor), typeof(string) }, null); MethodInfo method5 = type3.GetMethod("Log", bindingAttr, null, new Type[1] { typeof(string) }, null); MethodInfo method6 = type3.GetMethod("LogWarning", bindingAttr, null, new Type[1] { typeof(string) }, null); MethodInfo method7 = type3.GetMethod("LogError", bindingAttr, null, new Type[1] { typeof(string) }, null); FastReflectionDelegate logDebug = method4.CreateFastDelegate(); FastReflectionDelegate logInfo = method5.CreateFastDelegate(); FastReflectionDelegate logWarning = method6.CreateFastDelegate(); FastReflectionDelegate logError = method7.CreateFastDelegate(); _logMethod = delegate(LogLevel level, string msg) { switch (level) { case LogLevel.Debug: logDebug(null, ConsoleColor.Gray, msg); break; case LogLevel.Info: logInfo(null, msg); break; case LogLevel.Warn: logWarning(null, msg); break; case LogLevel.Error: logError(null, msg); break; default: throw new ArgumentException("level"); } }; } } if (_logMethod != null) { return; } throw new Exception("Did not recognize any mod loader!"); } protected override void Log(LogLevel level, string message) { _logMethod(level, message); } public static int Convert(LogLevel level) { return level switch { LogLevel.Debug => 32, LogLevel.Info => 16, LogLevel.Warn => 4, LogLevel.Error => 2, _ => 0, }; } } public abstract class XuaLogger { private static XuaLogger _default; private static XuaLogger _common; private static XuaLogger _resourceRedirector; public static XuaLogger AutoTranslator { get { if (_default == null) { _default = CreateLogger("XUnity.AutoTranslator"); } return _default; } set { _default = value ?? throw new ArgumentNullException("value"); } } public static XuaLogger Common { get { if (_common == null) { _common = CreateLogger("XUnity.Common"); } return _common; } set { _common = value ?? throw new ArgumentNullException("value"); } } public static XuaLogger ResourceRedirector { get { if (_resourceRedirector == null) { _resourceRedirector = CreateLogger("XUnity.ResourceRedirector"); } return _resourceRedirector; } set { _resourceRedirector = value ?? throw new ArgumentNullException("value"); } } public string Source { get; set; } internal static XuaLogger CreateLogger(string source) { try { return new ModLoaderSpecificLogger(source); } catch (Exception) { return new ConsoleLogger(source); } } public XuaLogger(string source) { Source = source; } public void Error(Exception e, string message) { Log(LogLevel.Error, message + Environment.NewLine + e); } public void Error(string message) { Log(LogLevel.Error, message); } public void Warn(Exception e, string message) { Log(LogLevel.Warn, message + Environment.NewLine + e); } public void Warn(string message) { Log(LogLevel.Warn, message); } public void Info(Exception e, string message) { Log(LogLevel.Info, message + Environment.NewLine + e); } public void Info(string message) { Log(LogLevel.Info, message); } public void Debug(Exception e, string message) { Log(LogLevel.Debug, message + Environment.NewLine + e); } public void Debug(string message) { Log(LogLevel.Debug, message); } protected abstract void Log(LogLevel level, string message); protected string GetDefaultPrefix(LogLevel level) { return level switch { LogLevel.Debug => "[DEBUG][" + Source + "]: ", LogLevel.Info => "[INFO][" + Source + "]: ", LogLevel.Warn => "[WARN][" + Source + "]: ", LogLevel.Error => "[ERROR][" + Source + "]: ", _ => "[UNKNOW][" + Source + "]: ", }; } } } namespace XUnity.Common.Harmony { public static class AccessToolsShim { private static readonly BindingFlags All; private static readonly Func<Type, string, Type[], Type[], MethodInfo> AccessTools_Method; private static readonly Func<Type, string, PropertyInfo> AccessTools_Property; static AccessToolsShim() { All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo method = ClrTypes.AccessTools.GetMethod("Method", All, null, new Type[4] { typeof(Type), typeof(string), typeof(Type[]), typeof(Type[]) }, null); MethodInfo? method2 = ClrTypes.AccessTools.GetMethod("Property", All, null, new Type[2] { typeof(Type), typeof(string) }, null); AccessTools_Method = (Func<Type, string, Type[], Type[], MethodInfo>)ExpressionHelper.CreateTypedFastInvoke(method); AccessTools_Property = (Func<Type, string, PropertyInfo>)ExpressionHelper.CreateTypedFastInvoke(method2); } public static MethodInfo Method(Type type, string name, params Type[] parameters) { return AccessTools_Method(type, name, parameters, null); } public static PropertyInfo Property(Type type, string name) { return AccessTools_Property(type, name); } } } namespace XUnity.Common.Extensions { public static class ExceptionExtensions { public static TException FirstInnerExceptionOfType<TException>(this Exception e) where TException : Exception { for (Exception ex = e; ex != null; ex = ex.InnerException) { if (ex is TException) { return (TException)ex; } } return null; } } public static class ObjectExtensions { public static Type GetUnityType(this object obj) { return obj.GetType(); } public static bool TryCastTo<TObject>(this object obj, out TObject castedObject) { if (obj is TObject val) { castedObject = val; return true; } castedObject = default(TObject); return false; } } public static class StreamExtensions { public static byte[] ReadFully(this Stream stream, int initialLength) { if (initialLength < 1) { initialLength = 32768; } byte[] array = new byte[initialLength]; int num = 0; int num2; while ((num2 = stream.Read(array, num, array.Length - num)) > 0) { num += num2; if (num == array.Length) { int num3 = stream.ReadByte(); if (num3 == -1) { return array; } byte[] array2 = new byte[array.Length * 2]; Array.Copy(array, array2, array.Length); array2[num] = (byte)num3; array = array2; num++; } } byte[] array3 = new byte[num]; Array.Copy(array, array3, num); return array3; } } public static class StringExtensions { private static readonly HashSet<char> InvalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars()); public static string UseCorrectDirectorySeparators(this string path) { if (Path.DirectorySeparatorChar == '\\') { return path.Replace('/', Path.DirectorySeparatorChar); } if (Path.DirectorySeparatorChar == '/') { return path.Replace('\\', Path.DirectorySeparatorChar); } return path; } public static bool IsNullOrWhiteSpace(this string value) { if (value == null) { return true; } for (int i = 0; i < value.Length; i++) { if (!char.IsWhiteSpace(value[i])) { return false; } } return true; } public static string MakeRelativePath(this string fullOrRelativePath, string basePath) { StringBuilder stringBuilder = new StringBuilder(); int i = 0; bool flag = false; string[] array = basePath.Split(':', '\\', '/'); List<string> list = fullOrRelativePath.Split(':', '\\', '/').ToList(); if (array.Length == 0 || list.Count <= 0 || array[0] != list[0]) { flag = true; } bool flag2 = false; for (int j = 0; j < list.Count; j++) { if (list[j] == "..") { if (flag2) { int num = j - 1; if (num >= 0) { list.RemoveAt(j); list.RemoveAt(num); j -= 2; } } } else { flag2 = true; } } if (!flag) { for (i = 1; i < array.Length && !(array[i] != list[i]); i++) { } for (int k = 0; k < array.Length - i; k++) { char directorySeparatorChar = Path.DirectorySeparatorChar; stringBuilder.Append(".." + directorySeparatorChar); } } for (int l = i; l < list.Count - 1; l++) { string value = list[l]; stringBuilder.Append(value).Append(Path.DirectorySeparatorChar); } string value2 = list[^1]; stringBuilder.Append(value2); return stringBuilder.ToString(); } public static string SanitizeForFileSystem(this string path) { StringBuilder stringBuilder = new StringBuilder(path.Length); foreach (char c in path) { if (!InvalidFileNameChars.Contains(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } public static string SplitToLines(this string text, int maxStringLength, params char[] splitOnCharacters) { StringBuilder stringBuilder = new StringBuilder(); int num; for (int i = 0; text.Length > i; i += num) { if (i != 0) { stringBuilder.Append('\n'); } num = ((i + maxStringLength <= text.Length) ? text.Substring(i, maxStringLength).LastIndexOfAny(splitOnCharacters) : (text.Length - i)); num = ((num == -1) ? maxStringLength : num); stringBuilder.Append(text.Substring(i, num).Trim()); } return stringBuilder.ToString(); } public static bool StartsWithStrict(this string str, string prefix) { int num = Math.Min(str.Length, prefix.Length); if (num < prefix.Length) { return false; } for (int i = 0; i < num; i++) { if (str[i] != prefix[i]) { return false; } } return true; } public static string GetBetween(this string strSource, string strStart, string strEnd) { int num = strSource.IndexOf(strStart); if (num != -1) { num += strStart.Length; int num2 = strSource.IndexOf(strEnd, num); if (num2 > num) { return strSource.Substring(num, num2 - num); } } return string.Empty; } public static bool RemindsOf(this string that, string other) { if (!that.StartsWith(other) && !other.StartsWith(that) && !that.EndsWith(other)) { return other.EndsWith(that); } return true; } } } namespace XUnity.Common.Constants { public static class ClrTypes { public static readonly Type AccessTools = FindTypeStrict("Harmony.AccessTools, 0Harmony") ?? FindTypeStrict("HarmonyLib.AccessTools, 0Harmony") ?? FindTypeStrict("Harmony.AccessTools, MelonLoader.ModHandler") ?? FindTypeStrict("HarmonyLib.AccessTools, MelonLoader.ModHandler"); public static readonly Type HarmonyMethod = FindTypeStrict("Harmony.HarmonyMethod, 0Harmony") ?? FindTypeStrict("HarmonyLib.HarmonyMethod, 0Harmony") ?? FindTypeStrict("Harmony.HarmonyMethod, MelonLoader.ModHandler") ?? FindTypeStrict("HarmonyLib.HarmonyMethod, MelonLoader.ModHandler"); public static readonly Type HarmonyInstance = FindTypeStrict("Harmony.HarmonyInstance, 0Harmony") ?? FindTypeStrict("Harmony.HarmonyInstance, MelonLoader.ModHandler"); public static readonly Type Harmony = FindTypeStrict("HarmonyLib.Harmony, 0Harmony") ?? FindTypeStrict("HarmonyLib.Harmony, MelonLoader.ModHandler"); public static readonly Type Hook = FindTypeStrict("MonoMod.RuntimeDetour.Hook, MonoMod.RuntimeDetour"); public static readonly Type Detour = FindTypeStrict("MonoMod.RuntimeDetour.Detour, MonoMod.RuntimeDetour"); public static readonly Type NativeDetour = FindTypeStrict("MonoMod.RuntimeDetour.NativeDetour, MonoMod.RuntimeDetour"); public static readonly Type DynamicMethodDefinition = FindTypeStrict("MonoMod.Utils.DynamicMethodDefinition, MonoMod.Utils"); public static readonly Type Imports = FindTypeStrict("MelonLoader.Imports, MelonLoader.ModHandler"); public static readonly Type MethodBase = FindType("System.Reflection.MethodBase"); public static readonly Type Task = FindType("System.Threading.Tasks.Task"); private static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(name, throwOnError: false); if ((object)type != null) { return type; } } catch { } } return null; } private static Type FindTypeStrict(string name) { return Type.GetType(name, throwOnError: false); } } public class TypeContainer { public Type ClrType { get; } public Type UnityType { get; } public TypeContainer(Type type) { UnityType = type; ClrType = type; } public bool IsAssignableFrom(Type unityType) { if ((object)UnityType != null) { return UnityType.IsAssignableFrom(unityType); } return false; } } public static class UnityFeatures { private static readonly BindingFlags All; public static bool SupportsMouseScrollDelta { get; } public static bool SupportsClipboard { get; } public static bool SupportsCustomYieldInstruction { get; } public static bool SupportsSceneManager { get; } public static bool SupportsWaitForSecondsRealtime { get; set; } static UnityFeatures() { All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; SupportsMouseScrollDelta = false; SupportsClipboard = false; SupportsCustomYieldInstruction = false; SupportsSceneManager = false; SupportsWaitForSecondsRealtime = false; try { SupportsClipboard = (object)UnityTypes.TextEditor?.ClrType.GetProperty("text")?.GetSetMethod() != null; } catch (Exception) { } try { SupportsCustomYieldInstruction = UnityTypes.CustomYieldInstruction != null; } catch (Exception) { } try { SupportsSceneManager = UnityTypes.Scene != null && UnityTypes.SceneManager != null && (object)UnityTypes.SceneManager.ClrType.GetMethod("add_sceneLoaded", All) != null; } catch (Exception) { } try { SupportsMouseScrollDelta = (object)UnityTypes.Input?.ClrType.GetProperty("mouseScrollDelta") != null; } catch (Exception) { } try { SupportsWaitForSecondsRealtime = UnityTypes.WaitForSecondsRealtime != null; } catch (Exception) { } } } public static class UnityTypes { public static class TMP_Settings_Properties { public static CachedProperty Version = TMP_Settings?.ClrType.CachedProperty("version"); public static CachedProperty FallbackFontAssets = TMP_Settings?.ClrType.CachedProperty("fallbackFontAssets"); } public static class TMP_FontAsset_Properties { public static CachedProperty Version = TMP_FontAsset?.ClrType.CachedProperty("version"); } public static class AdvScenarioData_Properties { public static CachedProperty ScenarioLabels = AdvScenarioData?.ClrType.CachedProperty("ScenarioLabels"); } public static class UguiNovelText_Properties { public static CachedProperty TextGenerator = UguiNovelText?.ClrType.CachedProperty("TextGenerator"); } public static class UguiNovelText_Methods { public static CachedMethod SetAllDirty = UguiNovelText?.ClrType.CachedMethod("SetAllDirty"); } public static class UguiNovelTextGenerator_Methods { public static CachedMethod Refresh = UguiNovelTextGenerator?.ClrType.CachedMethod("Refresh"); } public static class AdvUguiMessageWindow_Properties { public static CachedProperty Text = AdvUguiMessageWindow?.ClrType.CachedProperty("Text"); public static CachedProperty Engine = AdvUguiMessageWindow?.ClrType.CachedProperty("Engine"); } public static class AdvUiMessageWindow_Fields { public static CachedField text = AdvUiMessageWindow?.ClrType.CachedField("text"); public static CachedField nameText = AdvUiMessageWindow?.ClrType.CachedField("nameText"); } public static class AdvUguiMessageWindow_Fields { public static FieldInfo text = AdvUguiMessageWindow?.ClrType.GetField("text", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); public static FieldInfo nameText = AdvUguiMessageWindow?.ClrType.GetField("nameText", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); public static FieldInfo engine = AdvUguiMessageWindow?.ClrType.GetField("engine", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } public static class AdvEngine_Properties { public static CachedProperty Page = AdvEngine?.ClrType.CachedProperty("Page"); } public static class AdvPage_Methods { public static CachedMethod RemakeTextData = AdvPage?.ClrType.CachedMethod("RemakeTextData"); public static CachedMethod RemakeText = AdvPage?.ClrType.CachedMethod("RemakeText"); public static CachedMethod ChangeMessageWindowText = AdvPage?.ClrType.CachedMethod("ChangeMessageWindowText", typeof(string), typeof(string), typeof(string), typeof(string)); } public static class UILabel_Properties { public static CachedProperty MultiLine = UILabel?.ClrType.CachedProperty("multiLine"); public static CachedProperty OverflowMethod = UILabel?.ClrType.CachedProperty("overflowMethod"); public static CachedProperty SpacingX = UILabel?.ClrType.CachedProperty("spacingX"); public static CachedProperty UseFloatSpacing = UILabel?.ClrType.CachedProperty("useFloatSpacing"); } public static class Text_Properties { public static CachedProperty Font = Text?.ClrType.CachedProperty("font"); public static CachedProperty FontSize = Text?.ClrType.CachedProperty("fontSize"); public static CachedProperty HorizontalOverflow = Text?.ClrType.CachedProperty("horizontalOverflow"); public static CachedProperty VerticalOverflow = Text?.ClrType.CachedProperty("verticalOverflow"); public static CachedProperty LineSpacing = Text?.ClrType.CachedProperty("lineSpacing"); public static CachedProperty ResizeTextForBestFit = Text?.ClrType.CachedProperty("resizeTextForBestFit"); public static CachedProperty ResizeTextMinSize = Text?.ClrType.CachedProperty("resizeTextMinSize"); public static CachedProperty ResizeTextMaxSize = Text?.ClrType.CachedProperty("resizeTextMaxSize"); } public static class InputField_Properties { public static CachedProperty Placeholder = InputField?.ClrType.CachedProperty("placeholder"); } public static class TMP_InputField_Properties { public static CachedProperty Placeholder = TMP_InputField?.ClrType.CachedProperty("placeholder"); } public static class Font_Properties { public static CachedProperty FontSize = Font?.ClrType.CachedProperty("fontSize"); } public static class AssetBundle_Methods { public static CachedMethod LoadAll = AssetBundle?.ClrType.CachedMethod("LoadAll", typeof(Type)); public static CachedMethod LoadAllAssets = AssetBundle?.ClrType.CachedMethod("LoadAllAssets", typeof(Type)); public static CachedMethod LoadFromFile = AssetBundle?.ClrType.CachedMethod("LoadFromFile", typeof(string)); public static CachedMethod CreateFromFile = AssetBundle?.ClrType.CachedMethod("CreateFromFile", typeof(string)); } public static class TextExpansion_Methods { public static CachedMethod SetMessageType = TextExpansion?.ClrType.CachedMethod("SetMessageType"); public static CachedMethod SkipTypeWriter = TextExpansion?.ClrType.CachedMethod("SkipTypeWriter"); } public static class GameObject_Methods { } public static class TextMesh_Methods { } public static class Text_Methods { } public static class InputField_Methods { } public static class TMP_Text_Methods { } public static class TMP_InputField_Methods { } public static class TextMeshPro_Methods { } public static class TextMeshProUGUI_Methods { } public static class UILabel_Methods { } public static class UIRect_Methods { } public static class SceneManager_Methods { public static readonly Action<UnityAction<Scene, LoadSceneMode>> add_sceneLoaded = (Action<UnityAction<Scene, LoadSceneMode>>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(SceneManager).GetMethod("add_sceneLoaded", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(UnityAction<Scene, LoadSceneMode>) }, null)); } public static class Texture2D_Methods { public static readonly Func<Texture2D, byte[], bool> LoadImage = (Func<Texture2D, byte[], bool>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(Texture2D).GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(byte[]) }, null)); public static readonly Func<Texture2D, byte[]> EncodeToPNG = (Func<Texture2D, byte[]>)ExpressionHelper.CreateTypedFastInvokeUnchecked(typeof(Texture2D).GetMethod("EncodeToPNG", BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null)); } public static class ImageConversion_Methods { public static readonly Func<Texture2D, byte[], bool, bool> LoadImage = (Func<Texture2D, byte[], bool, bool>)ExpressionHelper.CreateTypedFastInvokeUnchecked(ImageConversion?.ClrType.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, null)); public static readonly Func<Texture2D, byte[]> EncodeToPNG = (Func<Texture2D, byte[]>)ExpressionHelper.CreateTypedFastInvokeUnchecked(ImageConversion?.ClrType.GetMethod("EncodeToPNG", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Texture2D) }, null)); } public static readonly TypeContainer UILabel = FindType("UILabel"); public static readonly TypeContainer UIWidget = FindType("UIWidget"); public static readonly TypeContainer UIAtlas = FindType("UIAtlas"); public static readonly TypeContainer UISprite = FindType("UISprite"); public static readonly TypeContainer UITexture = FindType("UITexture"); public static readonly TypeContainer UI2DSprite = FindType("UI2DSprite"); public static readonly TypeContainer UIFont = FindType("UIFont"); public static readonly TypeContainer UIPanel = FindType("UIPanel"); public static readonly TypeContainer UIRect = FindType("UIRect"); public static readonly TypeContainer UIInput = FindType("UIInput"); public static readonly TypeContainer TextField = FindType("FairyGUI.TextField"); public static readonly TypeContainer TMP_InputField = FindType("TMPro.TMP_InputField"); public static readonly TypeContainer TMP_Text = FindType("TMPro.TMP_Text"); public static readonly TypeContainer TextMeshProUGUI = FindType("TMPro.TextMeshProUGUI"); public static readonly TypeContainer TextMeshPro = FindType("TMPro.TextMeshPro"); public static readonly TypeContainer TMP_FontAsset = FindType("TMPro.TMP_FontAsset"); public static readonly TypeContainer TMP_Settings = FindType("TMPro.TMP_Settings"); public static readonly TypeContainer GameObject = FindType("UnityEngine.GameObject"); public static readonly TypeContainer Transform = FindType("UnityEngine.Transform"); public static readonly TypeContainer TextMesh = FindType("UnityEngine.TextMesh"); public static readonly TypeContainer Text = FindType("UnityEngine.UI.Text"); public static readonly TypeContainer Image = FindType("UnityEngine.UI.Image"); public static readonly TypeContainer RawImage = FindType("UnityEngine.UI.RawImage"); public static readonly TypeContainer MaskableGraphic = FindType("UnityEngine.UI.MaskableGraphic"); public static readonly TypeContainer Graphic = FindType("UnityEngine.UI.Graphic"); public static readonly TypeContainer GUIContent = FindType("UnityEngine.GUIContent"); public static readonly TypeContainer WWW = FindType("UnityEngine.WWW"); public static readonly TypeContainer InputField = FindType("UnityEngine.UI.InputField"); public static readonly TypeContainer GUI = FindType("UnityEngine.GUI"); public static readonly TypeContainer GUI_ToolbarButtonSize = FindType("UnityEngine.GUI+ToolbarButtonSize"); public static readonly TypeContainer GUIStyle = FindType("UnityEngine.GUIStyle"); public static readonly TypeContainer ImageConversion = FindType("UnityEngine.ImageConversion"); public static readonly TypeContainer Texture2D = FindType("UnityEngine.Texture2D"); public static readonly TypeContainer Texture = FindType("UnityEngine.Texture"); public static readonly TypeContainer SpriteRenderer = FindType("UnityEngine.SpriteRenderer"); public static readonly TypeContainer Sprite = FindType("UnityEngine.Sprite"); public static readonly TypeContainer Object = FindType("UnityEngine.Object"); public static readonly TypeContainer TextEditor = FindType("UnityEngine.TextEditor"); public static readonly TypeContainer CustomYieldInstruction = FindType("UnityEngine.CustomYieldInstruction"); public static readonly TypeContainer SceneManager = FindType("UnityEngine.SceneManagement.SceneManager"); public static readonly TypeContainer Scene = FindType("UnityEngine.SceneManagement.Scene"); public static readonly TypeContainer UnityEventBase = FindType("UnityEngine.Events.UnityEventBase"); public static readonly TypeContainer BaseInvokableCall = FindType("UnityEngine.Events.BaseInvokableCall"); public static readonly TypeContainer Font = FindType("UnityEngine.Font"); public static readonly TypeContainer WaitForSecondsRealtime = FindType("UnityEngine.WaitForSecondsRealtime"); public static readonly TypeContainer Input = FindType("UnityEngine.Input"); public static readonly TypeContainer AssetBundleCreateRequest = FindType("UnityEngine.AssetBundleCreateRequest"); public static readonly TypeContainer AssetBundle = FindType("UnityEngine.AssetBundle"); public static readonly TypeContainer AssetBundleRequest = FindType("UnityEngine.AssetBundleRequest"); public static readonly TypeContainer Resources = FindType("UnityEngine.Resources"); public static readonly TypeContainer AsyncOperation = FindType("UnityEngine.AsyncOperation"); public static readonly TypeContainer TextAsset = FindType("UnityEngine.TextAsset"); public static readonly Type HorizontalWrapMode = FindClrType("UnityEngine.HorizontalWrapMode"); public static readonly Type TextOverflowModes = FindClrType("TMPro.TextOverflowModes"); public static readonly Type TextAlignmentOptions = FindClrType("TMPro.TextAlignmentOptions"); public static readonly Type VerticalWrapMode = FindClrType("UnityEngine.VerticalWrapMode"); public static readonly TypeContainer TextExpansion = FindType("UnityEngine.UI.TextExpansion"); public static readonly TypeContainer Typewriter = FindType("Typewriter"); public static readonly TypeContainer UguiNovelText = FindType("Utage.UguiNovelText"); public static readonly TypeContainer UguiNovelTextGenerator = FindType("Utage.UguiNovelTextGenerator"); public static readonly TypeContainer AdvEngine = FindType("Utage.AdvEngine"); public static readonly TypeContainer AdvPage = FindType("Utage.AdvPage"); public static readonly TypeContainer TextData = FindType("Utage.TextData"); public static readonly TypeContainer AdvUguiMessageWindow = FindType("Utage.AdvUguiMessageWindow") ?? FindType("AdvUguiMessageWindow"); public static readonly TypeContainer AdvUiMessageWindow = FindType("AdvUiMessageWindow"); public static readonly TypeContainer AdvDataManager = FindType("Utage.AdvDataManager"); public static readonly TypeContainer AdvScenarioData = FindType("Utage.AdvScenarioData"); public static readonly TypeContainer AdvScenarioLabelData = FindType("Utage.AdvScenarioLabelData"); public static readonly TypeContainer DicingTextures = FindType("Utage.DicingTextures"); public static readonly TypeContainer DicingImage = FindType("Utage.DicingImage"); public static readonly TypeContainer TextArea2D = FindType("Utage.TextArea2D"); public static readonly TypeContainer CubismRenderer = FindType("Live2D.Cubism.Rendering.CubismRenderer"); public static readonly TypeContainer TextWindow = FindType("Assets.System.Text.TextWindow"); private static Type FindClrType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(name, throwOnError: false); if ((object)type != null) { return type; } } catch { } } return null; } private static TypeContainer FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(name, throwOnError: false); if ((object)type != null) { return new TypeContainer(type); } } catch { } } return null; } } }
BepInEx/plugins/AHook.dll
Decompiled 9 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using AHook.Patches; using AHook.Patches.util; using BepInEx; using BepInEx.Logging; using HarmonyLib; using XUnity.Common.Extensions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("GameTranslator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("GameTranslator")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("acc996e2-652f-4af2-ab3b-8062a6acc7e4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace AHook { [BepInPlugin("AHook", "AHook", "1.0.0")] public class AHookPlugin : BaseUnityPlugin { private const string PLUGIN_GUID = "AHook"; private const string PLUGIN_NAME = "AHook"; private const string PLUGIN_VERSION = "1.0.0"; internal static AHookPlugin Instance; private readonly Harmony harmony = new Harmony("AHook"); public static string ConfigPath; public static string DefaultPath; public static string TexturesPath; public static bool shouldTranslate; public static ManualLogSource logger; private void Awake() { logger = ((BaseUnityPlugin)this).Logger; Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"插件AHook已加载!"); ConfigPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("AHook.cfg", ""); DefaultPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("AHook.cfg", "translations\\zh-CN\\"); TranslateConfig.Load(); ModHook.PatchAll(harmony); } public static void LogInfo(string info) { if (logger != null) { logger.LogInfo((object)info); } } } public class TranslateConfig { public class TranslateConfigFile { public string ConfigFilePath; public string ConfigFileName; public bool shouldTranslate = false; public int shouldTranslateMinLength = 200; public int shouldTranslateMaxLength = 0; public bool shouldLoad = true; public IDictionary<string, string> normal = new Dictionary<string, string>(); public IDictionary<string, string> strong = new Dictionary<string, string>(); public IDictionary<string, string> special = new Dictionary<string, string>(); public IDictionary<string, string> useRex = new Dictionary<string, string>(); public static HashSet<TranslateConfigFile> configs = new HashSet<TranslateConfigFile>(); public Dictionary<string, string> translatePairs = new Dictionary<string, string>(); public bool isOrgin = false; public static Regex regex = new Regex("(?<!\\\\)="); private List<string> logs = new List<string>(); public TranslateConfigFile(string configName, bool saveOnInit, bool shouldLoad, bool origin = false) { ConfigFileName = configName; isOrgin = origin; if (origin) { ConfigFilePath = AHookPlugin.ConfigPath + configName + ".cfg"; } else { ConfigFilePath = AHookPlugin.DefaultPath + configName + ".cfg"; } this.shouldLoad = shouldLoad; if (ConfigFilePath == null) { throw new ArgumentNullException("configPath"); } ConfigFilePath = Path.GetFullPath(ConfigFilePath); if (this.shouldLoad && File.Exists(ConfigFilePath)) { Reload(); } else if (saveOnInit) { Save(); } configs.Add(this); } public void show() { foreach (KeyValuePair<string, string> item in normal) { string text = item.Key.Replace("=", "\\="); string text2 = item.Value.Replace("=", "\\="); AHookPlugin.LogInfo(text + "=" + text2); } } public void Reload() { normal.Clear(); useRex.Clear(); string[] array = File.ReadAllLines(ConfigFilePath); foreach (string text in array) { if (text.StartsWith("#") || !text.Contains("=")) { continue; } string[] array2 = regex.Split(text); if (array2.Length != 2) { continue; } string text2 = array2[0].Replace("\\=", "="); string text3 = array2[1].Replace("\\=", "="); if (isOrgin) { text2 = text2.Trim(); text3 = text3.Trim(); } if (text2.StartsWith(rexName)) { text2 = text2.Replace(rexName, ""); if (useRex.ContainsKey(text2)) { useRex[text2] = text3; } else { useRex.Add(text2, text3); } } else if (text2.StartsWith(strongName)) { text2 = text2.Replace(strongName, ""); if (strong.ContainsKey(text2)) { strong[text2] = text3; } else { strong.Add(text2, text3); } } else if (normal.ContainsKey(text2)) { normal[text2] = text3; special[text3] = text2; } else { normal.Add(text2, text3); if (!special.ContainsKey(text3)) { special.Add(text3, text2); } } if (text2.Length < shouldTranslateMinLength) { shouldTranslateMinLength = text2.Length; } if (text2.Length > shouldTranslateMaxLength) { shouldTranslateMaxLength = text2.Length; } } } public void Log(string text) { logs.Add(text); string directoryName = Path.GetDirectoryName(ConfigFilePath); if (directoryName == null) { Directory.CreateDirectory(directoryName); } List<string> list = new List<string>(); list.Add("##" + ConfigFileName); File.WriteAllLines(ConfigFilePath, logs); } public void Save() { string directoryName = Path.GetDirectoryName(ConfigFilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(ConfigFilePath)) { File.Create(ConfigFilePath).Close(); } else { if (!shouldLoad) { return; } List<string> list = new List<string>(); list.Add("##" + ConfigFileName); foreach (KeyValuePair<string, string> item in normal) { string text = item.Key.Replace("=", "\\="); string text2 = item.Value.Replace("=", "\\="); list.Add(text + "=" + text2); } list.Add("##强制替换"); foreach (KeyValuePair<string, string> item2 in strong) { string text3 = item2.Key.Replace("=", "\\="); string text4 = item2.Value.Replace("=", "\\="); list.Add(strongName + text3 + "=" + text4); } list.Add("##正则表达式"); foreach (KeyValuePair<string, string> item3 in useRex) { string text5 = item3.Key.Replace("=", "\\="); string text6 = item3.Value.Replace("=", "\\="); list.Add(rexName + text5 + "=" + text6); } File.WriteAllLines(ConfigFilePath, list.ToArray()); } } } public static TranslateConfigFile mod; public static TranslateConfigFile items; public static string rexName = "rex:"; public static string strongName = "stg:"; public static void Load() { items = CreateNewConfig("Item-Translate"); mod = CreateConfig("GameTranslator"); } private static TranslateConfigFile CreateConfig(string fileName) { return new TranslateConfigFile(fileName, saveOnInit: true, shouldLoad: true, origin: true); } private static TranslateConfigFile CreateNewConfig(string fileName, bool should) { return new TranslateConfigFile(fileName, saveOnInit: true, should); } private static TranslateConfigFile CreateNewConfig(string fileName) { AHookPlugin.logger.LogInfo((object)("GameTranslator正在载入配置文件" + fileName)); return new TranslateConfigFile(fileName, saveOnInit: true, shouldLoad: true); } public static string getRegex(string pattern) { StringBuilder stringBuilder = new StringBuilder(pattern); stringBuilder.Replace("\\", "\\\\"); return stringBuilder.ToString(); } public static bool IsStringContainsEnglish(string input) { Regex regex = new Regex("[a-zA-Z]"); return regex.IsMatch(input); } public static bool IsStringContainsChinese(string input) { Regex regex = new Regex("[\\u4e00-\\u9fa5]"); return regex.IsMatch(input); } public static string useRegularExpression(string raw, string pattern, string result) { pattern = pattern.Replace(rexName, ""); return Regex.Replace(raw, pattern, result); } public static string replaceByMap(string text, TranslateConfigFile file) { if (file.translatePairs.ContainsKey(text)) { return file.translatePairs[text]; } StringBuffer stringBuffer = new StringBuffer(text); foreach (KeyValuePair<string, string> item in file.useRex) { string pattern = Regex.Unescape(getRegex(item.Key)); string result = Regex.Unescape(item.Value); string str = useRegularExpression(stringBuffer.ToString(), pattern, result); stringBuffer.Clear().Append(str); } foreach (KeyValuePair<string, string> item2 in file.strong) { string oldValue = Regex.Unescape(item2.Key); string newValue = Regex.Unescape(item2.Value); stringBuffer.Replace(oldValue, newValue); } foreach (KeyValuePair<string, string> item3 in file.normal) { string oldValue2 = Regex.Unescape(item3.Key); string newValue2 = Regex.Unescape(item3.Value); stringBuffer.ReplaceFull(oldValue2, newValue2); } file.translatePairs[text] = stringBuffer.ToString(); return stringBuffer.ToString(); } } } namespace AHook.Patches { internal static class ModHook { internal class ReservedItemInfoHook { public static void Init(Harmony harmony) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown Type type = Type.GetType("ReservedItemSlotCore.ReservedItemInfo, ReservedItemSlotCore"); if (!(type == null)) { AHookPlugin.LogInfo("Trying to be compatible with ReservedItemSlotCore"); ConstructorInfo constructor = type.GetConstructor(new Type[6] { typeof(string), typeof(int), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }); MethodInfo method = typeof(ReservedItemInfoHook).GetMethod("Prefix"); harmony.Patch((MethodBase)constructor, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } public static bool Prefix(ref string itemName, int hotbarSlotPriority, bool forceUpdateCanBeGrabbedBeforeGameStart, bool canBeGrabbedBeforeGameStart, bool forceUpdateRequiresBattery, bool requiresBattery) { try { if (itemName != null && !StringExtensions.IsNullOrWhiteSpace(itemName) && TranslateConfig.mod.normal.ContainsKey("Translate Items") && TranslateConfig.mod.normal["Translate Items"].Equals("true")) { itemName = TranslateConfig.replaceByMap(itemName, TranslateConfig.items); } } catch (Exception ex) { AHookPlugin.LogInfo("未找到GameTranslator模组:" + ex.Message); } return true; } } public static void PatchAll(Harmony harmony) { ReservedItemInfoHook.Init(harmony); } } } namespace AHook.Patches.util { public class StringBuffer { private char[] value; private int length; private int capacity; private const int DEFAULT_CAPACITY = 16; public int Length { get { return length; } set { if (value < 0 || value > capacity) { throw new ArgumentOutOfRangeException("value"); } if (value < length) { Array.Clear(this.value, value, length - value); } length = value; } } public int Capacity { get { return capacity; } set { if (value < length) { throw new ArgumentOutOfRangeException("value"); } if (value != capacity) { char[] destinationArray = new char[value]; Array.Copy(this.value, 0, destinationArray, 0, length); this.value = destinationArray; capacity = value; } } } public StringBuffer() { value = new char[16]; length = 0; capacity = 16; } public StringBuffer(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity"); } value = new char[capacity]; length = 0; this.capacity = capacity; } public StringBuffer(string str) { if (str == null) { throw new ArgumentNullException("str"); } value = new char[str.Length + 16]; str.CopyTo(0, value, 0, str.Length); length = str.Length; capacity = str.Length + 16; } public void EnsureCapacity(int minimumCapacity) { if (minimumCapacity < 0) { throw new ArgumentOutOfRangeException("minimumCapacity"); } if (minimumCapacity > capacity) { int num = capacity * 2; if (num < minimumCapacity) { num = minimumCapacity; } Capacity = num; } } public StringBuffer Append(object obj) { if (obj == null) { return this; } return Append(obj.ToString()); } public StringBuffer Append(string str) { if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); str.CopyTo(0, value, length, num); length += num; return this; } public StringBuffer Append(char c) { EnsureCapacity(length + 1); value[length] = c; length++; return this; } public StringBuffer Insert(int index, object obj) { if (obj == null) { return this; } return Insert(index, obj.ToString()); } public StringBuffer Insert(int index, string str) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); Array.Copy(value, index, value, index + num, length - index); str.CopyTo(0, value, index, num); length += num; return this; } public StringBuffer Insert(int index, char c) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } EnsureCapacity(length + 1); Array.Copy(value, index, value, index + 1, length - index); value[index] = c; length++; return this; } public StringBuffer Remove(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } Array.Copy(value, startIndex + length, value, startIndex, this.length - startIndex - length); Array.Clear(value, this.length - length, length); this.length -= length; return this; } public StringBuffer Replace(char oldChar, char newChar) { for (int i = 0; i < length; i++) { if (value[i] == oldChar) { value[i] = newChar; } } return this; } public StringBuffer Replace(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOf(oldValue); num3 >= 0; num3 = IndexOf(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } public int IndexOf(char c) { return IndexOf(c, 0, length); } public int IndexOf(char c, int startIndex) { return IndexOf(c, startIndex, length - startIndex); } public int IndexOf(char c, int startIndex, int count) { if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return Array.IndexOf(value, c, startIndex, count); } public int IndexOf(string str) { return IndexOf(str, 0, length); } public int IndexOf(string str, int startIndex) { return IndexOf(str, startIndex, length - startIndex); } public int IndexOf(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return ToString(value, startIndex, count).IndexOf(str); } public static string ToString(char[] value, int startIndex, int count) { if (value == null) { throw new ArgumentNullException("value"); } if (startIndex < 0 || startIndex > value.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > value.Length) { throw new ArgumentOutOfRangeException("count"); } return new string(value, startIndex, count); } public string Substring(int startIndex) { return Substring(startIndex, length - startIndex); } public string Substring(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } return new string(value, startIndex, length); } public bool Contains(string text) { return IndexOf(text) >= 0; } public StringBuffer ReplaceFull(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOfWord(oldValue); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } private int IndexOfWord(string str) { return IndexOfWord(str, 0, length); } private int IndexOfWord(string str, int startIndex) { return IndexOfWord(str, startIndex, length - startIndex); } public int IndexOfWord(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } int num = str.Length; int[] array = new int[num]; int num2 = 0; computeLPSArray(str, num, array); int num3 = startIndex; while (num3 < startIndex + count) { if (str[num2] == value[num3]) { num2++; num3++; } if (num2 == num) { if ((num3 - num2 == 0 || !char.IsLetter(value[num3 - num2 - 1])) && (num3 == length || !char.IsLetter(value[num3]))) { return num3 - num2; } num2 = array[num2 - 1]; } else if (num3 < startIndex + count && str[num2] != value[num3]) { if (num2 != 0) { num2 = array[num2 - 1]; } else { num3++; } } } return -1; } private void computeLPSArray(string str, int M, int[] lps) { int num = 0; int num2 = 1; lps[0] = 0; while (num2 < M) { if (str[num2] == str[num]) { num = (lps[num2] = num + 1); num2++; } else if (num != 0) { num = lps[num - 1]; } else { lps[num2] = num; num2++; } } } private static bool IsWordChar(char c) { return char.IsLetter(c) || c == '_'; } public StringBuffer Clear() { Length = 0; return this; } public override string ToString() { return new string(value, 0, length); } } }
BepInEx/plugins/GameTranslator.dll
Decompiled 9 months 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameTranslator.Patches.Hooks.texture; using GameTranslator.Patches.Translatons; using GameTranslator.Patches.Translatons.Manipulator; using GameTranslator.Patches.Utils; using HarmonyLib; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.UIElements; using XUnity.Common.Constants; using XUnity.Common.Extensions; using XUnity.Common.Harmony; using XUnity.Common.Logging; using XUnity.Common.MonoMod; using XUnity.Common.Utilities; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("GameTranslator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("GameTranslator")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("acc996e2-652f-4af2-ab3b-8062a6acc7e4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace GameTranslator { public class TranslateConfig { public class TranslateConfigFile { public string ConfigFilePath; public string ConfigFileName; public bool shouldTranslate = false; public int shouldTranslateMinLength = 200; public int shouldTranslateMaxLength = 0; public bool shouldLoad = true; public IDictionary<string, string> normal = new Dictionary<string, string>(); public IDictionary<string, string> strong = new Dictionary<string, string>(); public IDictionary<string, string> special = new Dictionary<string, string>(); public IDictionary<string, string> useRex = new Dictionary<string, string>(); public static HashSet<TranslateConfigFile> configs = new HashSet<TranslateConfigFile>(); public Dictionary<string, string> translatePairs = new Dictionary<string, string>(); public static Regex regex = new Regex("(?<!\\\\)="); private List<string> logs = new List<string>(); public TranslateConfigFile(string configName, bool saveOnInit, bool shouldLoad) { ConfigFileName = configName; ConfigFilePath = TranslatePlugin.DefaultPath + configName + ".cfg"; this.shouldLoad = shouldLoad; if (ConfigFilePath == null) { throw new ArgumentNullException("configPath"); } ConfigFilePath = Path.GetFullPath(ConfigFilePath); if (this.shouldLoad && File.Exists(ConfigFilePath)) { Reload(); } else if (saveOnInit) { Save(); } configs.Add(this); } public void Reload() { normal.Clear(); useRex.Clear(); string[] array = File.ReadAllLines(ConfigFilePath); foreach (string text in array) { if (text.StartsWith("#") || !text.Contains("=")) { continue; } string[] array2 = regex.Split(text); if (array2.Length != 2) { continue; } string text2 = array2[0].Replace("\\=", "="); string text3 = array2[1].Replace("\\=", "="); if (text2.StartsWith(rexName)) { text2 = text2.Replace(rexName, ""); if (useRex.ContainsKey(text2)) { useRex[text2] = text3; } else { useRex.Add(text2, text3); } } else if (text2.StartsWith(strongName)) { text2 = text2.Replace(strongName, ""); if (strong.ContainsKey(text2)) { strong[text2] = text3; } else { strong.Add(text2, text3); } } else if (normal.ContainsKey(text2)) { normal[text2] = text3; special[text3] = text2; } else { normal.Add(text2, text3); if (!special.ContainsKey(text3)) { special.Add(text3, text2); } } if (text2.Length < shouldTranslateMinLength) { shouldTranslateMinLength = text2.Length; } if (text2.Length > shouldTranslateMaxLength) { shouldTranslateMaxLength = text2.Length; } } } public void Log(string text) { logs.Add(text); string directoryName = Path.GetDirectoryName(ConfigFilePath); if (directoryName == null) { Directory.CreateDirectory(directoryName); } List<string> list = new List<string>(); list.Add("##" + ConfigFileName); File.WriteAllLines(ConfigFilePath, logs); } public void Save() { string directoryName = Path.GetDirectoryName(ConfigFilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(ConfigFilePath)) { File.Create(ConfigFilePath).Close(); } else { if (!shouldLoad) { return; } List<string> list = new List<string>(); list.Add("##" + ConfigFileName); foreach (KeyValuePair<string, string> item in normal) { string text = item.Key.Replace("=", "\\="); string text2 = item.Value.Replace("=", "\\="); list.Add(text + "=" + text2); } list.Add("##强制替换"); foreach (KeyValuePair<string, string> item2 in strong) { string text3 = item2.Key.Replace("=", "\\="); string text4 = item2.Value.Replace("=", "\\="); list.Add(strongName + text3 + "=" + text4); } list.Add("##正则表达式"); foreach (KeyValuePair<string, string> item3 in useRex) { string text5 = item3.Key.Replace("=", "\\="); string text6 = item3.Value.Replace("=", "\\="); list.Add(rexName + text5 + "=" + text6); } File.WriteAllLines(ConfigFilePath, list.ToArray()); } } } public static TranslateConfigFile normal; public static TranslateConfigFile hud; public static TranslateConfigFile items; public static TranslateConfigFile terminal; public static TranslateConfigFile text; public static TranslateConfigFile cmd_py; public static TranslateConfigFile cmd_zh; public static TranslateConfigFile log; public static TranslateConfigFile gui; public static TranslateConfigFile command; public static NormalTextTranslator normalText; public static NormalTextTranslator hudText; public static NormalTextTranslator guiText; public static TextureTranslationCache cache; public static string rexName = "rex:"; public static string strongName = "stg:"; public static void Load() { hud = CreateNewConfig("HUD-Translate", should: false); hud.shouldTranslate = TranslatePlugin.shouldTranslateHUD.Value; items = CreateNewConfig("Item-Translate"); items.shouldTranslate = TranslatePlugin.shouldTranslateItems.Value; terminal = CreateNewConfig("Terminal-Translate"); terminal.shouldTranslate = TranslatePlugin.shouldTranslateTerimal.Value; cmd_py = CreateNewConfig("CMD-PY-Translate"); cmd_zh = CreateNewConfig("CMD-ZH-Translate"); text = CreateNewConfig("SpecialText-Translate"); text.shouldTranslate = TranslatePlugin.shouldTranslateSpecialText.Value; gui = CreateNewConfig("GuiText-Translate"); gui.shouldTranslate = TranslatePlugin.shouldTranslateGui.Value; normal = CreateNewConfig("Normal-Translate", should: false); normal.shouldTranslate = TranslatePlugin.shouldTranslateNormalText.Value; normalText = new NormalTextTranslator(normal.ConfigFileName + ".cfg"); normalText.Load(); hudText = new NormalTextTranslator(hud.ConfigFileName + ".cfg"); hudText.Load(); cache = new TextureTranslationCache(); cache.LoadTranslationFiles(); FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(); string fullPath = Path.GetFullPath(TranslatePlugin.DefaultPath); TranslatePlugin.logger.LogInfo((object)("开始监测" + fullPath)); fileSystemWatcher.Path = fullPath; fileSystemWatcher.Filter = "*.cfg"; fileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.Security; fileSystemWatcher.Changed += OnFileChange; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; } private static void OnFileChange(object sender, FileSystemEventArgs e) { if (e.ChangeType != WatcherChangeTypes.Created && e.ChangeType != WatcherChangeTypes.Changed) { return; } foreach (TranslateConfigFile config in TranslateConfigFile.configs) { if (config.shouldLoad && config.ConfigFilePath.ToLower().Contains(e.FullPath.ToLower())) { config.Reload(); TextTranslate.ChangeTime++; } } } public static void show(TranslateConfigFile file) { foreach (string key in file.normal.Keys) { TranslatePlugin.LogInfo(key + "=" + file.normal[key]); } } private static TranslateConfigFile CreateNewConfig(string fileName, bool should) { TranslatePlugin.logger.LogInfo((object)("GameTranslator正在载入配置文件" + fileName)); return new TranslateConfigFile(fileName, saveOnInit: true, should); } private static TranslateConfigFile CreateNewConfig(string fileName) { TranslatePlugin.logger.LogInfo((object)("GameTranslator正在载入配置文件" + fileName)); return new TranslateConfigFile(fileName, saveOnInit: true, shouldLoad: true); } public static string getRegex(string pattern) { StringBuilder stringBuilder = new StringBuilder(pattern); stringBuilder.Replace("\\", "\\\\"); return stringBuilder.ToString(); } public static bool IsStringContainsEnglish(string input) { Regex regex = new Regex("[a-zA-Z]"); return regex.IsMatch(input); } public static bool IsStringContainsChinese(string input) { Regex regex = new Regex("[\\u4e00-\\u9fa5]"); return regex.IsMatch(input); } public static string useRegularExpression(string raw, string pattern, string result) { pattern = pattern.Replace(rexName, ""); return Regex.Replace(raw, pattern, result); } public static string replaceByMap(string text, TranslateConfigFile file) { if (!file.shouldTranslate) { return text; } if (file.translatePairs.ContainsKey(text)) { return file.translatePairs[text]; } StringBuffer stringBuffer = new StringBuffer(text); foreach (KeyValuePair<string, string> item in file.useRex) { string pattern = Regex.Unescape(getRegex(item.Key)); string result = Regex.Unescape(item.Value); string str = useRegularExpression(stringBuffer.ToString(), pattern, result); stringBuffer.Clear().Append(str); } foreach (KeyValuePair<string, string> item2 in file.strong) { string oldValue = Regex.Unescape(item2.Key); string newValue = Regex.Unescape(item2.Value); stringBuffer.Replace(oldValue, newValue); } foreach (KeyValuePair<string, string> item3 in file.normal) { string oldValue2 = Regex.Unescape(item3.Key); string newValue2 = Regex.Unescape(item3.Value); stringBuffer.ReplaceFull(oldValue2, newValue2); } file.translatePairs[text] = stringBuffer.ToString(); return stringBuffer.ToString(); } public static void Log(string text) { log.Log(text); } } [BepInPlugin("GameTranslator", "GameTranslator", "1.8.33")] public class TranslatePlugin : BaseUnityPlugin { public static ManualLogSource logger; public static ConfigEntry<string> language; public static ConfigEntry<bool> shouldHideModList; public static ConfigEntry<bool> shouldFixTipErrors; public static ConfigEntry<bool> shouldTranslateTerimal; public static ConfigEntry<bool> shouldTranslateNormalText; public static ConfigEntry<bool> shouldTranslateSpecialText; public static ConfigEntry<bool> shouldTranslateItems; public static ConfigEntry<bool> shouldTranslateHUD; public static ConfigEntry<bool> shouldTranslateGui; public static ConfigEntry<bool> TerimalCanUseChinese; public static ConfigEntry<bool> TerimalCanUsePinyinAbbreviation; public static ConfigEntry<bool> generateTerimalCommand; public static ConfigEntry<bool> changeFont; public static ConfigEntry<bool> changeTexture; public static ConfigEntry<string> overrideFont; public static ConfigEntry<string> alternateFontTextMeshPro; public static ConfigEntry<string> overrideFontTextMeshPro; public static ConfigEntry<string> shouldRemoveChar; private const string PLUGIN_GUID = "GameTranslator"; private const string PLUGIN_NAME = "GameTranslator"; private const string PLUGIN_VERSION = "1.8.33"; internal static TranslatePlugin Instance; private readonly Harmony harmony = new Harmony("GameTranslator"); public static string DefaultPath; public static string TexturesPath; public static bool shouldTranslate; private void Awake() { logger = ((BaseUnityPlugin)this).Logger; Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"插件GameTranslator已加载!"); ConfigFile(); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.All, false); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.Sprite, false); HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.SpriteRenderer, false); harmony.PatchAll(); } private void ConfigFile() { language = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "Language", "zh-CN", "选择的语言,根据不同语言,目录位置不同"); shouldHideModList = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Hide Mod List", true, "是否隐藏mod列表弹窗"); shouldFixTipErrors = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Fix Tip Errors", true, "是否修复Tip提示界面乱码"); shouldTranslateTerimal = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Terimal", true, "是否翻译终端"); shouldTranslateGui = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Gui", true, "是否翻译Gui"); shouldTranslateNormalText = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Normal Text", true, "是否翻译普通文本【需要整段字满足才替换】"); shouldTranslateSpecialText = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Special Text", true, "是否翻译特殊文本【部分字满足就替换】"); TerimalCanUseChinese = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Terimal Can Use Chinese", true, "终端是否可以用中文执行指令"); TerimalCanUsePinyinAbbreviation = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Terimal Can Use Pinyin Abbreviation", true, "终端是否可以用拼音缩写执行指令"); shouldTranslateItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate Items", true, "是否翻译物品"); shouldTranslateHUD = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Translate HUD", true, "是否翻译聊天栏和小提示内容"); changeFont = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Change Font", true, "是否更换字体(关闭后中文会乱码)"); overrideFont = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "OverrideFont", "微软雅黑", "替换的字体"); overrideFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "OverrideFontTextMeshPro", "arialuni.cfg", "替换的字体文件"); alternateFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "AlternateFontTextMeshPro", "", "备用字体文件(如果出现口口的话,可以添加备用字体来解决)"); shouldRemoveChar = ((BaseUnityPlugin)this).Config.Bind<string>("Setting", "shouldRemoveChars", "$0123456789*%", "需要移除的原版字体"); changeTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("Setting", "Change Texture", true, "是否更换图片"); DefaultPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("GameTranslator.cfg", "translations\\" + language.Value + "\\"); TexturesPath = DefaultPath + "Texture\\"; TranslateConfig.Load(); TranslateExtensions.Load(); } public static List<char> getShouldRemoveChars() { return shouldRemoveChar.Value.ToCharArray().ToList(); } public static void LogInfo(string info) { if (logger != null) { logger.LogInfo((object)info); } } } } namespace GameTranslator.Patches { [HarmonyPatch(typeof(GrabbableObject))] internal class GrabbableObjectPatcher { public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>(); public static HashSet<string> translatedItems = new HashSet<string>(); [HarmonyPostfix] [HarmonyPatch("Start")] private static void start(ref GrabbableObject __instance) { if (!((Object)(object)__instance.itemProperties != (Object)null) || !TranslatePlugin.shouldTranslateItems.Value || Utility.IsNullOrWhiteSpace(__instance.itemProperties.itemName)) { return; } if (!translatedItems.Contains(__instance.itemProperties.itemName)) { originToTranslated[__instance.itemProperties.itemName] = TranslateConfig.replaceByMap(__instance.itemProperties.itemName, TranslateConfig.items); translatedItems.Add(originToTranslated[__instance.itemProperties.itemName]); __instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName]; } if (originToTranslated.ContainsKey(__instance.itemProperties.itemName)) { __instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName]; } ScanNodeProperties componentInChildren = ((Component)__instance).GetComponentInChildren<ScanNodeProperties>(); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.headerText != null) { if (!translatedItems.Contains(componentInChildren.headerText)) { originToTranslated[componentInChildren.headerText] = TranslateConfig.replaceByMap(componentInChildren.headerText, TranslateConfig.items); translatedItems.Add(originToTranslated[componentInChildren.headerText]); componentInChildren.headerText = originToTranslated[componentInChildren.headerText]; } if (originToTranslated.ContainsKey(componentInChildren.headerText)) { componentInChildren.headerText = originToTranslated[componentInChildren.headerText]; } } } } [HarmonyPatch(typeof(HUDManager))] internal class HUDManagerPatcher { public static TextMeshProUGUI tipText; public static bool fadeTipText = false; public static TextMeshProUGUI notificationText; public static bool fadeNotificationTextt = false; public static int tipShowTime = 0; public static int notificationShowTime = 0; public static FieldInfo scanNodes = typeof(HUDManager).GetField("scanNodes", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo nodesOnScreen = typeof(HUDManager).GetField("nodesOnScreen", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>(); public static HashSet<string> translatedItems = new HashSet<string>(); private static string lastChat = ""; private static readonly BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo CanTipDisplay = typeof(HUDManager).GetMethod("CanTipDisplay", All); private static MethodInfo StopCoroutine = typeof(HUDManager).GetMethod("StopCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null); private static MethodInfo StartCoroutine = typeof(HUDManager).GetMethod("StartCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null); private static MethodInfo TipsPanelTimer = typeof(HUDManager).GetMethod("TipsPanelTimer", All); private static FieldInfo tipsPanelCoroutine = typeof(HUDManager).GetField("tipsPanelCoroutine", All); [HarmonyPostfix] [HarmonyPatch("Start")] private static void start(ref HUDManager __instance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_006b: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.holdButtonToEndGameEarlyText != (Object)null) { ((Graphic)__instance.holdButtonToEndGameEarlyText).color = Color.yellow; } GameObject val = new GameObject("TipHUDDisplay"); val.AddComponent<RectTransform>(); tipText = val.AddComponent<TextMeshProUGUI>(); RectTransform rectTransform = ((TMP_Text)tipText).rectTransform; ((Transform)rectTransform).SetParent(((Component)__instance.PTTIcon).transform, false); rectTransform.anchoredPosition = new Vector2(310f, -130f); ((TMP_Text)tipText).fontSize = 9f; ((TMP_Text)tipText).overflowMode = (TextOverflowModes)0; ((Behaviour)tipText).enabled = false; notificationText = new GameObject("NotificationHUDDisplay").AddComponent<TextMeshProUGUI>(); RectTransform component = ((Component)notificationText).gameObject.GetComponent<RectTransform>(); ((Transform)component).SetParent(((Component)__instance.PTTIcon).transform, false); component.anchoredPosition = new Vector2(153f, -200f); ((TMP_Text)notificationText).fontStyle = (FontStyles)1; ((TMP_Text)notificationText).fontSize = 10f; ((TMP_Text)notificationText).alignment = (TextAlignmentOptions)514; ((TMP_Text)notificationText).overflowMode = (TextOverflowModes)0; ((Behaviour)notificationText).enabled = false; } [HarmonyPostfix] [HarmonyPatch("Update")] private static void update(HUDManager __instance) { if (TranslatePlugin.shouldTranslateHUD.Value && lastChat.Length != ((TMP_Text)__instance.chatText).text.Length) { ((TMP_Text)__instance.chatText).text = TranslateConfig.hudText.TryTranslateByStrong(((TMP_Text)__instance.chatText).text); } lastChat = ((TMP_Text)__instance.chatText).text; if (tipShowTime > 0) { tipShowTime--; } else if (((Behaviour)tipText).enabled && !fadeTipText) { fadeText(tipText, ref fadeTipText, 200f, 0f); } if (notificationShowTime > 0) { notificationShowTime--; } else if (((Behaviour)notificationText).enabled && !fadeNotificationTextt) { fadeText(notificationText, ref fadeNotificationTextt, 200f, 0f); } } private static void fadeText(TextMeshProUGUI text, ref bool fade, float duration, float newAlpha) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) float a = ((Graphic)text).color.a; float num = 0f; fade = true; while (num < duration) { num += Time.deltaTime; float num2 = Mathf.Lerp(a, newAlpha, num / duration); ((Graphic)text).color = new Color(((Graphic)text).color.r, ((Graphic)text).color.g, ((Graphic)text).color.b, num2); } ((Behaviour)text).enabled = false; fade = false; } public static void addNotification(string text, int time) { ((TMP_Text)notificationText).text = text; ((Behaviour)notificationText).enabled = true; ((TMP_Text)notificationText).alpha = 100f; notificationShowTime = time; } public static void addTip(string text, int time, bool isWarning) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) ((Graphic)tipText).color = (isWarning ? Color.red : Color.yellow); ((TMP_Text)tipText).text = text; ((Behaviour)tipText).enabled = true; ((TMP_Text)tipText).alpha = 100f; tipShowTime = time; } [HarmonyPrefix] [HarmonyPatch("DisplayGlobalNotification")] [HarmonyPriority(int.MaxValue)] private static void changeNotification(HUDManager __instance, ref string displayText) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (TranslatePlugin.shouldTranslateHUD.Value) { displayText = TranslateConfig.hudText.TryTranslateByStrong(displayText); } if (!TranslatePlugin.shouldFixTipErrors.Value) { ((Graphic)__instance.globalNotificationText).color = Color.white; return; } addNotification(displayText, 300); ((Behaviour)__instance.globalNotificationAnimator).enabled = false; ((TMP_Text)__instance.globalNotificationText).text = ""; } [HarmonyPostfix] [HarmonyPatch("AddChatMessage")] private static void changeChatMessage(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped) { if (TranslatePlugin.shouldTranslateHUD.Value) { ((TMP_Text)__instance.chatText).text = TranslateConfig.hudText.TryTranslateByStrong(((TMP_Text)__instance.chatText).text); } } [HarmonyPrefix] [HarmonyPatch("DisplayTip")] [HarmonyPriority(int.MaxValue)] private static bool changeTip(HUDManager __instance, ref string headerText, ref string bodyText, bool isWarning = false, bool useSave = false, string prefsKey = "LC_Tip1") { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if ((headerText.ToLower().Contains("mod list") || bodyText.ToLower().Contains("mod list")) && TranslatePlugin.shouldHideModList.Value) { return false; } if (TranslatePlugin.shouldTranslateHUD.Value) { headerText = TranslateConfig.hudText.TryTranslateByStrong(headerText); bodyText = TranslateConfig.hudText.TryTranslateByStrong(bodyText); } if (TranslatePlugin.shouldFixTipErrors.Value) { return addTip(__instance, headerText, bodyText, isWarning, useSave, prefsKey); } ((Graphic)__instance.tipsPanelHeader).color = Color.white; ((Graphic)__instance.tipsPanelBody).color = Color.white; return true; } private static bool addTip(HUDManager __instance, string headerText, string bodyText, bool isWarning = false, bool useSave = false, string prefsKey = "LC_Tip1") { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and O //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown string text = headerText + "\n" + bodyText; try { if (!(bool)CanTipDisplay.Invoke(__instance, new object[3] { isWarning, useSave, prefsKey })) { return false; } if (useSave) { if ((object)(Coroutine)tipsPanelCoroutine.GetValue(__instance) != null) { StopCoroutine.Invoke(__instance, new object[1] { tipsPanelCoroutine }); } tipsPanelCoroutine.SetValue(__instance, (object?)(Coroutine)StartCoroutine.Invoke(__instance, new object[1] { (IEnumerator)TipsPanelTimer.Invoke(__instance, new object[1] { prefsKey }) })); } ((Behaviour)__instance.tipsPanelAnimator).enabled = false; addTip(text, 320, isWarning); } catch (Exception ex) { TranslatePlugin.logger.LogError((object)ex); } return false; } } [HarmonyPatch(typeof(Terminal))] internal class TerminalPatch { public class Translator { public Dictionary<string, TerminalKeyword> keyValuePairs = new Dictionary<string, TerminalKeyword>(); public Dictionary<string, string> keys = new Dictionary<string, string>(); public TerminalKeyword getTranslateKey(TerminalKeyword old, bool useC) { if (!keyValuePairs.ContainsKey(((Object)old).name + old.word)) { if ((useC && !HaveChineseCMD(old.word)) || (!useC && !HavePinyinCMD(old.word))) { keyValuePairs.Add(((Object)old).name + old.word, old); return old; } string text = (useC ? getChineseCMD(old.word) : getPinyinCMD(old.word)); if (text == old.word) { keyValuePairs.Add(((Object)old).name + old.word, old); return old; } TerminalKeyword val = Copy(old); val.word = text; keyValuePairs.Add(((Object)old).name + old.word, val); if (val.compatibleNouns != null) { List<CompatibleNoun> list = val.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns = val.compatibleNouns; foreach (CompatibleNoun val2 in compatibleNouns) { if (!getTranslateKey(val2.noun, useC).word.Equals(val2.noun.word)) { CompatibleNoun val3 = TransReflection<CompatibleNoun>(val2); val3.noun = getTranslateKey(val2.noun, useC); list.Add(val3); } } val.compatibleNouns = list.ToArray(); } if ((Object)(object)val.defaultVerb != (Object)null && val.defaultVerb.compatibleNouns != null) { List<CompatibleNoun> list2 = val.defaultVerb.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns2 = val.defaultVerb.compatibleNouns; foreach (CompatibleNoun val4 in compatibleNouns2) { if (!getTranslateKey(val4.noun, useC).word.Equals(val4.noun.word)) { CompatibleNoun val5 = TransReflection<CompatibleNoun>(val4); val5.noun = getTranslateKey(val4.noun, useC); list2.Add(val5); } } val.defaultVerb.compatibleNouns = list2.ToArray(); } return val; } return keyValuePairs[((Object)old).name + old.word]; } public void getTranslateKey(TerminalKeyword old) { if (keys.ContainsKey(old.word)) { return; } keys.Add(old.word, old.word); if (old.compatibleNouns != null) { List<CompatibleNoun> list = old.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns = old.compatibleNouns; foreach (CompatibleNoun val in compatibleNouns) { getTranslateKey(val.noun); } } if ((Object)(object)old.defaultVerb != (Object)null && old.defaultVerb.compatibleNouns != null) { List<CompatibleNoun> list2 = old.defaultVerb.compatibleNouns.ToList(); CompatibleNoun[] compatibleNouns2 = old.defaultVerb.compatibleNouns; foreach (CompatibleNoun val2 in compatibleNouns2) { getTranslateKey(val2.noun); } } } } public static FieldInfo hasGottenVerb = typeof(Terminal).GetField("hasGottenVerb", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static FieldInfo hasGottenNoun = typeof(Terminal).GetField("hasGottenNoun", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static bool shouldTranslate = false; public static bool noText = false; public static int texdAdded = 0; public static FieldInfo modifyingText = typeof(Terminal).GetField("modifyingText", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); public static Dictionary<string, string> keyValuePairs = new Dictionary<string, string>(); public static TMP_InputField screenText = null; public static TextTranslationInfo info; public static List<object> ig = new List<object>(); private static int CheckForPlayerNameCommand(string firstWord, string secondWord) { if (firstWord == "radar") { return -1; } if (secondWord.Length <= 2) { return -1; } Debug.Log((object)("first word: " + firstWord + "; second word: " + secondWord)); List<string> list = new List<string>(); for (int i = 0; i < StartOfRound.Instance.mapScreen.radarTargets.Count; i++) { list.Add(StartOfRound.Instance.mapScreen.radarTargets[i].name); Debug.Log((object)$"name {i}: {list[i]}"); } secondWord = secondWord.ToLower(); for (int j = 0; j < list.Count; j++) { string text = list[j].ToLower(); if (text == secondWord) { 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 (getChineseCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { __result = options[i].result; return; } if (getPinyinCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { __result = options[i].result; return; } } } } [HarmonyPostfix] [HarmonyPatch("CheckForExactSentences")] private static void CheckForExactSentences(Terminal __instance, string playerWord, ref TerminalKeyword __result) { for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++) { if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; break; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; break; } } } private static string RemovePunctuation(string s) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in s) { if (!char.IsPunctuation(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString().ToLower(); } [HarmonyPostfix] [HarmonyPatch("CallFunctionInAccessibleTerminalObject")] private static void CallFunctionInAccessibleTerminalObject(Terminal __instance, string word) { TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>(); for (int i = 0; i < array.Length; i++) { if (getChineseCMD(array[i].objectCode).EqualsIgnoreCase(word) || getPinyinCMD(array[i].objectCode).EqualsIgnoreCase(word)) { Debug.Log((object)"Found accessible terminal object with corresponding string, calling function"); ((object)__instance).GetType().GetField("broadcastedCodeThisFrame", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(TranslateConfig.terminal, true); array[i].CallFunctionFromTerminal(); break; } } } [HarmonyPostfix] [HarmonyPatch("ParseWord")] private static void ParseWord(Terminal __instance, string playerWord, int specificityRequired, ref TerminalKeyword __result) { if (!TranslatePlugin.TerimalCanUseChinese.Value && !TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value) { return; } if (playerWord.Length < specificityRequired) { __result = null; return; } TerminalKeyword val = null; for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++) { if (__instance.terminalNodes.allKeywords[i].isVerb && (bool)hasGottenVerb.GetValue(__instance)) { continue; } _ = __instance.terminalNodes.allKeywords[i].accessTerminalObjects; if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; return; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord)) { __result = __instance.terminalNodes.allKeywords[i]; return; } if (!((Object)(object)val == (Object)null)) { continue; } for (int num = playerWord.Length; num > specificityRequired; num--) { if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { val = __instance.terminalNodes.allKeywords[i]; } if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower())) { val = __instance.terminalNodes.allKeywords[i]; } } } if ((Object)(object)val != (Object)null) { __result = val; } } [HarmonyPostfix] [HarmonyPatch("ParsePlayerSentence")] private static void customParser(Terminal __instance, ref TerminalNode __result) { string s = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded); s = RemovePunctuation(s); string[] array = s.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries); if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["transmit"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["transmit"])))) { try { string text = array[1]; SignalTranslator val = Object.FindObjectOfType<SignalTranslator>(); if ((Object)(object)val != (Object)null && Time.realtimeSinceStartup - val.timeLastUsingSignalTranslator > 8f && text.Length > 1) { if (!((NetworkBehaviour)__instance).IsServer) { val.timeLastUsingSignalTranslator = Time.realtimeSinceStartup; } __result = __instance.terminalNodes.specialNodes[22]; HUDManager.Instance.UseSignalTranslatorServerRpc(text.Substring(0, Mathf.Min(text.Length, 10))); } return; } catch (Exception ex) { TranslateConfig.Log(ex.Message); return; } } if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["switch"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["switch"])))) { int num = CheckForPlayerNameCommand(array[0], array[1]); if (num != -1) { StartOfRound.Instance.mapScreen.SwitchRadarTargetAndSync(num); __result = __instance.terminalNodes.specialNodes[20]; } } else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["ping"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["ping"])))) { int num2 = CheckForPlayerNameCommand(array[0], array[1]); if (num2 != -1) { StartOfRound.Instance.mapScreen.PingRadarBooster(num2); __result = __instance.terminalNodes.specialNodes[21]; } } else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["flash"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["flash"])))) { int num3 = CheckForPlayerNameCommand(array[0], array[1]); if (num3 != -1) { StartOfRound.Instance.mapScreen.FlashRadarBooster(num3); __result = __instance.terminalNodes.specialNodes[23]; } else if (StartOfRound.Instance.mapScreen.radarTargets[StartOfRound.Instance.mapScreen.targetTransformIndex].isNonPlayer) { StartOfRound.Instance.mapScreen.FlashRadarBooster(StartOfRound.Instance.mapScreen.targetTransformIndex); __result = __instance.terminalNodes.specialNodes[23]; } } } private static bool HaveChineseCMD(string name) { return TranslateConfig.cmd_zh.normal.ContainsKey(name); } private static bool HavePinyinCMD(string name) { return TranslateConfig.cmd_py.normal.ContainsKey(name); } private static string getChineseCMD(string name) { if (TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey(name)) { return TranslateConfig.cmd_zh.normal[name]; } return ""; } private static string getPinyinCMD(string name) { if (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey(name)) { return TranslateConfig.cmd_py.normal[name]; } return ""; } private static string getOrginByChineseCMD(string name) { if (TranslateConfig.cmd_zh.special.ContainsKey(name)) { return TranslateConfig.cmd_zh.special[name]; } return name; } private static string getOrginByPinyinCMD(string name) { if (TranslateConfig.cmd_py.special.ContainsKey(name)) { return TranslateConfig.cmd_py.special[name]; } return name; } private static T TransReflection<T>(T tIn) { T val = Activator.CreateInstance<T>(); foreach (FieldInfo runtimeField in tIn.GetType().GetRuntimeFields()) { val.GetType().GetRuntimeField(runtimeField.Name).SetValue(val, runtimeField.GetValue(tIn)); } return val; } public static TerminalKeyword Copy(TerminalKeyword oldOne) { return TransReflection<TerminalKeyword>(oldOne); } [HarmonyPostfix] [HarmonyPatch("LoadNewNode")] private static void changeNewNodeText(Terminal __instance, TerminalNode node) { if (info != null) { info.Reset(__instance.screenText.text); } } [HarmonyPrefix] [HarmonyPatch("OnSubmit")] private static void changeSubmit(Terminal __instance) { if (info != null) { texdAdded = __instance.currentText.Length - info.OriginalText.Length; if (texdAdded != 0) { info.Reset(__instance.currentText); } } } [HarmonyPostfix] [HarmonyPatch("Update")] private static void changeUpdateText(Terminal __instance) { try { if ((info == null || !info.IsTranslated) && info != null && !info.IsTranslated && TranslatePlugin.shouldTranslateTerimal.Value) { string translatedText = TranslateConfig.replaceByMap(__instance.currentText, TranslateConfig.terminal); info.SetTranslatedText(translatedText); SetText(info.TranslatedText, __instance); info.OriginalText = __instance.currentText; } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)ex); } } public static void SetText(string text, Terminal Instance) { if (!((Object)(object)Instance == (Object)null)) { modifyingText.SetValue(Instance, true); ((Selectable)Instance.screenText).interactable = true; Instance.screenText.text = text; Instance.currentText = Instance.screenText.text; if ((Object)(object)Instance.screenText.verticalScrollbar != (Object)null) { Instance.screenText.verticalScrollbar.value = 0f; } } } [HarmonyPatch("Start")] [HarmonyPostfix] private static void startTerminal(Terminal __instance) { info = __instance.screenText.GetOrCreateTextTranslationInfo(); ig.Clear(); foreach (FieldInfo runtimeField in ((object)__instance).GetType().GetRuntimeFields()) { if (runtimeField.GetValue(__instance) != null && UnityTypes.TMP_Text.IsAssignableFrom(runtimeField.GetValue(__instance).GetType())) { ig.Add(runtimeField.GetValue(__instance)); } } info.MustIgnore = true; } } } namespace GameTranslator.Patches.Utils { internal static class FontCache { private static readonly Dictionary<int, Font> CachedFonts = new Dictionary<int, Font>(); private static bool _hasReadOverrideFontTextMeshPro = false; private static Object OverrideFontTextMeshPro; private static bool _hasReadFallbackFontTextMeshPro = false; private static Object FallbackFontTextMeshPro; public static Font GetOrCreate(int size) { if (!CachedFonts.TryGetValue(size, out var value)) { value = FontHelper.GetTextFont(size); CachedFonts.Add(size, value); } return value; } public static object GetOrCreateOverrideFontTextMeshPro() { if (!_hasReadOverrideFontTextMeshPro) { try { _hasReadOverrideFontTextMeshPro = true; OverrideFontTextMeshPro = FontHelper.GetTextMeshProFont(TranslatePlugin.DefaultPath + TranslatePlugin.overrideFontTextMeshPro.Value); } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("加载替换字体时发生错误." + Environment.NewLine + ex)); } } return OverrideFontTextMeshPro; } public static object GetOrCreateAlternateFontTextMeshPro() { if (!_hasReadFallbackFontTextMeshPro) { try { _hasReadFallbackFontTextMeshPro = true; FallbackFontTextMeshPro = FontHelper.GetTextMeshProFont(TranslatePlugin.DefaultPath + TranslatePlugin.alternateFontTextMeshPro.Value); } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("加载备用字体时发生错误." + Environment.NewLine + ex)); } } return FallbackFontTextMeshPro; } } internal static class FontHelper { public static Object GetTextMeshProFont(string assetBundle) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown Object val = null; string text = Path.Combine(Paths.GameRoot, assetBundle); if (File.Exists(text)) { TranslatePlugin.logger.LogInfo((object)"正在尝试从目录中读取字体文件."); AssetBundle val2; if (AssetBundle_Methods.LoadFromFile != null) { val2 = (AssetBundle)AssetBundle_Methods.LoadFromFile.Invoke((object)null, new object[1] { text }); } else { if (AssetBundle_Methods.CreateFromFile == null) { TranslatePlugin.logger.LogError((object)("在加载字体时找不到合适的资源包加载方法: " + text)); return null; } val2 = (AssetBundle)AssetBundle_Methods.CreateFromFile.Invoke((object)null, new object[1] { text }); } if ((Object)(object)val2 == (Object)null) { TranslatePlugin.logger.LogError((object)("加载字体时无法加载资源包: " + text)); return null; } if (UnityTypes.TMP_FontAsset != null) { if (AssetBundle_Methods.LoadAllAssets != null) { val = ((Object[])AssetBundle_Methods.LoadAllAssets.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault(); } else if (AssetBundle_Methods.LoadAll != null) { val = ((Object[])AssetBundle_Methods.LoadAll.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault(); } } } else { TranslatePlugin.logger.LogInfo((object)("正在尝试从内部资源API加载TextMesh Pro字体:" + text)); val = Resources.Load(assetBundle); } if (val != (Object)null) { CachedProperty version = TMP_FontAsset_Properties.Version; string text2 = ((string)((version != null) ? version.Get((object)val) : null)) ?? "未知"; TranslatePlugin.logger.LogInfo((object)("加载的TextMesh Pro字体版本为: " + text2)); Object.DontDestroyOnLoad(val); } else { TranslatePlugin.logger.LogError((object)("找不到TextMeshPro字体资源: " + assetBundle)); } return val; } public static Font GetTextFont(int size) { Font val = Font.CreateDynamicFontFromOSFont(TranslatePlugin.overrideFont.Value, size); Object.DontDestroyOnLoad((Object)(object)val); return val; } public static string[] GetOSInstalledFontNames() { return Font.GetOSInstalledFontNames(); } } public class StringBuffer { private char[] value; private int length; private int capacity; private const int DEFAULT_CAPACITY = 16; public int Length { get { return length; } set { if (value < 0 || value > capacity) { throw new ArgumentOutOfRangeException("value"); } if (value < length) { Array.Clear(this.value, value, length - value); } length = value; } } public int Capacity { get { return capacity; } set { if (value < length) { throw new ArgumentOutOfRangeException("value"); } if (value != capacity) { char[] destinationArray = new char[value]; Array.Copy(this.value, 0, destinationArray, 0, length); this.value = destinationArray; capacity = value; } } } public StringBuffer() { value = new char[16]; length = 0; capacity = 16; } public StringBuffer(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity"); } value = new char[capacity]; length = 0; this.capacity = capacity; } public StringBuffer(string str) { if (str == null) { throw new ArgumentNullException("str"); } value = new char[str.Length + 16]; str.CopyTo(0, value, 0, str.Length); length = str.Length; capacity = str.Length + 16; } public void EnsureCapacity(int minimumCapacity) { if (minimumCapacity < 0) { throw new ArgumentOutOfRangeException("minimumCapacity"); } if (minimumCapacity > capacity) { int num = capacity * 2; if (num < minimumCapacity) { num = minimumCapacity; } Capacity = num; } } public StringBuffer Append(object obj) { if (obj == null) { return this; } return Append(obj.ToString()); } public StringBuffer Append(string str) { if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); str.CopyTo(0, value, length, num); length += num; return this; } public StringBuffer Append(char c) { EnsureCapacity(length + 1); value[length] = c; length++; return this; } public StringBuffer Insert(int index, object obj) { if (obj == null) { return this; } return Insert(index, obj.ToString()); } public StringBuffer Insert(int index, string str) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } if (str == null) { return this; } int num = str.Length; EnsureCapacity(length + num); Array.Copy(value, index, value, index + num, length - index); str.CopyTo(0, value, index, num); length += num; return this; } public StringBuffer Insert(int index, char c) { if (index < 0 || index > length) { throw new ArgumentOutOfRangeException("index"); } EnsureCapacity(length + 1); Array.Copy(value, index, value, index + 1, length - index); value[index] = c; length++; return this; } public StringBuffer Remove(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } Array.Copy(value, startIndex + length, value, startIndex, this.length - startIndex - length); Array.Clear(value, this.length - length, length); this.length -= length; return this; } public StringBuffer Replace(char oldChar, char newChar) { for (int i = 0; i < length; i++) { if (value[i] == oldChar) { value[i] = newChar; } } return this; } public StringBuffer Replace(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOf(oldValue); num3 >= 0; num3 = IndexOf(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } public int IndexOf(char c) { return IndexOf(c, 0, length); } public int IndexOf(char c, int startIndex) { return IndexOf(c, startIndex, length - startIndex); } public int IndexOf(char c, int startIndex, int count) { if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return Array.IndexOf(value, c, startIndex, count); } public int IndexOf(string str) { return IndexOf(str, 0, length); } public int IndexOf(string str, int startIndex) { return IndexOf(str, startIndex, length - startIndex); } public int IndexOf(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } return value.ToString(startIndex, count).IndexOf(str); } public string Substring(int startIndex) { return Substring(startIndex, length - startIndex); } public string Substring(int startIndex, int length) { if (startIndex < 0 || startIndex > this.length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > this.length) { throw new ArgumentOutOfRangeException("length"); } return new string(value, startIndex, length); } public bool Contains(string text) { return IndexOf(text) >= 0; } public StringBuffer ReplaceFull(string oldValue, string newValue) { if (oldValue == null) { throw new ArgumentNullException("oldValue"); } if (oldValue.Length == 0) { throw new ArgumentException("oldValue cannot be empty"); } if (newValue == null) { newValue = string.Empty; } int num = oldValue.Length; int num2 = newValue.Length; for (int num3 = IndexOfWord(oldValue); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2)) { Remove(num3, num); Insert(num3, newValue); } return this; } private int IndexOfWord(string str) { return IndexOfWord(str, 0, length); } private int IndexOfWord(string str, int startIndex) { return IndexOfWord(str, startIndex, length - startIndex); } public int IndexOfWord(string str, int startIndex, int count) { if (str == null) { throw new ArgumentNullException("str"); } if (startIndex < 0 || startIndex > length) { throw new ArgumentOutOfRangeException("startIndex"); } if (count < 0 || startIndex + count > length) { throw new ArgumentOutOfRangeException("count"); } int num = str.Length; int[] array = new int[num]; int num2 = 0; computeLPSArray(str, num, array); int num3 = startIndex; while (num3 < startIndex + count) { if (str[num2] == value[num3]) { num2++; num3++; } if (num2 == num) { if ((num3 - num2 == 0 || !char.IsLetter(value[num3 - num2 - 1])) && (num3 == length || !char.IsLetter(value[num3]))) { return num3 - num2; } num2 = array[num2 - 1]; } else if (num3 < startIndex + count && str[num2] != value[num3]) { if (num2 != 0) { num2 = array[num2 - 1]; } else { num3++; } } } return -1; } private void computeLPSArray(string str, int M, int[] lps) { int num = 0; int num2 = 1; lps[0] = 0; while (num2 < M) { if (str[num2] == str[num]) { num = (lps[num2] = num + 1); num2++; } else if (num != 0) { num = lps[num - 1]; } else { lps[num2] = num; num2++; } } } private static bool IsWordChar(char c) { return char.IsLetter(c) || c == '_'; } public StringBuffer Clear() { Length = 0; return this; } public override string ToString() { return new string(value, 0, length); } } internal static class TextHelper { public static string Encode(string text) { return EscapeNewlines(text); } public static string[] ReadTranslationLineAndDecode(string str) { if (string.IsNullOrEmpty(str)) { return null; } string[] array = new string[2]; int num = 0; bool flag = false; int length = str.Length; StringBuilder stringBuilder = new StringBuilder((int)((double)length / 1.3)); for (int i = 0; i < length; i++) { char c = str[i]; if (flag) { switch (c) { case '=': case '\\': stringBuilder.Append(c); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 'u': { int num2 = i + 4; if (num2 < length) { int num3 = int.Parse(new string(new char[4] { str[i + 1], str[i + 2], str[i + 3], str[i + 4] }), NumberStyles.HexNumber); stringBuilder.Append((char)num3); i += 4; break; } throw new Exception("Found invalid unicode in line: " + str); } default: stringBuilder.Append('\\'); stringBuilder.Append(c); 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 '%': { int num5 = i + 2; if (num5 < length && str[i + 1] == '3' && str[i + 2] == 'D') { stringBuilder.Append('='); i += 2; } else { stringBuilder.Append(c); } break; } case '/': { int num4 = i + 1; if (num4 < length && str[num4] == '/') { array[num++] = stringBuilder.ToString(); if (num == 2) { return array; } return null; } stringBuilder.Append(c); break; } default: stringBuilder.Append(c); break; } } if (num != 1) { return null; } array[num++] = stringBuilder.ToString(); return array; } internal static string EscapeNewlines(string str) { if (str == null || str.Length == 0) { return ""; } int length = str.Length; StringBuilder stringBuilder = new StringBuilder(length + 4); for (int i = 0; i < length; i++) { char c = str[i]; switch (c) { case '/': { int num = i + 1; if (num < length && str[num] == '/') { stringBuilder.Append('\\'); stringBuilder.Append(c); stringBuilder.Append('\\'); stringBuilder.Append(c); i++; } else { stringBuilder.Append(c); } break; } case '\\': stringBuilder.Append('\\'); stringBuilder.Append(c); break; case '=': stringBuilder.Append('\\'); stringBuilder.Append(c); break; case '\n': stringBuilder.Append("\\n"); break; case '\r': stringBuilder.Append("\\r"); break; default: stringBuilder.Append(c); break; } } return stringBuilder.ToString(); } } internal class TextTranslate { public static TextTranslate Instance = new TextTranslate(); public static long ChangeTime = 0L; internal void Hook_TextChanged(object ui) { if (TerminalPatch.ig == null || !TerminalPatch.ig.Contains(ui)) { TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo); if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value) { TranslateImmediate(ui, null, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState); } } } internal void Hook_TextChanged(object ui, ref string value) { if (TerminalPatch.ig == null || !TerminalPatch.ig.Contains(ui)) { TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo(); bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo); if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value) { value = TranslateImmediate(ui, value, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState); } } } public string TranslateImmediate(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState) { if (info != null && (info.IsCurrentlySettingText || info.MustIgnore)) { return text; } text = text ?? ui.GetText(info); if (Utility.IsNullOrWhiteSpace(text)) { return text; } if (info != null && info.IsTranslated) { if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime) { info.Reset(text); } else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text)) { return info.TranslatedText; } } string text2 = text; if ((normalText == null || normalText.IsTranslatable(text, isToken: false)) && (ignoreComponentState || ui.IsComponentActive())) { if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value) { text2 = normalText.TryTranslate(text2); } text2 = TranslateConfig.replaceByMap(text2, config); SetTranslatedText(ui, text2, text, info); } return text2; } internal void SetTranslatedText(object ui, string translatedText, string originalText, TextTranslationInfo info) { if (info != null) { info.OriginalText = originalText; info.SetTranslatedText(translatedText); } SetText(ui, translatedText, isTranslated: true, originalText, info); } private void SetText(object ui, string text, bool isTranslated, string originalText, TextTranslationInfo info) { if (info == null || !info.IsCurrentlySettingText) { if (info != null) { info.IsCurrentlySettingText = true; } ui.SetText(text, info); if (info != null) { info.IsCurrentlySettingText = false; } } } public bool DiscoverComponent(object ui, TextTranslationInfo info) { if (info == null || !TranslatePlugin.changeFont.Value) { return true; } try { bool flag = ui.IsComponentActive(); if (TranslatePlugin.overrideFontTextMeshPro.Value != null && flag) { info.ChangeFont(ui); return true; } return flag; } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("处理UI时发生错误." + Environment.NewLine + ex)); } return false; } } public class TextureTranslate { public static TextureTranslate Instance = new TextureTranslate(); public static bool ImageHooksEnabled = true; private static int ixs = 0; internal void Hook_ImageChangedOnComponent(object source, ref Texture2D texture, bool isPrefixHooked, bool onEnable = false) { if (ImageHooksEnabled && TranslatePlugin.changeTexture.Value && source.IsKnownImageType()) { Sprite sprite = null; HandleImage(source, ref sprite, ref texture, isPrefixHooked); } } internal void Hook_ImageChangedOnComponent(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked, bool onEnable) { if (ImageHooksEnabled && source.IsKnownImageType()) { HandleImage(source, ref sprite, ref texture, isPrefixHooked); } } internal void Hook_ImageChanged(ref Texture2D texture, bool isPrefixHooked) { if (ImageHooksEnabled && !((Object)(object)texture == (Object)null)) { Sprite sprite = null; HandleImage(null, ref sprite, ref texture, isPrefixHooked); } } public void HandleImage(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked) { bool flag = true; try { TranslateTexture(source, ref sprite, ref texture, isPrefixHooked); } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while translating texture."); } } private void TranslateTexture(object ui, ref Sprite sprite) { Texture2D texture = default(Texture2D); if (ObjectExtensions.TryCastTo<Texture2D>(ui, ref texture)) { TranslateTexture(null, ref sprite, ref texture, isPrefixHooked: false); return; } Texture2D texture2 = null; TranslateTexture(ui, ref sprite, ref texture2, isPrefixHooked: false); } private void TranslateTexture(object ui) { Sprite sprite = null; Texture2D texture = default(Texture2D); if (ObjectExtensions.TryCastTo<Texture2D>(ui, ref texture)) { TranslateTexture(null, ref sprite, ref texture, isPrefixHooked: false); return; } Texture2D texture2 = null; TranslateTexture(ui, ref sprite, ref texture2, isPrefixHooked: false); } private void TranslateTexture(object source, ref Sprite sprite, ref Texture2D texture, bool isPrefixHooked) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) ImageHooksEnabled = false; try { texture = texture ?? source.GetTexture(); if (!((Object)(object)texture != (Object)null)) { return; } TextureTranslationInfo orCreateTextureTranslationInfo = texture.GetOrCreateTextureTranslationInfo(); string key = orCreateTextureTranslationInfo.GetKey(); if (!string.IsNullOrEmpty(key) && TranslateConfig.cache.TryGetTranslatedImage(key, out var data, out var image)) { try { Texture2D val = Texture2D.CreateExternalTexture(((Texture)texture).width, ((Texture)texture).height, texture.format, false, false, ((Texture)texture).GetNativeTexturePtr()); ImageConversion.LoadImage(val, data); texture = val; orCreateTextureTranslationInfo.SetTranslated(val); source.SetTexture(image.texture, sprite, isPrefixHooked: true); return; } finally { orCreateTextureTranslationInfo.IsTranslated = true; } } } finally { ImageHooksEnabled = true; } } } internal static class TranslationScopeHelper { internal static class TranslationScopes { public const int None = -1; } public static bool EnableTranslationScoping = true; public static int GetScope(object ui) { if (EnableTranslationScoping) { try { Component val = (Component)((ui is Component) ? ui : null); if (val != null && Object.op_Implicit((Object)(object)val)) { return GetScopeFromComponent(val); } GUIContent val2 = (GUIContent)((ui is GUIContent) ? ui : null); if (val2 != null) { return -1; } return GetActiveSceneId(); } catch (MissingMemberException ex) { XuaLogger.AutoTranslator.Error((Exception)ex, "A 'missing member' error occurred while retriving translation scope. Disabling translation scopes."); EnableTranslationScoping = false; } } return -1; } private static int GetScopeFromComponent(Component component) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).buildIndex; } public static int GetActiveSceneId() { if (UnityFeatures.SupportsSceneManager) { return GetActiveSceneIdBySceneManager(); } return GetActiveSceneIdByApplication(); } private static int GetActiveSceneIdBySceneManager() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).buildIndex; } public static void RegisterSceneLoadCallback(Action<int> sceneLoaded) { SceneManager_Methods.add_sceneLoaded(delegate(Scene scene, LoadSceneMode mode) { sceneLoaded(((Scene)(ref scene)).buildIndex); }); SceneManagerLoader.EnableSceneLoadScanInternal(sceneLoaded); } private static int GetActiveSceneIdByApplication() { return Application.loadedLevel; } } internal static class SceneManagerLoader { public static void EnableSceneLoadScanInternal(Action<int> sceneLoaded) { SceneManager.sceneLoaded += delegate(Scene arg1, LoadSceneMode arg2) { sceneLoaded(((Scene)(ref arg1)).buildIndex); }; } } } namespace GameTranslator.Patches.Translatons { internal class ImageTranslationInfo { public bool IsTranslated { get; set; } public WeakReference<Texture2D> Original { get; private set; } public void Initialize(Texture2D texture) { Original = WeakReference<Texture2D>.Create(texture); } public void Reset(Texture2D newTexture) { IsTranslated = false; Original = WeakReference<Texture2D>.Create(newTexture); } } public class NormalTextTranslator { public Dictionary<string, string> _translations = new Dictionary<string, string>(); public Dictionary<string, string> _strongTranslations = new Dictionary<string, string>(); public Dictionary<string, string> _reverseTranslations = new Dictionary<string, string>(); public Dictionary<string, string> _tokenTranslations = new Dictionary<string, string>(); public Dictionary<string, string> _reverseTokenTranslations = new Dictionary<string, string>(); private HashSet<string> _partialTranslations = new HashSet<string>(); public List<RegexTranslation> _defaultRegexes = new List<RegexTranslation>(); private HashSet<string> _registeredRegexes = new HashSet<string>(); public List<RegexTranslationSplitter> _splitterRegexes = new List<RegexTranslationSplitter>(); public HashSet<string> _registeredSplitterRegexes = new HashSet<string>(); private FileSystemWatcher _fileWatcher; public string FileName; public string FilePath; public static Dictionary<string, NormalTextTranslator> keyValuePairs = new Dictionary<string, NormalTextTranslator>(); public NormalTextTranslator(string fileName) { try { FileName = fileName; FilePath = TranslatePlugin.DefaultPath + fileName; _fileWatcher = new FileSystemWatcher(); _fileWatcher.Path = Path.GetFullPath(TranslatePlugin.DefaultPath); _fileWatcher.Filter = FileName; _fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.Security; _fileWatcher.Changed += OnFileChange; _fileWatcher.IncludeSubdirectories = true; _fileWatcher.EnableRaisingEvents = true; _fileWatcher.Changed += OnFileChange; keyValuePairs.Add(FileName, this); } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)"在初始化翻译文件时发生错误!"); TranslatePlugin.logger.LogError((object)ex); } } private static void OnFileChange(object sender, FileSystemEventArgs e) { if (e.ChangeType != WatcherChangeTypes.Created && e.ChangeType != WatcherChangeTypes.Changed) { return; } foreach (NormalTextTranslator value in keyValuePairs.Values) { if (value.FileName.StartsWith(e.Name)) { value.Load(); } } TextTranslate.ChangeTime++; } public void Load() { TranslatePlugin.logger.LogInfo((object)("--- 正在载入" + FileName + "文件 ---")); try { _defaultRegexes.Clear(); _translations.Clear(); _reverseTranslations.Clear(); _partialTranslations.Clear(); _tokenTranslations.Clear(); _reverseTokenTranslations.Clear(); _splitterRegexes.Clear(); LoadTranslationsInStream(FilePath, FileName, isOutputFile: false, isLoad: true); if (!FileName.StartsWith("HUD-Translate")) { return; } foreach (string key in TranslateConfig.items.normal.Keys) { _strongTranslations[key] = TranslateConfig.items.normal[key]; } } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("在加载" + FileName + "文件时发生错误!")); TranslatePlugin.logger.LogError((object)ex); } } private void LoadTranslationsInStream(string stream, string fullFileName, bool isOutputFile, bool isLoad) { if (isLoad) { TranslatePlugin.logger.LogInfo((object)("正在加载文本文件: " + stream + ".")); } StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); string[] array = streamReader.ReadToEnd().Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { try { string[] array3 = TextHelper.ReadTranslationLineAndDecode(text); if (array3 == null) { continue; } string text2 = array3[0]; string value = array3[1]; if (string.IsNullOrEmpty(text2) || string.IsNullOrEmpty(value)) { continue; } if (text2.StartsWith("sr:")) { try { RegexTranslationSplitter regex = new RegexTranslationSplitter(text2, value); AddTranslationSplitterRegex(regex); } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("在构造正则表达式翻译分割器时发生错误: '" + text + "'." + Environment.NewLine + ex)); } } else if (text2.StartsWith("r:")) { try { RegexTranslation regex2 = new RegexTranslation(text2, value); AddTranslationRegex(regex2); } catch (Exception ex2) { TranslatePlugin.logger.LogWarning((object)("在构造正则表达式转换时发生错误: '" + text + "'." + Environment.NewLine + ex2)); } } else if (text2.StartsWith("sm:")) { try { text2 = text2.Substring(4, text2.Length - 5); AddStrongTranslation(text2, value); } catch (Exception ex3) { TranslatePlugin.logger.LogWarning((object)("在添加强匹配文本时发生错误: " + Environment.NewLine + ex3)); } } else { AddTranslation(text2, value); } } catch (Exception ex4) { TranslatePlugin.logger.LogWarning((object)("在读取翻译时发生错误: '" + text + "'." + Environment.NewLine + ex4)); } } streamReader.Close(); } private void AddStrongTranslation(string key, string value) { if (key != null && value != null) { _strongTranslations[key] = value; _reverseTranslations[value] = key; } } private void AddTranslation(string key, string value) { if (key != null && value != null) { _translations[key] = value; _reverseTranslations[value] = key; } } private void AddTranslationSplitterRegex(RegexTranslationSplitter regex) { if (!_registeredSplitterRegexes.Contains(regex.Original)) { _registeredSplitterRegexes.Add(regex.Original); _splitterRegexes.Add(regex); } } private void AddTranslationRegex(RegexTranslation regex) { if (!_registeredRegexes.Contains(regex.Original)) { _registeredRegexes.Add(regex.Original); _defaultRegexes.Add(regex); } } private bool HasTranslated(string key) { return _translations.ContainsKey(key); } private bool IsTranslation(string translation) { if (_reverseTranslations.ContainsKey(translation)) { return true; } return false; } private bool IsTokenTranslation(string translation) { return _reverseTokenTranslations.ContainsKey(translation); } public bool IsTranslatable(string text, bool isToken) { bool flag = !IsTranslation(text); if (isToken && flag) { flag = !IsTokenTranslation(text); } return flag; } private string ChangeRegex(string text) { Regex regex = new Regex("\\$([0-9]+)"); return regex.Replace(text, ReplaceFunc); static string ReplaceFunc(Match match) { int num = int.Parse(match.Groups[1].Value); return "{" + (num - 1) + "}"; } } public string SplitterTranslate(string text, RegexTranslationSplitter splitter, bool strong, bool ignoreCase) { StringBuilder stringBuilder = new StringBuilder(text); if (splitter.CompiledRegex != null) { MatchCollection matchCollection = splitter.CompiledRegex.Matches(text); foreach (Match item in matchCollection) { if (!item.Success) { continue; } List<string> list = new List<string>(); string[] array = new string[item.Groups.Count]; for (int i = 1; i < item.Groups.Count; i++) { string value = item.Groups[i].Value; StringBuffer stringBuffer = new StringBuffer(value); if (strong) { foreach (string key in _strongTranslations.Keys) { if (value.Contains(key)) { stringBuffer.ReplaceFull(key, _strongTranslations[key]); } } } else { foreach (string key2 in _translations.Keys) { if (value.Contains(key2)) { stringBuffer.ReplaceFull(key2, _translations[key2]); } } } array[i - 1] = stringBuffer.ToString(); } string format = Regex.Unescape(getRegex(ChangeRegex(splitter.Translation))); object[] args = array; string newValue = string.Format(format, args); stringBuilder = stringBuilder.Replace(item.Value, newValue); } } return stringBuilder.ToString(); } public static string getRegex(string pattern) { StringBuilder stringBuilder = new StringBuilder(pattern); stringBuilder.Replace("\\=", "\\\\="); return stringBuilder.ToString(); } public string SplitterTranslate(string text, RegexTranslationSplitter splitter, bool strong) { return SplitterTranslate(text, splitter, strong, ignoreCase: false); } public string TryTranslateByStrong(string text) { string text2 = text; try { StringBuilder stringBuilder = new StringBuilder(text2); foreach (string key in _translations.Keys) { stringBuilder.Replace(key, _translations[key]); } text2 = stringBuilder.ToString(); } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"弱匹配翻译出现问题."); } try { foreach (string key2 in _translations.Keys) { if (text2.Trim().Equals(key2.Trim())) { text2 = _translations[key2]; } } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"强匹配出现问题."); } try { foreach (RegexTranslationSplitter splitterRegex in _splitterRegexes) { text2 = SplitterTranslate(text2, splitterRegex, strong: true, ignoreCase: true); } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"sr翻译出现问题."); } try { foreach (RegexTranslation defaultRegex in _defaultRegexes) { if (defaultRegex.CompiledRegex != null) { Match match = defaultRegex.CompiledRegex.Match(text2); if (match.Success) { text2 = defaultRegex.CompiledRegex.Replace(text2, defaultRegex.Translation); } } } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"r翻译出现问题."); } return text2; } public string TryTranslate(string text) { string text2 = text; try { foreach (string key in _translations.Keys) { if (text2.Trim().Equals(key.Trim())) { text2 = _translations[key]; } } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"强匹配翻译出现问题."); } try { StringBuilder stringBuilder = new StringBuilder(text2); foreach (string key2 in _strongTranslations.Keys) { stringBuilder.Replace(key2, _strongTranslations[key2]); } text2 = stringBuilder.ToString(); } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"弱匹配翻译出现问题."); } try { foreach (RegexTranslationSplitter splitterRegex in _splitterRegexes) { text2 = SplitterTranslate(text2, splitterRegex, strong: false); } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"sr翻译出现问题."); } try { foreach (RegexTranslation defaultRegex in _defaultRegexes) { if (defaultRegex.CompiledRegex != null) { Match match = defaultRegex.CompiledRegex.Match(text2); if (match.Success) { text2 = defaultRegex.CompiledRegex.Replace(text2, defaultRegex.Translation); } } } } catch (Exception) { TranslatePlugin.logger.LogInfo((object)"r翻译出现问题."); } return text2; } } public class RegexTranslation { public Regex CompiledRegex { get; set; } public string Original { get; set; } public string Translation { get; set; } public string Key { get; set; } public string Value { get; set; } public RegexTranslation(string key, string value) { Key = key; Value = value; if (key.StartsWith("r:")) { key = key.Substring(2, key.Length - 2); } int num = key.IndexOf('"'); if (num != -1) { num++; int num2 = key.LastIndexOf('"'); if (num2 == num - 1) { throw new Exception("Splitter regex with key: '" + Key + "' starts with a \" but does not end with a \"."); } key = key.Substring(num, num2 - num); } if (value.StartsWith("r:")) { value = value.Substring(2, value.Length - 2); } num = value.IndexOf('"'); if (num != -1) { num++; int num3 = value.LastIndexOf('"'); if (num3 == num - 1) { throw new Exception("Splitter regex with value: '" + Value + "' starts with a \" but does not end with a \"."); } value = value.Substring(num, num3 - num); } key = key.Replace("^", ""); CompiledRegex = new Regex(key); Original = key; Translation = value; } } public class RegexTranslationSplitter { public Regex CompiledRegex { get; set; } public string Original { get; set; } public string Translation { get; set; } public string Key { get; set; } public string Value { get; set; } public RegexTranslationSplitter(string key, string value) { Key = key; Value = value; if (key.StartsWith("sr:")) { key = key.Substring(3, key.Length - 3); } if (key.EndsWith("$\"")) { key = key.Replace("$\"", "\""); } int num = key.IndexOf('"'); if (num != -1) { num++; int num2 = key.LastIndexOf('"'); if (num2 == num - 1) { throw new Exception("Regex with key: '" + Key + "' starts with a \" but does not end with a \"."); } key = key.Substring(num, num2 - num); } if (value.StartsWith("sr:")) { value = value.Substring(3, value.Length - 3); } num = value.IndexOf('"'); if (num != -1) { num++; int num3 = value.LastIndexOf('"'); if (num3 == num - 1) { throw new Exception("Regex with value: '" + Value + "' starts with a \" but does not end with a \"."); } value = value.Substring(num, num3 - num); } if (key.EndsWith("$")) { key = key.Substring(0, key.Length - 1); } key = key.Replace("^", ""); CompiledRegex = new Regex(key); Original = key; Translation = value; } } internal class TextTranslationInfo { private Action<object> _unfont; private bool _initialized = false; private HashSet<string> _redirectedTranslations; public long changeTime = 0L; public static TMP_FontAsset origin; public ITextComponentManipulator TextManipulator { get; set; } public string OriginalText { get; set; } public string TranslatedText { get; set; } public bool IsTranslated { get; set; } public bool IsCurrentlySettingText { get; set; } public bool IsStabilizingText { get; set; } public bool IsKnownTextComponent { get; set; } public bool SupportsStabilization { get; set; } public bool ShouldIgnore { get; set; } public bool MustIgnore { get; set; } public long ChangeTime { get { return changeTime; } set { changeTime = value; } } public HashSet<string> RedirectedTranslations => _redirectedTranslations ?? (_redirectedTranslations = new HashSet<string>()); public void Init(object ui) { if (!_initialized) { _initialized = true; MustIgnore = false; ShouldIgnore = ui.ShouldIgnoreTextComponent(); TextManipulator = ui.GetTextManipulator(); } } public void Reset(string newText) { IsTranslated = false; TranslatedText = null; OriginalText = newText; ChangeTime = TextTranslate.ChangeTime; } public void SetTranslatedText(string translatedText) { IsTranslated = true; TranslatedText = translatedText; } public static TMP_FontAsset getOriginTMPFont(TMP_FontAsset font) { if ((Object)(object)origin == (Object)null) { origin = font; foreach (char shouldRemoveChar in TranslatePlugin.getShouldRemoveChars()) { origin.TryRemoveCharacter(shouldRemoveChar); } } return origin; } public void ChangeFont(object ui) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Expected O, but got Unknown if (ui == null) { return; } Type unityType = ui.GetUnityType(); if (UnityTypes.Text != null && UnityTypes.Text.IsAssignableFrom(unityType)) { if (string.IsNullOrEmpty(TranslatePlugin.overrideFont.Value)) { return; } try { CachedProperty Text_fontProperty = Text_Properties.Font; Font previousFont = (Font)Text_fontProperty.Get(ui); CachedProperty fontSize = Font_Properties.FontSize; Font orCreate = FontCache.GetOrCreate((int)((fontSize != null) ? fontSize.Get((object)previousFont) : null)); if (!((Object)(object)orCreate == (Object)null) && !((Object)(object)previousFont == (Object)null) && !UnityObjectReferenceComparer.Default.Equals((object)orCreate, (object)previousFont)) { Text_fontProperty.Set(ui, (object)orCreate); _unfont = delegate(object obj) { Text_fontProperty.Set(obj, (object)previousFont); }; } return; } catch (Exception ex) { TranslatePlugin.logger.LogWarning((object)("更换字体时出现问题!" + ex.Message)); return; } } if (((UnityTypes.TextMeshPro == null || !UnityTypes.TextMeshPro.IsAssignableFrom(unityType)) && (UnityTypes.TextMeshProUGUI == null || !UnityTypes.TextMeshProUGUI.IsAssignableFrom(unityType))) || string.IsNullOrEmpty(TranslatePlugin.overrideFontTextMeshPro.Value)) { return; } try { CachedProperty val = ReflectionCache.CachedProperty(unityType, "font"); TMP_FontAsset originTMPFont = getOriginTMPFont((TMP_FontAsset)val.Get(ui)); TMP_FontAsset val2 = (TMP_FontAsset)FontCache.GetOrCreateOverrideFontTextMeshPro(); if ((Object)(object)val2 == (Object)null || (Object)(object)originTMPFont == (Object)null) { return; } CachedProperty val3 = ReflectionCache.CachedProperty(typeof(TMP_FontAsset), "m_CharacterTable"); List<TMP_Character> list = new List<TMP_Character>(); originTMPFont.fallbackFontAssetTable.Add(val2); if (!StringExtensions.IsNullOrWhiteSpace(TranslatePlugin.alternateFontTextMeshPro.Value)) { TMP_FontAsset val4 = (TMP_FontAsset)FontCache.GetOrCreateAlternateFontTextMeshPro(); if ((Object)(object)val4 != (Object)null) { originTMPFont.fallbackFontAssetTable.Add(val4); } } val.Set(ui, (object)originTMPFont); } catch (Exception ex2) { TranslatePlugin.logger.LogWarning((object)("更换字体时出现问题!" + ex2.Message)); } } public void UnchangeFont(object ui) { if (ui != null) { _unfont?.Invoke(ui); _unfont = null; } } } internal class TextureDataResult { public byte[] Data { get; } public bool NonReadable { get; } public float CalculationTime { get; set; } public TextureDataResult(byte[] data, bool nonReadable, float calculationTime) { Data = data; NonReadable = nonReadable; CalculationTime = calculationTime; } } public class TextureTranslationCache { internal class FileSystemTranslatedImageSource : TranslatedImage.ITranslatedImageSource { private readonly string _fileName; public FileSystemTranslatedImageSource(string fileName) { _fileName = fileName; } public byte[] GetData() { using FileStream fileStream = File.OpenRead(_fileName); return StreamExtensions.ReadFully((Stream)fileStream, 16384); } } internal static class HashHelper { private static readonly SHA1Managed SHA1 = new SHA1Managed(); private static readonly uint[] Lookup32 = CreateLookup32(); public static string Compute(byte[] data) { return ByteArrayToHexViaLookup32(SHA1.ComputeHash(data)).Substring(0, 10); } private static uint[] CreateLookup32() { uint[] array = new uint[256]; for (int i = 0; i < 256; i++) { string text = i.ToString("X2"); array[i] = text[0] + ((uint)text[1] << 16); } return array; } private static string ByteArrayToHexViaLookup32(byte[] bytes) { uint[] lookup = Lookup32; char[] array = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { uint num = lookup[bytes[i]]; array[2 * i] = (char)num; array[2 * i + 1] = (char)(num >> 16); } return new string(array); } } public Dictionary<string, TranslatedImage> _translatedImages = new Dictionary<string, TranslatedImage>(StringComparer.InvariantCultureIgnoreCase); public HashSet<string> _untranslatedImages = new HashSet<string>(); private Dictionary<string, string> _keyToFileName = new Dictionary<string, string>(); private bool _disposed; public TextureTranslationCache() { try { Directory.CreateDirectory(TranslatePlugin.TexturesPath); } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while initializing translation file watching for textures."); } } private void FileWatcher_DirectoryUpdated() { } private IEnumerable<string> GetTextureFiles() { return from x in Directory.GetFiles(TranslatePlugin.TexturesPath, "*.*", SearchOption.AllDirectories) where x.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) select x; } public void LoadTranslationFiles() { try { float realtimeSinceStartup = Time.realtimeSinceStartup; _translatedImages.Clear(); _untranslatedImages.Clear(); _keyToFileName.Clear(); Directory.CreateDirectory(TranslatePlugin.TexturesPath); foreach (string textureFile in GetTextureFiles()) { RegisterImageFromFile(textureFile); } float realtimeSinceStartup2 = Time.realtimeSinceStartup; XuaLogger.AutoTranslator.Debug($"Loaded texture files (took {Math.Round(realtimeSinceStartup2 - realtimeSinceStartup, 2)} seconds)"); } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while loading translations."); } } private void RegisterImageFromStream(string fullFileName, TranslatedImage.ITranslatedImageSource source) { try { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullFileName); int num = fileNameWithoutExtension.LastIndexOf("["); int num2 = fileNameWithoutExtension.LastIndexOf("]"); if (num2 > -1 && num > -1 && num2 > num) { int num3 = num + 1; string[] array = fileNameWithoutExtension.Substring(num3, num2 - num3).Split(new char[1] { '-' }); string key; string x; if (array.Length == 1) { key = array[0]; x = array[0]; } else { if (array.Length != 2) { XuaLogger.AutoTranslator.Warn("Image not loaded (unknown hash): " + fullFileName + "."); return; } key = array[0]; x = array[1]; } byte[] data = source.GetData(); string y = HashHelper.Compute(data); bool flag = StringComparer.InvariantCultureIgnoreCase.Compare(x, y) != 0; _keyToFileName[key] = fullFileName; if (flag) { RegisterTranslatedImage(fullFileName, key, data); XuaLogger.AutoTranslator.Debug("Image loaded: " + fullFileName + "."); } else { RegisterUntranslatedImage(key); XuaLogger.AutoTranslator.Warn("Image not loaded (unmodified): " + fullFileName + "."); } } else { XuaLogger.AutoTranslator.Warn("Image not loaded (no hash): " + fullFileName + "."); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while loading texture file: " + fullFileName); } } private void RegisterImageFromFile(string fullFileName) { if (File.Exists(fullFileName)) { FileSystemTranslatedImageSource source = new FileSystemTranslatedImageSource(fullFileName); RegisterImageFromStream(fullFileName, source); } } public void RenameFileWithKey(string name, string key, string newKey) { try { if (_keyToFileName.TryGetValue(key, out var value)) { _keyToFileName.Remove(key); value.Replace(key, newKey); if (!IsImageRegistered(newKey)) { byte[] data = File.ReadAllBytes(value); RegisterImageFromData(name, newKey, data); File.Delete(value); XuaLogger.AutoTranslator.Warn("Replaced old file with name '" + name + "' registered with key old '" + key + "'."); } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while trying to rename file with key '" + key + "'."); } } internal void RegisterImageFromData(string textureName, string key, byte[] data) { string text = StringExtensions.SanitizeForFileSystem(textureName); string text2 = HashHelper.Compute(data); string text3 = ((!(key == text2)) ? (text + " [" + key + "-" + text2 + "].png") : (text + " [" + key + "].png")); string text4 = Path.Combine(TranslatePlugin.TexturesPath, text3); File.WriteAllBytes(text4, data); XuaLogger.AutoTranslator.Info("Dumped texture file: " + text3); _keyToFileName[key] = text4; RegisterTranslatedImage(text4, key, data); } private void RegisterTranslatedImage(string fileName, string key, byte[] data) { _translatedImages[key] = new TranslatedImage(fileName, data, null); } private void RegisterUntranslatedImage(string key) { _untranslatedImages.Add(key); } internal bool IsImageRegistered(string key) { return _translatedImages.ContainsKey(key) || _untranslatedImages.Contains(key); } internal bool TryGetTranslatedImage(string key, out byte[] data, out TranslatedImage image) { if (_translatedImages.TryGetValue(key, out var value)) { try { data = value.GetData(); image = value; return data != null; } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurrd while attempting to load image: " + value.FileName); _translatedImages.Remove(key); } } data = null; image = null; return false; } private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { } _disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal class TextureTranslationInfo { private static Dictionary<string, string> NameToHash = new Dictionary<string, string>(); private static readonly Encoding UTF8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private string _key; private byte[] _originalData; private bool _initialized; private TextureFormat _textureFormat; public static HashSet<string> DuplicateTextureNames = new HashSet<string>(); public WeakReference<Texture2D> Original { get; private set; } public Texture2D Translated { get; private set; } public Sprite TranslatedSprite { get; set; } public bool IsTranslated { get; set; } public bool IsDumped { get; set; } public bool UsingReplacedTexture { get; set; } public void Initialize(Texture2D texture) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _initialized = true; _textureFormat = texture.format; SetOriginal(texture); } } public void SetOriginal(Texture2D texture) { Original = WeakReference<Texture2D>.Create(texture); } public void SetTranslated(Texture2D texture) { Translated = texture; } public void CreateTranslatedTexture(byte[] newData, TranslateExtensions.ImageFormat format) { if ((Object)(object)Translated == (Object)null) { Texture2D target = Original.Target; Texture2D val = CreateEmptyTexture2D(target); val.LoadImageEx(target); SetTranslated(val); ExtensionDataHelper.SetExtensionData<TextureTranslationInfo>((object)val, this); UsingReplacedTexture = true; } } public void CreateOriginalTexture() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!((WeakReference)(object)Original).IsAlive && _originalData != null) { Texture2D val = CreateEmptyTexture2D(_textureFormat); val.LoadImageEx(null); SetOriginal(val); } } public string GetKey() { SetupHashAndData(Original.Target); return _key; } public byte[] GetOriginalData() { SetupHashAndData(Original.Target); return _originalData; } public byte[] GetOrCreateOriginalData() { SetupHashAndData(Original.Target); if (_originalData != null) { return _originalData; } return Original.Target.GetTextureData().Data; } public static void AddDuplicateName(string name) { DuplicateTextureNames.Add(name); } private TextureDataResult SetupKeyForNameWithFallback(string name, Texture2D texture) { bool flag = false; string value = null; string text = null; TextureDataResult textureDataResult = null; textureDataResult = texture.GetTextureData(); text = TextureTranslationCache.HashHelper.Compute(textureDataResult.Data); if (NameToHash.TryGetValue(name, out value)) { if (value != text) { XuaLogger.AutoTranslator.Warn("Detected duplicate image name: " + name); flag = true; AddDuplicateName(name); } } else { NameToHash[name] = text; } _key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); if (flag) { string key = TextureTranslationCache.HashHelper.Compute(UTF8.GetBytes(name)); TranslateConfig.cache.RenameFileWithKey(name, key, value); } return textureDataResult; } private void SetupHashAndData(Texture2D texture) { if (_key != null) { return; } string textureName = texture.GetTextureName(null); if (textureName != null) { TextureDataResult textureDataResult = SetupKeyForNameWithFallback(textureName, texture); if (textureDataResult == null) { textureDataResult = texture.GetTextureData(); } _originalData = textureDataResult.Data; } } public static Texture2D CreateEmptyTexture2D(TextureFormat format) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown return new Texture2D(2, 2, format, false); } public static Texture2D CreateEmptyTexture2D(Texture2D texture) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown return new Texture2D(((Texture)texture).width, ((Texture)texture).height, texture.format, false); } } public class TranslatedImage { public interface ITranslatedImageSource { byte[] GetData(); } public Texture2D texture; private static readonly Dictionary<string, TranslateExtensions.ImageFormat> Formats = new Dictionary<string, TranslateExtensions.ImageFormat>(StringComparer.OrdinalIgnoreCase) { { ".png", TranslateExtensions.ImageFormat.PNG }, { ".tga", TranslateExtensions.ImageFormat.TGA } }; private readonly ITranslatedImageSource _source; private WeakReference<byte[]> _weakData;