Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalCompany JP v1.1.8
BepInEx/core/XUnity.Common.dll
Decompiled 2 years 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 2 years 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 2 years 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;