

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using XUnity.Common.Constants;
using XUnity.Common.Extensions;
using XUnity.Common.Logging;
using XUnity.Common.Utilities;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("XUnity.AutoTranslator.Plugin.Core")]
[assembly: AssemblyCompany("gravydevsupreme")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2018 / MIT License")]
[assembly: AssemblyDescription("Common dependencies shared between XUnity Auto Translator and Resource Redirector.")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+ca3085aaf78da7291484707ca7a6b715a67885c7")]
[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
{
internal static class GeneratedInfo
{
public const string PROJECT_VERSION = "1.0.3";
}
}
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")).GetValueOrDefault()) ? 1 : 0);
}
else
{
num = 1;
}
return num != 0;
}).Invoke())
{
return PatchWithExternHooks(type, methodBase, intPtr, forced: false);
}
}
XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework.");
}
}
catch (Exception e2)
{
if ((object)methodBase != null)
{
XuaLogger.Common.Warn(e2, "An error occurred while patching property/method '" + methodBase.DeclaringType.FullName + "." + methodBase.Name + "'. Failing hook: '" + type.Name + "'.");
}
else
{
XuaLogger.Common.Warn(e2, "An error occurred while patching property/method. Failing hook: '" + type.Name + "'.");
}
}
return false;
}
private static bool PatchWithExternHooks(Type type, MethodBase original, IntPtr originalPtr, bool forced)
{
BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
if ((object)ClrTypes.Imports != null)
{
if (originalPtr == IntPtr.Zero)
{
XuaLogger.Common.Warn("Could not hook '" + type.Name + "'. Likely due differences between different versions of the engine or text framework.");
return false;
}
IntPtr? intPtr = type.GetMethod("ML_Detour", bindingAttr)?.MethodHandle.GetFunctionPointer();
if (intPtr.HasValue && intPtr.Value != IntPtr.Zero)
{
ClrTypes.Imports.GetMethod("Hook", bindingAttr).Invoke(null, new object[2] { originalPtr, intPtr.Value });
XuaLogger.Common.Debug("Hooked " + type.Name + " through MelonMod Imports.Hook method.");
return true;
}
XuaLogger.Common.Warn("Could not hook '" + type.Name + "' because no detour method was found.");
}
else
{
if ((object)original == null)
{
XuaLogger.Common.Warn("Cannot hook '" + type.Name + "'. Could not locate the original method. Failing hook: '" + type.Name + "'.");
return false;
}
if ((object)ClrTypes.Hook == null || (object)ClrTypes.NativeDetour == null)
{
XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. MonoMod hooks is not supported in this runtime as MonoMod is not loaded. Failing hook: '" + type.Name + "'.");
return false;
}
object obj = type.GetMethod("Get_MM_Detour", bindingAttr)?.Invoke(null, null) ?? type.GetMethod("MM_Detour", bindingAttr);
if (obj != null)
{
string text = "(managed)";
object obj2;
try
{
obj2 = ClrTypes.Hook.GetConstructor(new Type[2]
{
typeof(MethodBase),
typeof(MethodInfo)
}).Invoke(new object[2] { original, obj });
obj2.GetType().GetMethod("Apply").Invoke(obj2, null);
}
catch (Exception e) when (((Func<bool>)delegate
{
// Could not convert BlockContainer to single expression
System.Runtime.CompilerServices.Unsafe.SkipInit(out int num);
if (e.FirstInnerExceptionOfType<NullReferenceException>() == null)
{
NotSupportedException ex = e.FirstInnerExceptionOfType<NotSupportedException>();
num = ((ex != null && (ex.Message?.Contains("Body-less")).GetValueOrDefault()) ? 1 : 0);
}
else
{
num = 1;
}
return num != 0;
}).Invoke())
{
text = "(native)";
obj2 = ClrTypes.NativeDetour.GetConstructor(new Type[2]
{
typeof(MethodBase),
typeof(MethodBase)
}).Invoke(new object[2] { original, obj });
obj2.GetType().GetMethod("Apply").Invoke(obj2, null);
}
type.GetMethod("MM_Init", bindingAttr)?.Invoke(null, new object[1] { obj2 });
if (forced)
{
XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through forced MonoMod hooks. " + text);
}
else
{
XuaLogger.Common.Debug("Hooked " + original.DeclaringType.FullName + "." + original.Name + " through MonoMod hooks. " + text);
}
return true;
}
if (forced)
{
XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'.");
}
else
{
XuaLogger.Common.Warn("Cannot hook '" + original.DeclaringType.FullName + "." + original.Name + "'. Harmony is not supported in this runtime and no alternate MonoMod hook has been implemented. Failing hook: '" + type.Name + "'.");
}
}
return false;
}
private static object CreateHarmonyMethod(MethodInfo method, int? priority)
{
object obj = ClrTypes.HarmonyMethod.GetConstructor(new Type[1] { typeof(MethodInfo) }).Invoke(new object[1] { method });
if (priority.HasValue)
{
(ClrTypes.HarmonyMethod.GetField("priority", BindingFlags.Instance | BindingFlags.Public) ?? ClrTypes.HarmonyMethod.GetField("prioritiy", BindingFlags.Instance | BindingFlags.Public)).SetValue(obj, priority.Value);
}
return obj;
}
}
public class HookingHelperPriorityAttribute : Attribute
{
public int priority;
public HookingHelperPriorityAttribute(int priority)
{
this.priority = priority;
}
}
public static class HookPriority
{
public const int Last = 0;
public const int VeryLow = 100;
public const int Low = 200;
public const int LowerThanNormal = 300;
public const int Normal = 400;
public const int HigherThanNormal = 500;
public const int High = 600;
public const int VeryHigh = 700;
public const int First = 800;
}
public static class ListExtensions
{
public static void BinarySearchInsert<T>(this List<T> items, T item) where T : IComparable<T>
{
int num = items.BinarySearch(item);
if (num < 0)
{
items.Insert(~num, item);
}
else
{
items.Insert(num, item);
}
}
}
public static class MaintenanceHelper
{
private class ActionRegistration
{
public Action Action { get; }
public int Filter { get; }
public ActionRegistration(Action action, int filter)
{
Action = action;
Filter = filter;
}
}
private static readonly object Sync = new object();
private static readonly List<ActionRegistration> RegisteredActions = new List<ActionRegistration>();
private static bool _initialized;
public static void AddMaintenanceFunction(Action action, int filter)
{
lock (Sync)
{
if (!_initialized)
{
_initialized = true;
StartMaintenance();
}
ActionRegistration item = new ActionRegistration(action, filter);
RegisteredActions.Add(item);
}
}
private static void StartMaintenance()
{
Thread thread = new Thread(MaintenanceLoop);
thread.IsBackground = true;
thread.Start();
}
private static void MaintenanceLoop(object state)
{
int num = 0;
while (true)
{
lock (Sync)
{
foreach (ActionRegistration registeredAction in RegisteredActions)
{
if (num % registeredAction.Filter == 0)
{
try
{
registeredAction.Action();
}
catch (Exception e)
{
XuaLogger.Common.Error(e, "An unexpected error occurred during maintenance.");
}
}
}
}
num++;
Thread.Sleep(5000);
}
}
}
public static class Paths
{
private static string _gameRoot;
public static string GameRoot
{
get
{
return _gameRoot ?? GetAndSetGameRoot();
}
set
{
_gameRoot = value;
}
}
public static void Initialize()
{
GetAndSetGameRoot();
}
private static string GetAndSetGameRoot()
{
return _gameRoot = new DirectoryInfo(Application.dataPath).Parent.FullName;
}
}
public static class ReflectionCache
{
private struct MemberLookupKey
{
public Type Type { get; set; }
public string MemberName { get; set; }
public MemberLookupKey(Type type, string memberName)
{
Type = type;
MemberName = memberName;
}
public override bool Equals(object obj)
{
if (obj is MemberLookupKey memberLookupKey)
{
if ((object)Type == memberLookupKey.Type)
{
return MemberName == memberLookupKey.MemberName;
}
return false;
}
return false;
}
public override int GetHashCode()
{
return Type.GetHashCode() + MemberName.GetHashCode();
}
}
private static Dictionary<MemberLookupKey, CachedMethod> Methods = new Dictionary<MemberLookupKey, CachedMethod>();
private static Dictionary<MemberLookupKey, CachedProperty> Properties = new Dictionary<MemberLookupKey, CachedProperty>();
private static Dictionary<MemberLookupKey, CachedField> Fields = new Dictionary<MemberLookupKey, CachedField>();
public static CachedMethod CachedMethod(this Type type, string name)
{
return type.CachedMethod(name, (Type[])null);
}
public static CachedMethod CachedMethod(this Type type, string name, params Type[] types)
{
MemberLookupKey key = new MemberLookupKey(type, name);
if (!Methods.TryGetValue(key, out var value))
{
Type type2 = type;
MethodInfo methodInfo = null;
while ((object)methodInfo == null && (object)type2 != null)
{
methodInfo = ((types != null && types.Length != 0) ? type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) : type2.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
type2 = type2.BaseType;
}
if ((object)methodInfo != null)
{
value = new CachedMethod(methodInfo);
}
Methods[key] = value;
}
return value;
}
public static CachedProperty CachedProperty(this Type type, string name)
{
MemberLookupKey key = new MemberLookupKey(type, name);
if (!Properties.TryGetValue(key, out var value))
{
Type type2 = type;
PropertyInfo propertyInfo = null;
while ((object)propertyInfo == null && (object)type2 != null)
{
propertyInfo = type2.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
type2 = type2.BaseType;
}
if ((object)propertyInfo != null)
{
value = new CachedProperty(propertyInfo);
}
Properties[key] = value;
}
return value;
}
public static CachedField CachedField(this Type type, string name)
{
MemberLookupKey key = new MemberLookupKey(type, name);
if (!Fields.TryGetValue(key, out var value))
{
Type type2 = type;
FieldInfo fieldInfo = null;
while ((object)fieldInfo == null && (object)type2 != null)
{
fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
type2 = type2.BaseType;
}
if ((object)fieldInfo != null)
{
value = new CachedField(fieldInfo);
}
Fields[key] = value;
}
return value;
}
public static CachedField CachedFieldByIndex(this Type type, int index, Type fieldType, BindingFlags flags)
{
FieldInfo[] array = (from x in type.GetFields(flags)
where (object)x.FieldType == fieldType
select x).ToArray();
if (index < array.Length)
{
return new CachedField(array[index]);
}
return null;
}
}
public class CachedMethod
{
private static readonly object[] Args0 = new object[0];
private static readonly object[] Args1 = new object[1];
private static readonly object[] Args2 = new object[2];
private FastReflectionDelegate _invoke;
internal CachedMethod(MethodInfo method)
{
_invoke = method.CreateFastDelegate();
}
public object Invoke(object instance, object[] arguments)
{
return _invoke(instance, arguments);
}
public object Invoke(object instance)
{
return _invoke(instance, Args0);
}
public object Invoke(object instance, object arg1)
{
try
{
Args1[0] = arg1;
return _invoke(instance, Args1);
}
finally
{
Args1[0] = null;
}
}
public object Invoke(object instance, object arg1, object arg2)
{
try
{
Args2[0] = arg1;
Args2[1] = arg2;
return _invoke(instance, Args2);
}
finally
{
Args2[0] = null;
Args2[1] = null;
}
}
}
public class CachedProperty
{
private static readonly object[] Args0 = new object[0];
private static readonly object[] Args1 = new object[1];
private FastReflectionDelegate _set;
private FastReflectionDelegate _get;
public Type PropertyType { get; }
internal CachedProperty(PropertyInfo propertyInfo)
{
if (propertyInfo.CanRead)
{
_get = propertyInfo.GetGetMethod(nonPublic: true).CreateFastDelegate();
}
if (propertyInfo.CanWrite)
{
_set = propertyInfo.GetSetMethod(nonPublic: true).CreateFastDelegate();
}
PropertyType = propertyInfo.PropertyType;
}
public void Set(object instance, object[] arguments)
{
if (_set != null)
{
_set(instance, arguments);
}
}
public void Set(object instance, object arg1)
{
if (_set == null)
{
return;
}
try
{
Args1[0] = arg1;
_set(instance, Args1);
}
finally
{
Args1[0] = null;
}
}
public object Get(object instance, object[] arguments)
{
if (_get == null)
{
return null;
}
return _get(instance, arguments);
}
public object Get(object instance)
{
if (_get == null)
{
return null;
}
return _get(instance, Args0);
}
}
public class CachedField
{
private Func<object, object> _get;
private Action<object, object> _set;
public Type FieldType { get; }
internal CachedField(FieldInfo fieldInfo)
{
_get = CustomFastReflectionHelper.CreateFastFieldGetter<object, object>(fieldInfo);
_set = CustomFastReflectionHelper.CreateFastFieldSetter<object, object>(fieldInfo);
FieldType = fieldInfo.FieldType;
}
public void Set(object instance, object value)
{
if (_set != null)
{
_set(instance, value);
}
}
public object Get(object instance)
{
if (_get == null)
{
return null;
}
return _get(instance);
}
}
internal static class ReflectionEmitFastReflectionHelper
{
private static readonly Type[] DynamicMethodDelegateArgs = new Type[2]
{
typeof(object),
typeof(object[])
};
public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall)
{
DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + method.DeclaringType.FullName + "." + method.Name + ">", typeof(object), DynamicMethodDelegateArgs, method.DeclaringType.Module, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
ParameterInfo[] parameters = method.GetParameters();
bool flag = true;
if (!method.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
if (method.DeclaringType.IsValueType)
{
iLGenerator.Emit(OpCodes.Unbox_Any, method.DeclaringType);
}
}
for (int i = 0; i < parameters.Length; i++)
{
Type type = parameters[i].ParameterType;
bool isByRef = type.IsByRef;
if (isByRef)
{
type = type.GetElementType();
}
bool isValueType = type.IsValueType;
if (isByRef && isValueType && !directBoxValueAccess)
{
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Ldc_I4, i);
}
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Ldc_I4, i);
if (isByRef && !isValueType)
{
iLGenerator.Emit(OpCodes.Ldelema, typeof(object));
continue;
}
iLGenerator.Emit(OpCodes.Ldelem_Ref);
if (!isValueType)
{
continue;
}
if (!isByRef || !directBoxValueAccess)
{
iLGenerator.Emit(OpCodes.Unbox_Any, type);
if (isByRef)
{
iLGenerator.Emit(OpCodes.Box, type);
iLGenerator.Emit(OpCodes.Dup);
iLGenerator.Emit(OpCodes.Unbox, type);
if (flag)
{
flag = false;
throw new NotImplementedException("No idea how to implement this...");
}
iLGenerator.Emit(OpCodes.Stloc_0);
iLGenerator.Emit(OpCodes.Stelem_Ref);
iLGenerator.Emit(OpCodes.Ldloc_0);
}
}
else
{
iLGenerator.Emit(OpCodes.Unbox, type);
}
}
if (method.IsConstructor)
{
iLGenerator.Emit(OpCodes.Newobj, method as ConstructorInfo);
}
else if (method.IsFinal || !method.IsVirtual || forceNonVirtcall)
{
iLGenerator.Emit(OpCodes.Call, method as MethodInfo);
}
else
{
iLGenerator.Emit(OpCodes.Callvirt, method as MethodInfo);
}
Type type2 = (method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType);
if ((object)type2 != typeof(void))
{
if (type2.IsValueType)
{
iLGenerator.Emit(OpCodes.Box, type2);
}
}
else
{
iLGenerator.Emit(OpCodes.Ldnull);
}
iLGenerator.Emit(OpCodes.Ret);
return (FastReflectionDelegate)dynamicMethod.CreateDelegate(typeof(FastReflectionDelegate));
}
public static Func<T, F> CreateFastFieldGetter<T, F>(FieldInfo fieldInfo)
{
if ((object)fieldInfo == null)
{
throw new ArgumentNullException("fieldInfo");
}
if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
{
throw new ArgumentException("FieldInfo type does not match return type.");
}
if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
{
throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
}
DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Get_" + fieldInfo.Name + ">", typeof(F), new Type[1] { typeof(T) }, fieldInfo.DeclaringType.Module, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!fieldInfo.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
}
iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo);
if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
{
iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType);
}
iLGenerator.Emit(OpCodes.Ret);
return (Func<T, F>)dynamicMethod.CreateDelegate(typeof(Func<T, F>));
}
public static Action<T, F> CreateFastFieldSetter<T, F>(FieldInfo fieldInfo)
{
if ((object)fieldInfo == null)
{
throw new ArgumentNullException("fieldInfo");
}
if (!typeof(F).IsAssignableFrom(fieldInfo.FieldType))
{
throw new ArgumentException("FieldInfo type does not match argument type.");
}
if ((object)typeof(T) != typeof(object) && ((object)fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))))
{
throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
}
DynamicMethod dynamicMethod = new DynamicMethod("FastReflection<" + typeof(T).FullName + ".Set_" + fieldInfo.Name + ">", null, new Type[2]
{
typeof(T),
typeof(F)
}, fieldInfo.DeclaringType.Module, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!fieldInfo.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
}
iLGenerator.Emit(OpCodes.Ldarg_1);
if ((object)fieldInfo.FieldType != typeof(F))
{
if (fieldInfo.FieldType.IsValueType != typeof(F).IsValueType)
{
if (fieldInfo.FieldType.IsValueType)
{
iLGenerator.Emit(OpCodes.Unbox, fieldInfo.FieldType);
}
else
{
iLGenerator.Emit(OpCodes.Box, fieldInfo.FieldType);
}
}
else
{
iLGenerator.Emit(OpCodes.Castclass, fieldInfo.FieldType);
}
}
iLGenerator.Emit(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
iLGenerator.Emit(OpCodes.Ret);
return (Action<T, F>)dynamicMethod.CreateDelegate(typeof(Action<T, F>));
}
}
public static class TimeHelper
{
public static float realtimeSinceStartup => Time.realtimeSinceStartup;
}
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;
}
}
}using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameTranslator.Patches;
using GameTranslator.Patches.Hooks;
using GameTranslator.Patches.Hooks.texture;
using GameTranslator.Patches.InteractiveTerminalAPI;
using GameTranslator.Patches.Translatons;
using GameTranslator.Patches.Translatons.Manipulator;
using GameTranslator.Patches.Utils;
using GameTranslator.Patches.Utils.Textures;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.UIElements;
using XUnity.Common.Constants;
using XUnity.Common.Extensions;
using XUnity.Common.Harmony;
using XUnity.Common.Logging;
using XUnity.Common.MonoMod;
using XUnity.Common.Utilities;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("GameTranslator")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+db62a282db8ba3002b5234a206e61537a23d464a")]
[assembly: AssemblyProduct("GameTranslator")]
[assembly: AssemblyTitle("GameTranslator")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace GameTranslator
{
public class TranslateConfig
{
public class TranslateConfigFile
{
public string ConfigFilePath;
public string ConfigFileName;
public bool shouldTranslate;
public int shouldTranslateMinLength = 200;
public int shouldTranslateMaxLength;
public bool shouldLoad = true;
public IDictionary<string, string> normal = new Dictionary<string, string>();
public IDictionary<string, string> special = new Dictionary<string, string>();
public static HashSet<TranslateConfigFile> configs = new HashSet<TranslateConfigFile>();
public Dictionary<string, string> translatePairs = new Dictionary<string, string>();
private List<string> logs = new List<string>();
public List<RegexTranslation> regexTranslations = new List<RegexTranslation>();
public List<RegexTranslationSplitter> splitterRegexTranslations = new List<RegexTranslationSplitter>();
private static Regex _regex;
internal readonly ConcurrentDictionary<string, DateTime> _translatePairLastAccess = new ConcurrentDictionary<string, DateTime>();
public static Regex regex
{
get
{
if (_regex == null)
{
Type typeFromHandle = typeof(TranslateConfigFile);
lock (typeFromHandle)
{
if (_regex == null)
{
_regex = new Regex("(?<!\\\\)=", NormalTextTranslator.RegexCompiledSupportedFlag);
}
}
}
return _regex;
}
}
public TranslateConfigFile(string configName, bool saveOnInit, bool shouldLoad)
{
ConfigFileName = configName;
ConfigFilePath = TranslatePlugin.DefaultPath + configName + ".cfg";
this.shouldLoad = shouldLoad;
if (ConfigFilePath == null)
{
throw new ArgumentNullException("configPath");
}
ConfigFilePath = Path.GetFullPath(ConfigFilePath);
if (this.shouldLoad && File.Exists(ConfigFilePath))
{
Reload();
}
else if (saveOnInit)
{
Save();
}
configs.Add(this);
}
public void Reload()
{
translatePairs.Clear();
_translatePairLastAccess.Clear();
normal.Clear();
regexTranslations.Clear();
splitterRegexTranslations.Clear();
List<string> list = new List<string>();
string[] array = File.ReadAllLines(ConfigFilePath);
foreach (string text in array)
{
if (text.StartsWith("#") || !text.Contains("="))
{
continue;
}
string[] array2 = regex.Split(text);
if (array2.Length != 2)
{
continue;
}
string text2 = array2[0].Replace("\\=", "=");
string text3 = array2[1].Replace("\\=", "=");
if (text2.StartsWith("r:"))
{
try
{
RegexTranslation item = new RegexTranslation(text2, text3);
regexTranslations.Add(item);
}
catch (Exception ex)
{
string text4 = text2 + "=" + text3;
list.Add("Invalid regex: " + text4 + " - " + ex.Message);
TranslatePlugin.logger.LogWarning((object)("Failed to parse regex: " + text4 + ". Error: " + ex.Message));
}
continue;
}
if (text2.StartsWith("sr:"))
{
try
{
RegexTranslationSplitter item2 = new RegexTranslationSplitter(text2, text3);
splitterRegexTranslations.Add(item2);
}
catch (Exception ex2)
{
string text5 = text2 + "=" + text3;
list.Add("Invalid splitter regex: " + text5 + " - " + ex2.Message);
TranslatePlugin.logger.LogWarning((object)("Failed to parse splitter regex: " + text5 + ". Error: " + ex2.Message));
}
continue;
}
if (normal.ContainsKey(text2))
{
normal[text2] = text3;
special[text3] = text2;
}
else
{
normal.Add(text2, text3);
if (!special.ContainsKey(text3))
{
special.Add(text3, text2);
}
}
if (text2.Length < shouldTranslateMinLength)
{
shouldTranslateMinLength = text2.Length;
}
if (text2.Length > shouldTranslateMaxLength)
{
shouldTranslateMaxLength = text2.Length;
}
}
if (list.Count > 0)
{
File.AppendAllLines(Path.Combine(Path.GetDirectoryName(ConfigFilePath), ConfigFileName + "_errors.log"), list);
}
}
public void Log(string text)
{
logs.Add(text);
string directoryName = Path.GetDirectoryName(ConfigFilePath);
if (directoryName == null)
{
Directory.CreateDirectory(directoryName);
}
new List<string>().Add("##" + ConfigFileName);
File.WriteAllLines(ConfigFilePath, logs);
}
public void Save()
{
string directoryName = Path.GetDirectoryName(ConfigFilePath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
if (!File.Exists(ConfigFilePath))
{
File.Create(ConfigFilePath).Close();
}
else
{
if (!shouldLoad)
{
return;
}
List<string> list = new List<string>();
list.Add("##" + ConfigFileName);
foreach (KeyValuePair<string, string> item in normal)
{
string text = item.Key.Replace("=", "\\=");
string text2 = item.Value.Replace("=", "\\=");
list.Add(text + "=" + text2);
}
list.Add("##RegularExpression");
foreach (RegexTranslation regexTranslation in regexTranslations)
{
list.Add("r:" + regexTranslation.Key + "=" + regexTranslation.Value);
}
list.Add("##SplitterRegularExpression");
foreach (RegexTranslationSplitter splitterRegexTranslation in splitterRegexTranslations)
{
list.Add("sr:" + splitterRegexTranslation.Key + "=" + splitterRegexTranslation.Value);
}
File.WriteAllLines(ConfigFilePath, list.ToArray());
}
}
}
private static SafeFileWatcher _fileWatcher;
private static ConcurrentDictionary<string, DateTime> _fileLastModifiedTimes = new ConcurrentDictionary<string, DateTime>();
private static Timer _pollingTimer;
private static readonly object _updateLock = new object();
public static TranslateConfigFile normal;
public static TranslateConfigFile hud;
public static TranslateConfigFile items;
public static TranslateConfigFile terminal;
public static TranslateConfigFile text;
public static TranslateConfigFile cmd_py;
public static TranslateConfigFile cmd_zh;
public static TranslateConfigFile gui;
public static TranslateConfigFile interactiveTerminalAPI;
public static NormalTextTranslator normalText;
public static NormalTextTranslator hudText;
public static NormalTextTranslator guiText;
public static TextureTranslationCache cache;
public static NormalTextTranslator itemsText;
public static NormalTextTranslator terminalText;
public static NormalTextTranslator textText;
public static NormalTextTranslator cmdPyText;
public static NormalTextTranslator cmdZhText;
public static NormalTextTranslator interactiveTerminalAPIText;
private static DateTime _lastCleanupTime = DateTime.Now;
private static readonly TimeSpan CLEANUP_INTERVAL = TimeSpan.FromMinutes(30.0);
public static void Load()
{
hud = CreateNewConfig("HUD-Translate");
hud.shouldTranslate = TranslatePlugin.shouldTranslateHUD.Value;
hudText = new NormalTextTranslator(hud.ConfigFileName + ".cfg");
hudText.Load();
items = CreateNewConfig("Item-Translate");
items.shouldTranslate = TranslatePlugin.shouldTranslateItems.Value;
itemsText = new NormalTextTranslator(items.ConfigFileName + ".cfg");
itemsText.Load();
terminal = CreateNewConfig("Terminal-Translate");
terminal.shouldTranslate = TranslatePlugin.shouldTranslateTerimal.Value;
terminalText = new NormalTextTranslator(terminal.ConfigFileName + ".cfg");
terminalText.Load();
cmd_py = CreateNewConfig("CMD-PY-Translate");
cmdPyText = new NormalTextTranslator(cmd_py.ConfigFileName + ".cfg");
cmdPyText.Load();
cmd_zh = CreateNewConfig("CMD-ZH-Translate");
cmdZhText = new NormalTextTranslator(cmd_zh.ConfigFileName + ".cfg");
cmdZhText.Load();
text = CreateNewConfig("SpecialText-Translate");
text.shouldTranslate = TranslatePlugin.shouldTranslateSpecialText.Value;
textText = new NormalTextTranslator(text.ConfigFileName + ".cfg");
textText.Load();
gui = CreateNewConfig("GuiText-Translate");
gui.shouldTranslate = TranslatePlugin.shouldTranslateGui.Value;
guiText = new NormalTextTranslator(gui.ConfigFileName + ".cfg");
guiText.Load();
interactiveTerminalAPI = CreateNewConfig("InteractiveTerminalAPI-Translate");
interactiveTerminalAPI.shouldTranslate = TranslatePlugin.shouldTranslateInteractiveTerminalAPI.Value;
interactiveTerminalAPIText = new NormalTextTranslator(interactiveTerminalAPI.ConfigFileName + ".cfg");
interactiveTerminalAPIText.Load();
normal = CreateNewConfig("Normal-Translate");
normal.shouldTranslate = TranslatePlugin.shouldTranslateNormalText.Value;
normalText = new NormalTextTranslator(normal.ConfigFileName + ".cfg");
normalText.Load();
cache = new TextureTranslationCache();
cache.LoadTranslationFiles();
string fullPath = Path.GetFullPath(TranslatePlugin.DefaultPath);
ConfigEntry<bool> enableFileWatcher = TranslatePlugin.enableFileWatcher;
if (enableFileWatcher != null && enableFileWatcher.Value)
{
_fileWatcher = new SafeFileWatcher(fullPath);
_fileWatcher.DirectoryUpdated += OnDirectoryUpdated;
TranslatePlugin.logger.LogInfo((object)("Tracking path " + fullPath));
}
foreach (TranslateConfigFile config in TranslateConfigFile.configs)
{
if (File.Exists(config.ConfigFilePath))
{
_fileLastModifiedTimes[config.ConfigFilePath] = File.GetLastWriteTime(config.ConfigFilePath);
}
}
AsyncTranslationManager.Instance.ClearCache();
ConfigEntry<bool> enablePollingCheck = TranslatePlugin.enablePollingCheck;
if (enablePollingCheck != null && enablePollingCheck.Value)
{
_pollingTimer = new Timer(delegate
{
OnDirectoryUpdated();
}, null, TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(10.0));
TranslatePlugin.logger.LogInfo((object)("Polling check tracking path " + fullPath));
}
}
public static void Unload()
{
_fileWatcher?.Dispose();
_fileWatcher = null;
cache?.Dispose();
cache = null;
_pollingTimer?.Dispose();
_pollingTimer = null;
}
private static void OnDirectoryUpdated()
{
lock (_updateLock)
{
try
{
bool flag = false;
foreach (TranslateConfigFile config in TranslateConfigFile.configs)
{
if (!config.shouldLoad || !File.Exists(config.ConfigFilePath))
{
continue;
}
DateTime lastWriteTime = File.GetLastWriteTime(config.ConfigFilePath);
if (_fileLastModifiedTimes.TryGetValue(config.ConfigFilePath, out var value))
{
if (!(lastWriteTime > value))
{
continue;
}
_fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime;
for (int i = 0; i < 3; i++)
{
try
{
config.Reload();
GetModuleTranslator(config)?.Load();
TextTranslate.ChangeTime++;
flag = true;
}
catch (IOException) when (i < 2)
{
Thread.Sleep(100 * (i + 1));
continue;
}
catch (Exception ex2)
{
TranslatePlugin.logger.LogError((object)("Unexpected error reloading config " + config.ConfigFileName + ": " + ex2.Message));
}
break;
}
}
else
{
_fileLastModifiedTimes[config.ConfigFilePath] = lastWriteTime;
}
}
if (flag)
{
TranslatePlugin.logger.LogInfo((object)"Translate files reloaded due to file changes.");
}
}
catch (Exception ex3)
{
TranslatePlugin.logger.LogError((object)("Error in OnDirectoryUpdated: " + ex3.Message));
}
}
}
public static void show(TranslateConfigFile file)
{
foreach (string key in file.normal.Keys)
{
TranslatePlugin.LogInfo(key + "=" + file.normal[key]);
}
}
private static TranslateConfigFile CreateNewConfig(string fileName, bool should)
{
TranslatePlugin.logger.LogInfo((object)("GameTranslator is loading config file call " + fileName));
return new TranslateConfigFile(fileName, saveOnInit: true, should);
}
public static bool IsStringContainsEnglish(string input)
{
if (!string.IsNullOrEmpty(input))
{
return new Regex("[a-zA-Z]").IsMatch(input);
}
return false;
}
public static bool IsStringContainsChinese(string input)
{
if (!string.IsNullOrEmpty(input))
{
return new Regex("[\\u4e00-\\u9fa5]").IsMatch(input);
}
return false;
}
public static string useRegularExpression(string raw, string pattern, string result)
{
return Regex.Replace(raw, pattern, result);
}
public static string replaceByMap(string text, TranslateConfigFile file)
{
if (file.normal.Count == 0 && file.regexTranslations.Count == 0 && file.splitterRegexTranslations.Count == 0)
{
return text;
}
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
if (DateTime.Now - _lastCleanupTime > CLEANUP_INTERVAL)
{
CleanupTranslatePairs();
_lastCleanupTime = DateTime.Now;
}
if (!file.shouldTranslate)
{
return text;
}
if (file.translatePairs.ContainsKey(text))
{
file._translatePairLastAccess.TryAdd(text, DateTime.Now);
file._translatePairLastAccess[text] = DateTime.Now;
return file.translatePairs[text];
}
StringBuffer stringBuffer = new StringBuffer(text);
NormalTextTranslator moduleTranslator = GetModuleTranslator(file);
if (moduleTranslator != null)
{
foreach (RegexTranslationSplitter splitterRegex in moduleTranslator._splitterRegexes)
{
string str = moduleTranslator.SplitterTranslate(stringBuffer.ToString(), splitterRegex, ignoreCase: false);
stringBuffer.Clear().Append(str);
}
foreach (RegexTranslation defaultRegex in moduleTranslator._defaultRegexes)
{
if (defaultRegex.CompiledRegex != null && defaultRegex.CompiledRegex.IsMatch(stringBuffer.ToString()))
{
string str2 = defaultRegex.CompiledRegex.Replace(stringBuffer.ToString(), defaultRegex.Translation);
stringBuffer.Clear().Append(str2);
}
}
}
foreach (KeyValuePair<string, string> item in file.normal.OrderByDescending((KeyValuePair<string, string> kv) => kv.Key.Length))
{
stringBuffer.ReplaceFull(item.Key, item.Value);
}
string text2 = stringBuffer.ToString();
file.translatePairs[text] = text2;
file._translatePairLastAccess.TryAdd(text, DateTime.Now);
return text2;
}
finally
{
stopwatch.Stop();
if (stopwatch.ElapsedMilliseconds > 500)
{
string arg = ((text.Length > 50) ? (text.Substring(0, 50) + "...") : text);
TranslatePlugin.logger.LogWarning((object)$"replaceByMap took {stopwatch.ElapsedMilliseconds}ms for text: {arg}");
}
}
}
private static NormalTextTranslator GetModuleTranslator(TranslateConfigFile file)
{
if (file == normal)
{
return normalText;
}
if (file == hud)
{
return hudText;
}
if (file == items)
{
return itemsText;
}
if (file == terminal)
{
return terminalText;
}
if (file == text)
{
return textText;
}
if (file == cmd_py)
{
return cmdPyText;
}
if (file == cmd_zh)
{
return cmdZhText;
}
if (file == gui)
{
return guiText;
}
if (file == interactiveTerminalAPI)
{
return interactiveTerminalAPIText;
}
return null;
}
private static void CleanupTranslatePairs()
{
bool flag = GC.GetTotalMemory(forceFullCollection: false) > 104857600;
foreach (TranslateConfigFile config in TranslateConfigFile.configs)
{
int num = 0;
if (flag)
{
num = (int)((float)config.translatePairs.Count * 0.2f);
num = Math.Max(1, Math.Min(num, config.translatePairs.Count));
}
else if (config.translatePairs.Count > 6000)
{
num = config.translatePairs.Count - 6000;
}
if (num <= 0)
{
continue;
}
List<string> list = config._translatePairLastAccess.OrderBy((KeyValuePair<string, DateTime> kv) => kv.Value).Take(num).Select(delegate(KeyValuePair<string, DateTime> kv)
{
KeyValuePair<string, DateTime> keyValuePair = kv;
return keyValuePair.Key;
})
.ToList();
foreach (string item in list)
{
config.translatePairs.Remove(item);
config._translatePairLastAccess.TryRemove(item, out var _);
}
TranslatePlugin.logger.LogInfo((object)$"Cleaned {list.Count} translate pairs from {config.ConfigFileName}. Remaining: {config.translatePairs.Count}");
}
}
private static TranslateConfigFile CreateNewConfig(string fileName)
{
return CreateNewConfig(fileName, should: true);
}
}
[BepInPlugin("GameTranslator", "GameTranslator", "2.1.1")]
public class TranslatePlugin : BaseUnityPlugin
{
private class TranslationUpdater : MonoBehaviour
{
private void Update()
{
try
{
AsyncTranslationManager.Instance.ProcessMainThreadActions();
}
catch (Exception ex)
{
ManualLogSource logger = TranslatePlugin.logger;
if (logger != null)
{
logger.LogError((object)("Error in TranslationUpdater Update: " + ex.Message));
}
}
}
}
private readonly Harmony harmony = new Harmony("GameTranslator");
private const string PLUGIN_GUID = "GameTranslator";
private const string PLUGIN_NAME = "GameTranslator";
private const string PLUGIN_VERSION = "2.1.1";
public static ManualLogSource logger;
public static ConfigEntry<int> syncTranslationThreshold;
public static ConfigEntry<bool> showAvailableText;
public static ConfigEntry<bool> showOtherDebug;
public static ConfigEntry<bool> enableFileWatcher;
public static ConfigEntry<bool> enablePollingCheck;
public static ConfigEntry<bool> replaceUnsupportedCharacters;
public static ConfigEntry<bool> cacheUnmodifiedTextures;
public static ConfigEntry<bool> enableGrabbableObjectPatch;
public static ConfigEntry<bool> enableHUDManagerPatch;
public static ConfigEntry<bool> enableTerminalPatch;
public static ConfigEntry<bool> changeFont;
public static ConfigEntry<string> fallbackFontTextMeshPro;
public static ConfigEntry<string> shouldRemoveChar;
public static ConfigEntry<string> language;
public static ConfigEntry<bool> shouldTranslateNormalText;
public static ConfigEntry<bool> shouldTranslateSpecialText;
public static ConfigEntry<bool> shouldTranslateTerimal;
public static ConfigEntry<bool> shouldTranslateInteractiveTerminalAPI;
public static ConfigEntry<bool> TerimalCanUseChinese;
public static ConfigEntry<bool> TerimalCanUsePinyinAbbreviation;
public static ConfigEntry<bool> shouldTranslateGui;
public static ConfigEntry<bool> shouldTranslateItems;
public static ConfigEntry<bool> shouldTranslateHUD;
public static ConfigEntry<bool> changeTexture;
public static ConfigEntry<bool> cacheTexturesInMemory;
public static ConfigEntry<bool> disableDuplicateTextureCheck;
public static ConfigEntry<string> ignoredTextureNames;
public static ConfigEntry<bool> generateTerimalCommand;
internal static TranslatePlugin Instance;
public static string DefaultPath;
public static string TexturesPath;
public static bool shouldTranslate;
public static bool CacheTexturesInMemory => cacheTexturesInMemory.Value;
private void Awake()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
GameObject val = new GameObject("GameTranslator Manager");
val.AddComponent<TranslationUpdater>();
((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
ConfigFile();
HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.All, false);
HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.Sprite, false);
HookingHelper.PatchAll((IEnumerable<Type>)ImageHooks.SpriteRenderer, false);
ApplyBasicPatches();
ApplyGrabbableObjectPatch();
ApplyHUDManagerPatch();
ApplyTerminalPatch();
ApplyInteractiveTerminalAPIPatch();
if (replaceUnsupportedCharacters.Value)
{
FontSupportChecker.InitializeFonts();
}
AsyncTranslationManager.Instance.Start();
((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator is loaded");
}
private void OnDestroy()
{
try
{
AsyncTranslationManager.Instance.Stop();
}
catch (Exception ex)
{
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogError((object)("Error in GameTranslatorManager OnDestroy: " + ex.Message));
}
}
TranslateConfig.Unload();
((BaseUnityPlugin)this).Logger.LogInfo((object)"GameTranslator destroyed");
}
private void ConfigFile()
{
syncTranslationThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("ASync", "Sync Translation Threshold", 300, "Define the character threshold to not use async translation");
showAvailableText = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Available Text", false, "Define whether to show available text");
showOtherDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show Other Debug", false, "Define whether to show other debug");
enableFileWatcher = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable File Watcher", false, "If true, enable file system watcher for file updates");
enablePollingCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Polling Check", false, "If true, enable the 10-seconds polling fallback for file updates");
replaceUnsupportedCharacters = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Replace Unsupported Characters", false, "Define whether to replace unsupported characters with Unicode character u25A1");
cacheUnmodifiedTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Cache Unmodified Textures", false, "Define whether to cache textures that have not been modified");
enableGrabbableObjectPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable GrabbableObject Patch", true, "Define whether to patch GrabbableObject");
enableHUDManagerPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable HUDManager Patch", true, "Define whether to patch HUDManager");
enableTerminalPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Terminal Patch", true, "Define whether to patch Terminal");
changeFont = ((BaseUnityPlugin)this).Config.Bind<bool>("Font", "Change Font", false, "Define whether to change the font");
fallbackFontTextMeshPro = ((BaseUnityPlugin)this).Config.Bind<string>("Font", "FallbackFontTextMeshPro", "", "Define the fallback font file used");
shouldRemoveChar = ((BaseUnityPlugin)this).Config.Bind<string>("Font", "Custom Characters", "", "Define what vanilla characters will use custom ones");
language = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "Default", "Define what language folder is used");
shouldTranslateNormalText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Normal Text", true, "Define whether to use Normal Translate method");
shouldTranslateSpecialText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Special Text", false, "Define whether to use SpecialText Translate method");
shouldTranslateTerimal = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Terminal", false, "Define whether translate Terminal");
shouldTranslateInteractiveTerminalAPI = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate InteractiveTerminalAPI", false, "Define whether translate InteractiveTerminalAPI");
TerimalCanUseChinese = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Terminal Can Use Non-English Shortcut Commands", false, "Define whether the terminal can use non-English shortcut commands, such as Chinese");
TerimalCanUsePinyinAbbreviation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Terminal Can Use Custom Shortcut Commands", false, "Define whether the terminal can use custom shortcut commands");
shouldTranslateGui = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Gui", false, "Define whether translate Gui");
shouldTranslateItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate Items", false, "Define whether translate Items");
shouldTranslateHUD = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Translate HUD", false, "Define whether translate HUD");
changeTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Change Texture", false, "Define whether to change the texture");
cacheTexturesInMemory = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Cache Textures In Memory", true, "Define whether to cache texture data in memory for faster loading");
disableDuplicateTextureCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture", "Disable Duplicate Texture Check", true, "Define whether to disable duplicate texture name check");
ignoredTextureNames = ((BaseUnityPlugin)this).Config.Bind<string>("Texture", "Ignored Texture Names", "", "Define what texture names to skip duplicate check");
DefaultPath = ((BaseUnityPlugin)this).Config.ConfigFilePath.Replace("GameTranslator.cfg", "translations\\" + language.Value + "\\");
if (!Directory.Exists(DefaultPath))
{
logger.LogWarning((object)("Translation path does not exist: " + DefaultPath));
try
{
Directory.CreateDirectory(DefaultPath);
logger.LogInfo((object)("Created translation directory: " + DefaultPath));
}
catch (Exception ex)
{
logger.LogError((object)("Failed to create translation directory: " + ex.Message));
DefaultPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), "translations", "default");
Directory.CreateDirectory(DefaultPath);
logger.LogInfo((object)("Using fallback translation directory: " + DefaultPath));
}
}
TexturesPath = DefaultPath + "Texture\\";
if (!Directory.Exists(TexturesPath))
{
Directory.CreateDirectory(TexturesPath);
}
TranslateConfig.Load();
TranslateExtensions.Load();
}
public static List<char> getShouldRemoveChars()
{
return shouldRemoveChar.Value.ToCharArray().ToList();
}
public static void LogInfo(string info)
{
if (logger != null)
{
logger.LogInfo((object)info);
}
}
private void ApplyBasicPatches()
{
try
{
logger.LogInfo((object)"Applying basic patches...");
Type[] array = new Type[63]
{
typeof(GameObjectHook),
typeof(GuiContentHook),
typeof(TeshMeshProHook),
typeof(TeshMeshProUGUIHook),
typeof(TextArea2DHook),
typeof(TextFieldHook),
typeof(TextHook),
typeof(TextMeshHook),
typeof(Texture2DHook),
typeof(TMP_FontAssetHook),
typeof(TMP_TextHook),
typeof(CubismRenderer_MainTexture_Hook),
typeof(CubismRenderer_TryInitialize_Hook),
typeof(Cursor_SetCursor_Hook),
typeof(DicingTextures_GetTexture_Hook),
typeof(Image_material_Hook),
typeof(Image_overrideSprite_Hook),
typeof(Image_sprite_Hook),
typeof(ImageHooks),
typeof(MaskableGraphic_OnEnable_Hook),
typeof(Material_mainTexture_Hook),
typeof(RawImage_texture_Hook),
typeof(Sprite_texture_Hook),
typeof(SpriteRenderer_sprite_Hook),
typeof(UI2DSprite_material_Hook),
typeof(UI2DSprite_sprite2D_Hook),
typeof(UIAtlas_spriteMaterial_Hook),
typeof(UIPanel_clipTexture_Hook),
typeof(UIRect_OnInit_Hook),
typeof(UISprite_atlas_Hook),
typeof(UISprite_material_Hook),
typeof(UISprite_OnInit_Hook),
typeof(UITexture_mainTexture_Hook),
typeof(UITexture_material_Hook),
typeof(AsyncTranslationManager),
typeof(ImageTranslationInfo),
typeof(NormalTextTranslator),
typeof(RegexTranslation),
typeof(RegexTranslationSplitter),
typeof(TextTranslationInfo),
typeof(TextureDataResult),
typeof(TextureTranslationCache),
typeof(TextureTranslationInfo),
typeof(TranslatedImage),
typeof(TranslateExtensions),
typeof(DefaultTextComponentManipulator),
typeof(FairyGUITextComponentManipulator),
typeof(ITextComponentManipulator),
typeof(TextArea2DComponentManipulator),
typeof(UguiNovelTextComponentManipulator),
typeof(FontCache),
typeof(FontHelper),
typeof(FontSupportChecker),
typeof(SceneManagerLoader),
typeof(StringBuffer),
typeof(TextHelper),
typeof(TextTranslate),
typeof(TextureTranslate),
typeof(TranslationScopeHelper),
typeof(ITextureLoader),
typeof(LoadImageImageLoader),
typeof(TextureLoader),
typeof(TgaImageLoader)
};
List<string> list = array.Select((Type t) => t.Name).ToList();
logger.LogDebug((object)string.Format("Found {0} basic patch types: {1}", list.Count, string.Join(", ", list)));
int num = 0;
List<string> list2 = new List<string>();
Type[] array2 = array;
foreach (Type type in array2)
{
try
{
harmony.PatchAll(type);
num++;
list2.Add(type.Name);
logger.LogDebug((object)("Applied basic patch: " + type.Name));
}
catch (Exception ex)
{
logger.LogWarning((object)("Failed to apply basic patch " + type.Name + ": " + ex.Message));
}
}
logger.LogInfo((object)$"Basic patches applied. Successfully applied {num}/{array.Length} patches.");
if (list2.Count > 0)
{
logger.LogDebug((object)("Successfully applied patches: " + string.Join(", ", list2)));
}
if (num < array.Length)
{
List<string> list3 = list.Except(list2).ToList();
logger.LogWarning((object)string.Format("Failed to apply {0} patches: {1}", list3.Count, string.Join(", ", list3)));
}
}
catch (Exception ex2)
{
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogWarning((object)("Error applying basic patches: " + ex2.Message));
}
}
}
private void ApplyGrabbableObjectPatch()
{
try
{
if (enableGrabbableObjectPatch != null && enableGrabbableObjectPatch.Value)
{
harmony.PatchAll(typeof(GrabbableObjectPatcher));
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogInfo((object)"GrabbableObject patch applied successfully");
}
}
else
{
ManualLogSource obj2 = logger;
if (obj2 != null)
{
obj2.LogInfo((object)"GrabbableObject patch disabled by config");
}
}
}
catch (Exception ex)
{
ManualLogSource obj3 = logger;
if (obj3 != null)
{
obj3.LogWarning((object)("Error applying GrabbableObject patch: " + ex.Message));
}
}
}
private void ApplyHUDManagerPatch()
{
try
{
if (enableHUDManagerPatch != null && enableHUDManagerPatch.Value)
{
harmony.PatchAll(typeof(HUDManagerPatcher));
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogInfo((object)"HUDManager patch applied successfully");
}
}
else
{
ManualLogSource obj2 = logger;
if (obj2 != null)
{
obj2.LogInfo((object)"HUDManager patch disabled by config");
}
}
}
catch (Exception ex)
{
ManualLogSource obj3 = logger;
if (obj3 != null)
{
obj3.LogWarning((object)("Error applying HUDManager patch: " + ex.Message));
}
}
}
private void ApplyTerminalPatch()
{
try
{
if (enableTerminalPatch != null && enableTerminalPatch.Value)
{
harmony.PatchAll(typeof(TerminalPatch));
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogInfo((object)"Terminal patch applied successfully");
}
}
else
{
ManualLogSource obj2 = logger;
if (obj2 != null)
{
obj2.LogInfo((object)"Terminal patch disabled by config");
}
}
}
catch (Exception ex)
{
ManualLogSource obj3 = logger;
if (obj3 != null)
{
obj3.LogWarning((object)("Error applying Terminal patch: " + ex.Message));
}
}
}
private void ApplyInteractiveTerminalAPIPatch()
{
try
{
if (shouldTranslateInteractiveTerminalAPI != null && shouldTranslateInteractiveTerminalAPI.Value)
{
harmony.PatchAll(typeof(InteractiveTerminalAPIPatch));
ManualLogSource obj = logger;
if (obj != null)
{
obj.LogInfo((object)"InteractiveTerminalAPI patch applied successfully");
}
InteractiveTerminalAPIPatch.Initialize();
}
else
{
ManualLogSource obj2 = logger;
if (obj2 != null)
{
obj2.LogInfo((object)"InteractiveTerminalAPI patch disabled by config");
}
}
}
catch (Exception ex)
{
ManualLogSource obj3 = logger;
if (obj3 != null)
{
obj3.LogWarning((object)("Error applying InteractiveTerminalAPI patch: " + ex.Message));
}
}
}
}
}
namespace GameTranslator.Patches
{
[HarmonyPatch(typeof(GrabbableObject))]
internal class GrabbableObjectPatcher
{
public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>();
public static HashSet<string> translatedItems = new HashSet<string>();
[HarmonyPostfix]
[HarmonyPatch("Start")]
private static void start(ref GrabbableObject __instance)
{
if (!((Object)(object)__instance.itemProperties != (Object)null) || !TranslatePlugin.shouldTranslateItems.Value || Utility.IsNullOrWhiteSpace(__instance.itemProperties.itemName))
{
return;
}
if (!translatedItems.Contains(__instance.itemProperties.itemName))
{
originToTranslated[__instance.itemProperties.itemName] = TranslateConfig.replaceByMap(__instance.itemProperties.itemName, TranslateConfig.items);
translatedItems.Add(originToTranslated[__instance.itemProperties.itemName]);
__instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName];
}
if (originToTranslated.ContainsKey(__instance.itemProperties.itemName))
{
__instance.itemProperties.itemName = originToTranslated[__instance.itemProperties.itemName];
}
ScanNodeProperties componentInChildren = ((Component)__instance).GetComponentInChildren<ScanNodeProperties>();
if ((Object)(object)componentInChildren != (Object)null && componentInChildren.headerText != null)
{
if (!translatedItems.Contains(componentInChildren.headerText))
{
originToTranslated[componentInChildren.headerText] = TranslateConfig.replaceByMap(componentInChildren.headerText, TranslateConfig.items);
translatedItems.Add(originToTranslated[componentInChildren.headerText]);
componentInChildren.headerText = originToTranslated[componentInChildren.headerText];
}
if (originToTranslated.ContainsKey(componentInChildren.headerText))
{
componentInChildren.headerText = originToTranslated[componentInChildren.headerText];
}
}
}
}
[HarmonyPatch(typeof(HUDManager))]
internal class HUDManagerPatcher
{
public static FieldInfo scanNodes = typeof(HUDManager).GetField("scanNodes", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
public static FieldInfo nodesOnScreen = typeof(HUDManager).GetField("nodesOnScreen", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
public static Dictionary<string, string> originToTranslated = new Dictionary<string, string>();
public static HashSet<string> translatedItems = new HashSet<string>();
private static string lastChat = "";
private static readonly BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
private static MethodInfo CanTipDisplay = typeof(HUDManager).GetMethod("CanTipDisplay", All);
private static MethodInfo StopCoroutine = typeof(HUDManager).GetMethod("StopCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null);
private static MethodInfo StartCoroutine = typeof(HUDManager).GetMethod("StartCoroutine", All, null, new Type[1] { typeof(IEnumerator) }, null);
private static MethodInfo TipsPanelTimer = typeof(HUDManager).GetMethod("TipsPanelTimer", All);
private static FieldInfo tipsPanelCoroutine = typeof(HUDManager).GetField("tipsPanelCoroutine", All);
[HarmonyPostfix]
[HarmonyPatch("Start")]
private static void start(ref HUDManager __instance)
{
}
[HarmonyPostfix]
[HarmonyPatch("Update")]
private static void update(HUDManager __instance)
{
if (!TranslatePlugin.shouldTranslateHUD.Value)
{
return;
}
string text = ((TMP_Text)__instance.chatText).text;
if (lastChat.Length != text.Length)
{
string text2 = TranslateConfig.hudText.TryTranslate(text);
if (!string.IsNullOrEmpty(text2) && text2 != text)
{
((TMP_Text)__instance.chatText).text = text2;
}
}
lastChat = text;
}
private static void fadeText(TextMeshProUGUI text, ref bool fade, float duration, float newAlpha)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
float a = ((Graphic)text).color.a;
float num = 0f;
fade = true;
while (num < duration)
{
num += Time.deltaTime;
float num2 = Mathf.Lerp(a, newAlpha, num / duration);
((Graphic)text).color = new Color(((Graphic)text).color.r, ((Graphic)text).color.g, ((Graphic)text).color.b, num2);
}
((Behaviour)text).enabled = false;
fade = false;
}
[HarmonyPostfix]
[HarmonyPatch("AddChatMessage")]
private static void changeChatMessage(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped)
{
if (!TranslatePlugin.shouldTranslateHUD.Value || (Object)(object)__instance == (Object)null || ((object)__instance).Equals((object?)null))
{
return;
}
TMP_Text chatText = (TMP_Text)(object)__instance.chatText;
if ((Object)(object)chatText == (Object)null || ((object)chatText).Equals((object?)null) || !((Component)chatText).gameObject.activeInHierarchy)
{
return;
}
try
{
string text = chatText.text;
if (!string.IsNullOrEmpty(text))
{
string text2 = TranslateConfig.hudText.TryTranslate(text);
if (!string.IsNullOrEmpty(text2) && !text2.Equals(text))
{
chatText.text = text2;
}
}
}
catch (Exception ex)
{
TranslatePlugin.logger.LogError((object)("Error in changeChatMessage: " + ex.Message));
}
}
[HarmonyPrefix]
[HarmonyPatch("DisplayTip")]
[HarmonyPriority(int.MaxValue)]
private static bool changeTip(HUDManager __instance, ref string headerText, ref string bodyText, bool isWarning = false, bool useSave = false, string prefsKey = "LC_Tip1")
{
if (TranslatePlugin.shouldTranslateHUD.Value)
{
headerText = TranslateConfig.hudText.TryTranslate(headerText);
bodyText = TranslateConfig.hudText.TryTranslate(bodyText);
}
return true;
}
[HarmonyPrefix]
[HarmonyPatch("DisplayGlobalNotification")]
[HarmonyPriority(int.MaxValue)]
private static bool changeNotification(HUDManager __instance, ref string displayText)
{
if (TranslatePlugin.shouldTranslateHUD.Value)
{
displayText = TranslateConfig.hudText.TryTranslate(displayText);
}
return true;
}
}
[HarmonyPatch(typeof(Terminal))]
internal class TerminalPatch
{
public class Translator
{
public Dictionary<string, TerminalKeyword> keyValuePairs = new Dictionary<string, TerminalKeyword>();
public Dictionary<string, string> keys = new Dictionary<string, string>();
public TerminalKeyword getTranslateKey(TerminalKeyword old, bool useC)
{
if (!keyValuePairs.ContainsKey(((Object)old).name + old.word))
{
if ((useC && !HaveChineseCMD(old.word)) || (!useC && !HavePinyinCMD(old.word)))
{
keyValuePairs.Add(((Object)old).name + old.word, old);
return old;
}
string text = (useC ? getChineseCMD(old.word) : getPinyinCMD(old.word));
if (text == old.word)
{
keyValuePairs.Add(((Object)old).name + old.word, old);
return old;
}
TerminalKeyword val = Copy(old);
val.word = text;
keyValuePairs.Add(((Object)old).name + old.word, val);
if (val.compatibleNouns != null)
{
List<CompatibleNoun> list = val.compatibleNouns.ToList();
CompatibleNoun[] compatibleNouns = val.compatibleNouns;
foreach (CompatibleNoun val2 in compatibleNouns)
{
if (!getTranslateKey(val2.noun, useC).word.Equals(val2.noun.word))
{
CompatibleNoun val3 = TransReflection<CompatibleNoun>(val2);
val3.noun = getTranslateKey(val2.noun, useC);
list.Add(val3);
}
}
val.compatibleNouns = list.ToArray();
}
if ((Object)(object)val.defaultVerb != (Object)null && val.defaultVerb.compatibleNouns != null)
{
List<CompatibleNoun> list2 = val.defaultVerb.compatibleNouns.ToList();
CompatibleNoun[] compatibleNouns2 = val.defaultVerb.compatibleNouns;
foreach (CompatibleNoun val4 in compatibleNouns2)
{
if (!getTranslateKey(val4.noun, useC).word.Equals(val4.noun.word))
{
CompatibleNoun val5 = TransReflection<CompatibleNoun>(val4);
val5.noun = getTranslateKey(val4.noun, useC);
list2.Add(val5);
}
}
val.defaultVerb.compatibleNouns = list2.ToArray();
}
return val;
}
return keyValuePairs[((Object)old).name + old.word];
}
public void getTranslateKey(TerminalKeyword old)
{
if (keys.ContainsKey(old.word))
{
return;
}
keys.Add(old.word, old.word);
if (old.compatibleNouns != null)
{
old.compatibleNouns.ToList();
CompatibleNoun[] compatibleNouns = old.compatibleNouns;
foreach (CompatibleNoun val in compatibleNouns)
{
getTranslateKey(val.noun);
}
}
if ((Object)(object)old.defaultVerb != (Object)null && old.defaultVerb.compatibleNouns != null)
{
old.defaultVerb.compatibleNouns.ToList();
CompatibleNoun[] compatibleNouns2 = old.defaultVerb.compatibleNouns;
foreach (CompatibleNoun val2 in compatibleNouns2)
{
getTranslateKey(val2.noun);
}
}
}
}
public static FieldInfo hasGottenVerb = typeof(Terminal).GetField("hasGottenVerb", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
public static FieldInfo hasGottenNoun = typeof(Terminal).GetField("hasGottenNoun", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
public static bool shouldTranslate = false;
public static bool noText = false;
public static int texdAdded = 0;
public static FieldInfo modifyingText = typeof(Terminal).GetField("modifyingText", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
public static Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
public static TMP_InputField screenText = null;
public static TextTranslationInfo info;
public static HashSet<object> ig = new HashSet<object>();
private static int CheckForPlayerNameCommand(string firstWord, string secondWord)
{
if (firstWord == "radar")
{
return -1;
}
if (secondWord.Length <= 2)
{
return -1;
}
Debug.Log((object)("first word: " + firstWord + "; second word: " + secondWord));
List<string> list = new List<string>();
for (int i = 0; i < StartOfRound.Instance.mapScreen.radarTargets.Count; i++)
{
list.Add(StartOfRound.Instance.mapScreen.radarTargets[i].name);
Debug.Log((object)$"name {i}: {list[i]}");
}
string text = secondWord.ToLower();
for (int j = 0; j < list.Count; j++)
{
if (list[j].ToLower() == text)
{
return j;
}
}
Debug.Log((object)$"Target names length: {list.Count}");
for (int k = 0; k < list.Count; k++)
{
Debug.Log((object)"A");
string text2 = list[k].ToLower();
Debug.Log((object)$"Word #{k}: {text2}; length: {text2.Length}");
for (int num = secondWord.Length; num > 2; num--)
{
Debug.Log((object)$"c: {num}");
Debug.Log((object)secondWord.Substring(0, num));
if (text2.StartsWith(secondWord.Substring(0, num)))
{
return k;
}
}
}
return -1;
}
[HarmonyPostfix]
[HarmonyPatch("ParseWordOverrideOptions")]
private static void ParseWordOverrideOptions(string playerWord, CompatibleNoun[] options, ref TerminalNode __result)
{
for (int i = 0; i < options.Length; i++)
{
for (int num = playerWord.Length; num > 0; num--)
{
if (getChineseCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
{
__result = options[i].result;
return;
}
if (getPinyinCMD(options[i].noun.word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
{
__result = options[i].result;
return;
}
}
}
}
[HarmonyPostfix]
[HarmonyPatch("CheckForExactSentences")]
private static void CheckForExactSentences(Terminal __instance, string playerWord, ref TerminalKeyword __result)
{
for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++)
{
if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
{
__result = __instance.terminalNodes.allKeywords[i];
break;
}
if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
{
__result = __instance.terminalNodes.allKeywords[i];
break;
}
}
}
private static string RemovePunctuation(string s)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (char c in s)
{
if (!char.IsPunctuation(c))
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().ToLower();
}
[HarmonyPostfix]
[HarmonyPatch("CallFunctionInAccessibleTerminalObject")]
private static void CallFunctionInAccessibleTerminalObject(Terminal __instance, string word)
{
TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>();
for (int i = 0; i < array.Length; i++)
{
if (getChineseCMD(array[i].objectCode).EqualsIgnoreCase(word) || getPinyinCMD(array[i].objectCode).EqualsIgnoreCase(word))
{
Debug.Log((object)"Found accessible terminal object with corresponding string, calling function");
((object)__instance).GetType().GetField("broadcastedCodeThisFrame", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(TranslateConfig.terminal, true);
array[i].CallFunctionFromTerminal();
break;
}
}
}
[HarmonyPostfix]
[HarmonyPatch("ParseWord")]
private static void ParseWord(Terminal __instance, string playerWord, int specificityRequired, ref TerminalKeyword __result)
{
if (!TranslatePlugin.TerimalCanUseChinese.Value && !TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value)
{
return;
}
if (playerWord.Length < specificityRequired)
{
__result = null;
return;
}
TerminalKeyword val = null;
for (int i = 0; i < __instance.terminalNodes.allKeywords.Length; i++)
{
if (__instance.terminalNodes.allKeywords[i].isVerb && (bool)hasGottenVerb.GetValue(__instance))
{
continue;
}
bool accessTerminalObjects = __instance.terminalNodes.allKeywords[i].accessTerminalObjects;
if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
{
__result = __instance.terminalNodes.allKeywords[i];
return;
}
if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).EqualsIgnoreCase(playerWord))
{
__result = __instance.terminalNodes.allKeywords[i];
return;
}
if (!((Object)(object)val == (Object)null))
{
continue;
}
for (int num = playerWord.Length; num > specificityRequired; num--)
{
if (getChineseCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
{
val = __instance.terminalNodes.allKeywords[i];
}
if (getPinyinCMD(__instance.terminalNodes.allKeywords[i].word).ToLower().StartsWith(playerWord.Substring(0, num).ToLower()))
{
val = __instance.terminalNodes.allKeywords[i];
}
}
}
if ((Object)(object)val != (Object)null)
{
__result = val;
}
}
[HarmonyPostfix]
[HarmonyPatch("ParsePlayerSentence")]
private static void customParser(Terminal __instance, ref TerminalNode __result)
{
string[] array = RemovePunctuation(__instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded)).Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["transmit"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("transmit") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["transmit"]))))
{
try
{
string text = array[1];
SignalTranslator val = Object.FindObjectOfType<SignalTranslator>();
if ((Object)(object)val != (Object)null && Time.realtimeSinceStartup - val.timeLastUsingSignalTranslator > 8f && text.Length > 1)
{
if (!((NetworkBehaviour)__instance).IsServer)
{
val.timeLastUsingSignalTranslator = Time.realtimeSinceStartup;
}
__result = __instance.terminalNodes.specialNodes[22];
HUDManager.Instance.UseSignalTranslatorServerRpc(text.Substring(0, Mathf.Min(text.Length, 10)));
}
return;
}
catch (Exception ex)
{
TranslatePlugin.logger.LogError((object)ex.Message);
return;
}
}
if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["switch"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("switch") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["switch"]))))
{
int num = CheckForPlayerNameCommand(array[0], array[1]);
if (num != -1)
{
StartOfRound.Instance.mapScreen.SwitchRadarTargetAndSync(num);
__result = __instance.terminalNodes.specialNodes[20];
}
}
else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["ping"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("ping") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["ping"]))))
{
int num2 = CheckForPlayerNameCommand(array[0], array[1]);
if (num2 != -1)
{
StartOfRound.Instance.mapScreen.PingRadarBooster(num2);
__result = __instance.terminalNodes.specialNodes[21];
}
}
else if (array.Length > 1 && ((TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_zh.normal["flash"])) || (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey("flash") && array[0].ToLower().Equals(TranslateConfig.cmd_py.normal["flash"]))))
{
int num3 = CheckForPlayerNameCommand(array[0], array[1]);
if (num3 != -1)
{
StartOfRound.Instance.mapScreen.FlashRadarBooster(num3);
__result = __instance.terminalNodes.specialNodes[23];
}
else if (StartOfRound.Instance.mapScreen.radarTargets[StartOfRound.Instance.mapScreen.targetTransformIndex].isNonPlayer)
{
StartOfRound.Instance.mapScreen.FlashRadarBooster(StartOfRound.Instance.mapScreen.targetTransformIndex);
__result = __instance.terminalNodes.specialNodes[23];
}
}
}
private static bool HaveChineseCMD(string name)
{
return TranslateConfig.cmd_zh.normal.ContainsKey(name);
}
private static bool HavePinyinCMD(string name)
{
return TranslateConfig.cmd_py.normal.ContainsKey(name);
}
private static string getChineseCMD(string name)
{
if (TranslatePlugin.TerimalCanUseChinese.Value && TranslateConfig.cmd_zh.normal.ContainsKey(name))
{
return TranslateConfig.cmd_zh.normal[name];
}
return "";
}
private static string getPinyinCMD(string name)
{
if (TranslatePlugin.TerimalCanUsePinyinAbbreviation.Value && TranslateConfig.cmd_py.normal.ContainsKey(name))
{
return TranslateConfig.cmd_py.normal[name];
}
return "";
}
private static string getOrginByChineseCMD(string name)
{
if (TranslateConfig.cmd_zh.special.ContainsKey(name))
{
return TranslateConfig.cmd_zh.special[name];
}
return name;
}
private static string getOrginByPinyinCMD(string name)
{
if (TranslateConfig.cmd_py.special.ContainsKey(name))
{
return TranslateConfig.cmd_py.special[name];
}
return name;
}
private static T TransReflection<T>(T tIn)
{
T val = Activator.CreateInstance<T>();
Type typeFromHandle = typeof(T);
foreach (FieldInfo runtimeField in typeFromHandle.GetRuntimeFields())
{
typeFromHandle.GetRuntimeField(runtimeField.Name).SetValue(val, runtimeField.GetValue(tIn));
}
return val;
}
public static TerminalKeyword Copy(TerminalKeyword oldOne)
{
return TransReflection<TerminalKeyword>(oldOne);
}
[HarmonyPostfix]
[HarmonyPatch("LoadNewNode")]
private static void changeNewNodeText(Terminal __instance, TerminalNode node)
{
if (info != null)
{
info.Reset(__instance.screenText.text);
}
}
[HarmonyPrefix]
[HarmonyPatch("OnSubmit")]
private static void changeSubmit(Terminal __instance)
{
if (info != null)
{
texdAdded = __instance.currentText.Length - info.OriginalText.Length;
if (texdAdded != 0)
{
info.Reset(__instance.currentText);
}
}
}
[HarmonyPostfix]
[HarmonyPatch("Update")]
private static void changeUpdateText(Terminal __instance)
{
try
{
if ((info == null || !info.IsTranslated) && info != null && !info.IsTranslated && TranslatePlugin.shouldTranslateTerimal.Value)
{
if (TranslatePlugin.showAvailableText.Value && !string.IsNullOrEmpty(__instance.currentText) && TextTranslate.ShouldOutputDebug("terminal:" + __instance.currentText))
{
TranslatePlugin.LogInfo("[Debug] Terminal available text: '" + __instance.currentText + "'");
}
string translatedText = TranslateConfig.replaceByMap(__instance.currentText, TranslateConfig.terminal);
info.SetTranslatedText(translatedText);
SetText(info.TranslatedText, __instance);
info.OriginalText = __instance.currentText;
}
}
catch (Exception ex)
{
TranslatePlugin.logger.LogWarning((object)ex);
}
}
public static void SetText(string text, Terminal Instance)
{
if (!((Object)(object)Instance == (Object)null))
{
modifyingText.SetValue(Instance, true);
((Selectable)Instance.screenText).interactable = true;
Instance.screenText.text = text;
Instance.currentText = Instance.screenText.text;
if ((Object)(object)Instance.screenText.verticalScrollbar != (Object)null)
{
Instance.screenText.verticalScrollbar.value = 0f;
}
}
}
[HarmonyPatch("Start")]
[HarmonyPostfix]
private static void startTerminal(Terminal __instance)
{
info = __instance.screenText.GetOrCreateTextTranslationInfo();
ig.Clear();
foreach (FieldInfo runtimeField in ((object)__instance).GetType().GetRuntimeFields())
{
if (runtimeField.GetValue(__instance) != null && UnityTypes.TMP_Text.IsAssignableFrom(runtimeField.GetValue(__instance).GetType()))
{
ig.Add(runtimeField.GetValue(__instance));
}
}
info.MustIgnore = true;
}
}
}
namespace GameTranslator.Patches.Utils
{
internal static class ComponentExtensions
{
private static readonly string XuaIgnore = "XUAIGNORE";
private static readonly string XuaIgnoreTree = "XUAIGNORETREE";
private static bool _guiContentCheckFailed = false;
public static bool SupportsStabilization(this object ui)
{
if (ui == null)
{
return false;
}
if (!_guiContentCheckFailed)
{
return !IsGUIContentSafe(ui);
}
return true;
}
public static bool IsSpammingComponent(this object ui)
{
if (ui != null)
{
if (!_guiContentCheckFailed)
{
return IsGUIContentSafe(ui);
}
return false;
}
return true;
}
public static bool ShouldIgnoreTextComponent(this object ui)
{
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Expected O, but got Unknown
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Expected O, but got Unknown
Component val = (Component)((ui is Component) ? ui : null);
if (val != null && (Object)(object)val != (Object)null)
{
Transform val2 = val.transform;
if (((Object)val2).name.Contains(XuaIgnore))
{
return true;
}
while ((Object)(object)val2.parent != (Object)null)
{
val2 = val2.parent;
if (((Object)val2).name.Contains(XuaIgnoreTree))
{
return true;
}
}
Component val3 = null;
if (GetTypeByName("UnityEngine.UI.InputField") != null)
{
val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("UnityEngine.UI.InputField"));
if ((Object)(object)val3 != (Object)null)
{
PropertyInfo property = ((object)val3).GetType().GetProperty("placeholder");
if (property != null)
{
Component val4 = (Component)property.GetValue(val3);
return val4 != val;
}
}
}
if (GetTypeByName("TMPro.TMP_InputField") != null)
{
val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("TMPro.TMP_InputField"));
if ((Object)(object)val3 != (Object)null)
{
PropertyInfo property2 = ((object)val3).GetType().GetProperty("placeholder");
if (property2 != null)
{
Component val5 = (Component)property2.GetValue(val3);
return val5 != val;
}
}
}
val3 = val.gameObject.GetFirstComponentInSelfOrAncestor(GetTypeByName("UIInput"));
return (Object)(object)val3 != (Object)null;
}
return false;
}
public static string GetPath(this object ui)
{
Component val = (Component)((ui is Component) ? ui : null);
if (val != null && (Object)(object)val != (Object)null)
{
StringBuilder stringBuilder = new StringBuilder();
Transform val2 = val.transform;
List<string> list = new List<string>();
while ((Object)(object)val2 != (Object)null)
{
list.Add(((Object)val2).name);
val2 = val2.parent;
}
list.Reverse();
foreach (string item in list)
{
stringBuilder.Append("/").Append(item);
}
return stringBuilder.ToString();
}
return "Unknown";
}
private static bool IsGUIContentSafe(object ui)
{
try
{
return IsGUIContentUnsafe(ui);
}
catch
{
_guiContentCheckFailed = true;
}
return false;
}
private static bool IsGUIContentUnsafe(object ui)
{
return ui is GUIContent;
}
private static bool SetTextOnGUIContentSafe(object ui, string text)
{
try
{
return SetTextOnGUIContentUnsafe(ui, text);
}
catch
{
_guiContentCheckFailed = true;
}
return false;
}
private static bool SetTextOnGUIContentUnsafe(object ui, string text)
{
GUIContent val = (GUIContent)((ui is GUIContent) ? ui : null);
if (val != null)
{
val.text = text;
return true;
}
return false;
}
private static bool TryGetTextFromGUIContentSafe(object ui, out string text)
{
try
{
return TryGetTextFromGUIContentUnsafe(ui, out text);
}
catch
{
_guiContentCheckFailed = false;
}
text = null;
return false;
}
private static bool TryGetTextFromGUIContentUnsafe(object ui, out string text)
{
GUIContent val = (GUIContent)((ui is GUIContent) ? ui : null);
if (val != null)
{
text = val.text;
return true;
}
text = null;
return false;
}
private static Type GetTypeByName(string typeName)
{
try
{
return Type.GetType(typeName);
}
catch
{
return null;
}
}
private static Component GetFirstComponentInSelfOrAncestor(this GameObject go, Type type)
{
if (type == null)
{
return null;
}
GameObject val = go;
while ((Object)(object)val != (Object)null)
{
Component component = val.GetComponent(type);
if ((Object)(object)component != (Object)null)
{
return component;
}
Transform transform = val.transform;
object obj;
if (transform == null)
{
obj = null;
}
else
{
Transform parent = transform.parent;
obj = ((parent != null) ? ((Component)parent).gameObject : null);
}
val = (GameObject)obj;
}
return null;
}
private static string GetTextFromComponent(object ui, TextTranslationInfo info)
{
if (ui == null || info?.TextManipulator == null)
{
return null;
}
try
{
return info.TextManipulator.GetText(ui);
}
catch (Exception ex)
{
TranslatePlugin.logger.LogError((object)("Error in GetTextFromComponent: " + ex.Message));
return null;
}
}
private static void SetTextOnComponent(object ui, string text, TextTranslationInfo info)
{
if (ui == null || info?.TextManipulator == null)
{
return;
}
try
{
info.TextManipulator.SetText(ui, text);
}
catch (Exception ex)
{
TranslatePlugin.logger.LogError((object)("Error in SetTextOnComponent: " + ex.Message));
}
}
}
internal static class FontCache
{
private static bool _hasReadFallbackFontTextMeshPro;
private static Object FallbackFontTextMeshPro;
public static object GetOrCreateFallbackFontTextMeshPro()
{
if (!_hasReadFallbackFontTextMeshPro)
{
try
{
_hasReadFallbackFontTextMeshPro = true;
if (string.IsNullOrEmpty(TranslatePlugin.fallbackFontTextMeshPro.Value))
{
FallbackFontTextMeshPro = null;
return null;
}
string assetBundle = Path.Combine(TranslatePlugin.DefaultPath, TranslatePlugin.fallbackFontTextMeshPro.Value);
FallbackFontTextMeshPro = FontHelper.GetTextMeshProFont(assetBundle);
}
catch (Exception ex) when (ex.ToString().ToLowerInvariant().Contains("missing") || ex.ToString().ToLowerInvariant().Contains("not found"))
{
TranslatePlugin.logger.LogWarning((object)("An error occurred while loading text mesh pro fallback font. This may be due to missing font file. Error: " + ex.Message));
}
catch (Exception ex2)
{
TranslatePlugin.logger.LogError((object)("An error occurred while loading text mesh pro fallback font: " + TranslatePlugin.fallbackFontTextMeshPro.Value + ". Error: " + ex2.Message));
}
}
return FallbackFontTextMeshPro;
}
}
internal static class FontHelper
{
private static readonly List<AssetBundle> _loadedBundles = new List<AssetBundle>();
public static Object GetTextMeshProFont(string assetBundle)
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Expected O, but got Unknown
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Expected O, but got Unknown
if (string.IsNullOrEmpty(assetBundle))
{
return null;
}
Object val = null;
string text = Path.Combine(Paths.GameRoot, assetBundle);
if (File.Exists(text))
{
TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from asset bundle: " + text));
AssetBundle val2 = null;
if (AssetBundle_Methods.LoadFromFile != null)
{
val2 = (AssetBundle)AssetBundle_Methods.LoadFromFile.Invoke((object)null, new object[1] { text });
}
else
{
if (AssetBundle_Methods.CreateFromFile == null)
{
TranslatePlugin.logger.LogError((object)("Could not find an appropriate asset bundle load method while loading font: " + text));
return null;
}
val2 = (AssetBundle)AssetBundle_Methods.CreateFromFile.Invoke((object)null, new object[1] { text });
}
if ((Object)(object)val2 == (Object)null)
{
TranslatePlugin.logger.LogWarning((object)("Could not load asset bundle while loading font: " + text));
return null;
}
_loadedBundles.Add(val2);
if (UnityTypes.TMP_FontAsset != null)
{
if (AssetBundle_Methods.LoadAllAssets != null)
{
val = ((Object[])AssetBundle_Methods.LoadAllAssets.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault();
}
else if (AssetBundle_Methods.LoadAll != null)
{
val = ((Object[])AssetBundle_Methods.LoadAll.Invoke((object)val2, new object[1] { UnityTypes.TMP_FontAsset.UnityType }))?.FirstOrDefault();
}
}
}
else
{
TranslatePlugin.logger.LogInfo((object)("Attempting to load TextMesh Pro font from internal Resources API: " + text));
val = Resources.Load(assetBundle);
}
if (val != (Object)null)
{
CachedProperty version = TMP_FontAsset_Properties.Version;
string text2 = ((string)((version != null) ? version.Get((object)val) : null)) ?? "Unknown";
TranslatePlugin.logger.LogInfo((object)("Loaded TextMesh Pro font uses version: " + text2));
Object.DontDestroyOnLoad(val);
}
else
{
TranslatePlugin.logger.LogError((object)("Could not find the TextMeshPro font asset: " + assetBundle));
}
return val;
}
public static string[] GetOSInstalledFontNames()
{
return Font.GetOSInstalledFontNames();
}
public static void UnloadAllBundles()
{
foreach (AssetBundle loadedBundle in _loadedBundles)
{
try
{
if ((Object)(object)loadedBundle != (Object)null)
{
loadedBundle.Unload(true);
}
}
catch (Exception ex)
{
TranslatePlugin.logger.LogError((object)("Error unloading bundle: " + ex.Message));
}
}
_loadedBundles.Clear();
}
}
public static class FontSupportChecker
{
private static readonly ConcurrentDictionary<TMP_FontAsset, bool> _availableFonts = new ConcurrentDictionary<TMP_FontAsset, bool>();
private static readonly Dictionary<char, bool> _characterSupportCache = new Dictionary<char, bool>();
private static readonly LRUCache<string, string> _textCache = new LRUCache<string, string>(1000);
private static bool _isInitialized = false;
private static readonly object _lockObject = new object();
public static void InitializeFonts()
{
if (_isInitialized)
{
return;
}
lock (_lockObject)
{
if (_isInitialized)
{
return;
}
_availableFonts.Clear();
_characterSupportCache.Clear();
_textCache.Clear();
object orCreateFallbackFontTextMeshPro = FontCache.GetOrCreateFallbackFontTextMeshPro();
TMP_FontAsset val = (TMP_FontAsset)((orCreateFallbackFontTextMeshPro is TMP_FontAsset) ? orCreateFallbackFontTextMeshPro : null);
if ((Object)(object)val != (Object)null)
{
AddFont(val);
}
TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
TMP_FontAsset[] array2 = array;
foreach (TMP_FontAsset val2 in array2)
{
if ((Object)(object)val2 != (Object)null && !_availableFonts.ContainsKey(val2))
{
AddFont(val2);
}
}
_isInitialized = true;
TranslatePlugin.logger.LogInfo((object)$"FontSupportChecker initialized with {_availableFonts.Count} fonts");
}
}
private static void AddFont(TMP_FontAsset font)
{
if ((Object)(object)font == (Object)null || _availableFonts.ContainsKey(font))
{
return;
}
_availableFonts.TryAdd(font, value: true);
if (font.fallbackFontAssetTable == null)
{
return;
}
foreach (TMP_FontAsset item in font.fallbackFontAssetTable)
{
if ((Object)(object)item != (Object)null && !_availableFonts.ContainsKey(item))
{
_availableFonts.TryAdd(item, value: true);
}
}
}
public static void RegisterFont(TMP_FontAsset font)
{
if ((Object)(object)font == (Object)null)
{
return;
}
lock (_lockObject)
{
AddFont(font);
_characterSupportCache.Clear();
_textCache.Clear();
TranslatePlugin.logger.LogDebug((object)("Registered new font: " + ((Object)font).name));
}
}
public static bool IsCharacterSupported(char character)
{
if (!_isInitialized)
{
return true;
}
if (_characterSupportCache.TryGetValue(character, out var value))
{
return value;
}
value = _availableFonts.Keys.Any((TMP_FontAsset font) => (Object)(object)font != (Object)null && font.HasCharacter(character, true, true));
_characterSupportCache[character] = value;
return value;
}
public static string ReplaceUnsupportedCharacters(string text, TMP_Text textComponent = null)
{
if (string.IsNullOrEmpty(text) || !TranslatePlugin.replaceUnsupportedCharacters.Value)
{
return text;
}
if (!_isInitialized)
{
InitializeFonts();
}
if (_textCache.TryGetValue(text, out var value))
{
return value;
}
bool flag = true;
foreach (char character in text)
{
if (!IsCharacterSupported(character))
{
flag = false;
break;
}
}
if (flag)
{
_textCache.Add(text, text);
return text;
}
StringBuilder stringBuilder = new StringBuilder();
bool flag2 = false;
foreach (char c in text)
{
if (char.IsControl(c))
{
stringBuilder.Append(c);
continue;
}
if (IsCharacterSupported(c))
{
stringBuilder.Append(c);
continue;
}
stringBuilder.Append('□');
flag2 = true;
}
string text2 = stringBuilder.ToString();
if (flag2 && TranslatePlugin.showOtherDebug.Value)
{
TranslatePlugin.logger.LogInfo((object)("[FontSupport] Replaced unsupported characters in text: '" + text + "' -> '" + text2 + "'"));
}
_textCache.Add(text, text2);
return text2;
}
public static void ClearCache()
{
lock (_lockObject)
{
_characterSupportCache.Clear();
_textCache.Clear();
TranslatePlugin.logger.LogDebug((object)"FontSupportChecker cache cleared");
}
}
public static string GetStats()
{
return $"Fonts: {_availableFonts.Count}, CharacterCache: {_characterSupportCache.Count}, TextCache: {_textCache.Count}";
}
}
public class LRUCache<TKey, TValue>
{
private class CacheItem
{
public TKey Key { get; set; }
public TValue Value { get; set; }
}
private readonly int _capacity;
private readonly Dictionary<TKey, LinkedListNode<CacheItem>> _cacheMap;
private readonly LinkedList<CacheItem> _lruList;
public int Count => _cacheMap.Count;
public LRUCache(int capacity)
{
_capacity = capacity;
_cacheMap = new Dictionary<TKey, LinkedListNode<CacheItem>>(capacity);
_lruList = new LinkedList<CacheItem>();
}
public bool TryGetValue(TKey key, out TValue value)
{
if (_cacheMap.TryGetValue(key, out var value2))
{
value = value2.Value.Value;
_lruList.Remove(value2);
_lruList.AddFirst(value2);
return true;
}
value = default(TValue);
return false;
}
public void Add(TKey key, TValue value)
{
if (_cacheMap.TryGetValue(key, out var value2))
{
_lruList.Remove(value2);
}
else if (_cacheMap.Count >= _capacity)
{
RemoveLeastRecentlyUsed();
}
LinkedListNode<CacheItem> linkedListNode = new LinkedListNode<CacheItem>(new CacheItem
{
Key = key,
Value = value
});
_lruList.AddFirst(linkedListNode);
_cacheMap[key] = linkedListNode;
}
public void Clear()
{
_cacheMap.Clear();
_lruList.Clear();
}
private void RemoveLeastRecentlyUsed()
{
LinkedListNode<CacheItem> last = _lruList.Last;
if (last != null)
{
_cacheMap.Remove(last.Value.Key);
_lruList.RemoveLast();
}
}
}
internal sealed class SafeFileWatcher : IDisposable
{
private int _counter;
private FileSystemWatcher _watcher;
private bool _disposed;
private object _sync = new object();
private Timer _timer;
private readonly string _directory;
public event Action DirectoryUpdated;
public SafeFileWatcher(string directory)
{
_directory = directory;
_timer = new Timer(RaiseEvent, null, -1, -1);
EnableWatcher();
}
public void EnableWatcher()
{
if (_watcher == null)
{
_watcher = new FileSystemWatcher(_directory);
_watcher.Changed += Watcher_Changed;
_watcher.Created += Watcher_Created;
_watcher.Deleted += Watcher_Deleted;
_watcher.EnableRaisingEvents = true;
}
}
public void DisableWatcher()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher.Dispose();
_watcher = null;
}
}
public void RaiseEvent(object state)
{
this.DirectoryUpdated?.Invoke();
}
public void Disable()
{
int num = Interlocked.Increment(ref _counter);
UpdateRaisingEvents(num == 0);
}
public void Enable()
{
int num = Interlocked.Decrement(ref _counter);
UpdateRaisingEvents(num == 0);
}
private void Watcher_Deleted(object sender, FileSystemEventArgs e)
{
_timer.Change(1000, -1);
}
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
FileInfo file = new FileInfo(e.FullPath);
WaitForFile(file);
_timer.Change(1000, -1);
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
_timer.Change(1000, -1);
}
private void UpdateRaisingEvents(bool enabled)
{
lock (_sync)
{
if (enabled)
{
EnableWatcher();
}
else
{
DisableWatcher();
}
}
}
private void WaitForFile(FileInfo file)
{
while (IsFileLocked(file))
{
Thread.Sleep(100);
}
}
private bool IsFileLocked(FileInfo file)
{
try
{
using (file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
}
}
catch (IOException)
{
return true;
}
return false;
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_watcher?.Dispose();
_watcher = null;
_timer.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
internal static class SceneManagerLoader
{
public static void EnableSceneLoadScanInternal(Action<int> sceneLoaded)
{
SceneManager.sceneLoaded += delegate(Scene arg1, LoadSceneMode arg2)
{
sceneLoaded(((Scene)(ref arg1)).buildIndex);
};
}
}
public class StringBuffer
{
private char[] value;
private int length;
private int capacity;
private const int DEFAULT_CAPACITY = 16;
public int Length
{
get
{
return length;
}
set
{
if (value < 0 || value > capacity)
{
throw new ArgumentOutOfRangeException("value");
}
if (value < length)
{
Array.Clear(this.value, value, length - value);
}
length = value;
}
}
public int Capacity
{
get
{
return capacity;
}
set
{
if (value < length)
{
throw new ArgumentOutOfRangeException("value");
}
if (value != capacity)
{
char[] destinationArray = new char[value];
Array.Copy(this.value, 0, destinationArray, 0, length);
this.value = destinationArray;
capacity = value;
}
}
}
public StringBuffer()
{
value = new char[16];
length = 0;
capacity = 16;
}
public StringBuffer(int capacity)
{
if (capacity < 0)
{
throw new ArgumentOutOfRangeException("capacity");
}
value = new char[capacity];
length = 0;
this.capacity = capacity;
}
public StringBuffer(string str)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
value = new char[str.Length + 16];
str.CopyTo(0, value, 0, str.Length);
length = str.Length;
capacity = str.Length + 16;
}
public void EnsureCapacity(int minimumCapacity)
{
if (minimumCapacity < 0)
{
throw new ArgumentOutOfRangeException("minimumCapacity");
}
if (minimumCapacity > capacity)
{
int num = capacity * 2;
if (num < minimumCapacity)
{
num = minimumCapacity;
}
Capacity = num;
}
}
public StringBuffer Append(object obj)
{
if (obj == null)
{
return this;
}
return Append(obj.ToString());
}
public StringBuffer Append(string str)
{
if (str == null)
{
return this;
}
int num = str.Length;
EnsureCapacity(length + num);
str.CopyTo(0, value, length, num);
length += num;
return this;
}
public StringBuffer Append(char c)
{
EnsureCapacity(length + 1);
value[length] = c;
length++;
return this;
}
public StringBuffer Insert(int index, object obj)
{
if (obj == null)
{
return this;
}
return Insert(index, obj.ToString());
}
public StringBuffer Insert(int index, string str)
{
if (index < 0 || index > length)
{
throw new ArgumentOutOfRangeException("index");
}
if (str == null)
{
return this;
}
int num = str.Length;
EnsureCapacity(length + num);
Array.Copy(value, index, value, index + num, length - index);
str.CopyTo(0, value, index, num);
length += num;
return this;
}
public StringBuffer Insert(int index, char c)
{
if (index < 0 || index > length)
{
throw new ArgumentOutOfRangeException("index");
}
EnsureCapacity(length + 1);
Array.Copy(value, index, value, index + 1, length - index);
value[index] = c;
length++;
return this;
}
public StringBuffer Remove(int startIndex, int length)
{
if (startIndex < 0 || startIndex > this.length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (length < 0 || startIndex + length > this.length)
{
throw new ArgumentOutOfRangeException("length");
}
Array.Copy(value, startIndex + length, value, startIndex, this.length - startIndex - length);
Array.Clear(value, this.length - length, length);
this.length -= length;
return this;
}
public StringBuffer Replace(char oldChar, char newChar)
{
for (int i = 0; i < length; i++)
{
if (value[i] == oldChar)
{
value[i] = newChar;
}
}
return this;
}
public StringBuffer Replace(string oldValue, string newValue)
{
if (oldValue == null)
{
throw new ArgumentNullException("oldValue");
}
if (oldValue.Length == 0)
{
throw new ArgumentException("oldValue cannot be empty");
}
if (newValue == null)
{
newValue = string.Empty;
}
int num = oldValue.Length;
int num2 = newValue.Length;
for (int num3 = IndexOf(oldValue); num3 >= 0; num3 = IndexOf(oldValue, num3 + num2))
{
Remove(num3, num);
Insert(num3, newValue);
}
return this;
}
public int IndexOf(char c)
{
return IndexOf(c, 0, length);
}
public int IndexOf(char c, int startIndex)
{
return IndexOf(c, startIndex, length - startIndex);
}
public int IndexOf(char c, int startIndex, int count)
{
if (startIndex < 0 || startIndex > length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (count < 0 || startIndex + count > length)
{
throw new ArgumentOutOfRangeException("count");
}
return Array.IndexOf(value, c, startIndex, count);
}
public int IndexOf(string str)
{
return IndexOf(str, 0, length);
}
public int IndexOf(string str, int startIndex)
{
return IndexOf(str, startIndex, length - startIndex);
}
public int IndexOf(string str, int startIndex, int count)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
if (startIndex < 0 || startIndex > length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (count < 0 || startIndex + count > length)
{
throw new ArgumentOutOfRangeException("count");
}
return value.ToString(startIndex, count).IndexOf(str);
}
public string Substring(int startIndex)
{
return Substring(startIndex, length - startIndex);
}
public string Substring(int startIndex, int length)
{
if (startIndex < 0 || startIndex > this.length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (length < 0 || startIndex + length > this.length)
{
throw new ArgumentOutOfRangeException("length");
}
return new string(value, startIndex, length);
}
public bool Contains(string text)
{
return IndexOf(text) >= 0;
}
public StringBuffer ReplaceFull(string oldValue, string newValue)
{
if (oldValue == null)
{
throw new ArgumentNullException("oldValue");
}
if (oldValue.Length == 0)
{
throw new ArgumentException("oldValue cannot be empty");
}
if (newValue == null)
{
newValue = string.Empty;
}
int num = oldValue.Length;
int num2 = newValue.Length;
for (int num3 = IndexOfWord(oldValue); num3 >= 0; num3 = IndexOfWord(oldValue, num3 + num2))
{
Remove(num3, num);
Insert(num3, newValue);
}
return this;
}
private int IndexOfWord(string str)
{
return IndexOfWord(str, 0, length);
}
private int IndexOfWord(string str, int startIndex)
{
return IndexOfWord(str, startIndex, length - startIndex);
}
public int IndexOfWord(string str, int startIndex, int count)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
if (startIndex < 0 || startIndex > length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (count < 0 || startIndex + count > length)
{
throw new ArgumentOutOfRangeException("count");
}
int num = str.Length;
int[] array = new int[num];
int num2 = 0;
computeLPSArray(str, num, array);
int num3 = startIndex;
while (num3 < startIndex + count)
{
if (str[num2] == value[num3])
{
num2++;
num3++;
}
if (num2 == num)
{
if ((num3 - num2 == 0 || !char.IsLetter(value[num3 - num2 - 1])) && (num3 == length || !char.IsLetter(value[num3])))
{
return num3 - num2;
}
num2 = array[num2 - 1];
}
else if (num3 < startIndex + count && str[num2] != value[num3])
{
if (num2 != 0)
{
num2 = array[num2 - 1];
}
else
{
num3++;
}
}
}
return -1;
}
private void computeLPSArray(string str, int M, int[] lps)
{
int num = 0;
int num2 = 1;
lps[0] = 0;
while (num2 < M)
{
if (str[num2] == str[num])
{
num = (lps[num2] = num + 1);
num2++;
}
else if (num != 0)
{
num = lps[num - 1];
}
else
{
lps[num2] = num;
num2++;
}
}
}
private static bool IsWordChar(char c)
{
if (!char.IsLetter(c))
{
return c == '_';
}
return true;
}
public StringBuffer Clear()
{
Length = 0;
return this;
}
public override string ToString()
{
return new string(value, 0, length);
}
}
public static class StringBuilderExtensions
{
public static bool EndsWithWhitespaceOrNewline(this StringBuilder builder)
{
if (builder.Length == 0)
{
return true;
}
char c = builder[builder.Length - 1];
if (!char.IsWhiteSpace(c))
{
return c == '\n';
}
return true;
}
internal static StringBuilder Reverse(this StringBuilder text)
{
if (text.Length > 1)
{
int num = text.Length / 2;
for (int i = 0; i < num; i++)
{
int index = text.Length - (i + 1);
char value = text[i];
char value2 = text[index];
text[i] = value2;
text[index] = value;
}
}
return text;
}
}
internal static class TemplatingHelper
{
public static bool ContainsUntemplatedCharacters(string text)
{
bool flag = false;
int length = text.Length;
for (int i = 0; i < length; i++)
{
char c = text[i];
if (flag)
{
if (c == '}')
{
int num = i + 1;
if (num < length && text[num] == '}')
{
i++;
flag = false;
}
}
}
else if (c == '{')
{
int num2 = i + 1;
if (num2 < length && text[num2] == '{')
{
i++;
flag = true;
}
}
else if (!char.IsWhiteSpace(c))
{
return true;
}
}
return false;
}
public static TemplatedString TemplatizeByReplacementsAndNumbers(this string str)
{
TemplatedString templatedString = str.TemplatizeByReplacements();
if (templatedString == null)
{
return str.TemplatizeByNumbers();
}
return templatedString.Template.TemplatizeByNumbers(templatedString);
}
public static TemplatedString TemplatizeByReplacements(this string str)
{
return null;
}
public static bool IsNumberOrDotOrControl(char c)
{
if ('*' > c || c > ':')
{
if ('0' <= c)
{
return c <= '9';
}
return false;
}
return true;
}
public static bool IsNumber(char c)
{
if ('0' > c || c > '9')
{
if ('0' <= c)
{
return c <= '9';
}
return false;
}
return true;
}
public static TemplatedString TemplatizeByNumbers(this string str, TemplatedString existingTemplatedString = null)
{
int num = 0;
if (existingTemplatedString != null)
{
num = existingTemplatedString.Arguments?.Count ?? 0;
str = existingTemplatedString.Template ?? str;
}
Dictionary<string, string> dictionary = new Dictionary<string, string>();
bool flag = false;
StringBuilder stringBuilder = null;
char c = (char)(65 + num);
int num2 = -1;
int num3 = -1;
for (int i = 0; i < str.Length; i++)
{
char c2 = str[i];
if (flag)
{
if (IsNumber(c2))
{
num3 = i;
}
if (IsNumberOrDotOrControl(c2))
{
stringBuilder.Append(c2);
continue;
}
int num4 = i - num3 - 1;
stringBuilder.Remove(stringBuilder.Length - num4, num4);
string text = stringBuilder.ToString();
string text2 = "{{" + c + "}}";
dictionary.Add(text2, text);
c = (char)(c + 1);
stringBuilder = null;
flag = false;
str = str.Remove(num2, num3 - num2 + 1).Insert(num2, text2);
i += text2.Length - text.Length;
}
else if (IsNumber(c2))
{
flag = true;
stringBuilder = new StringBuilder();
stringBuilder.Append(c2);
num2 = i;
num3 = i;
}
}
if (stringBuilder != null)
{
int num5 = str.Length - num3 - 1;
stringBuilder.Remove(stringBuilder.Length - num5, num5);
string value = stringBuilder.ToString();
string text3 = "{{" + c + "}}";
dictionary.Add(text3, value);
str = str.Remove(num2, str.Length - num2 - num5).Insert(num2, text3);
}
if (dictionary.Count > 0)
{
Dictionary<string, string> dictionary2 = existingTemplatedString?.Arguments ?? dictionary;
if (dictionary != dictionary2)
{
foreach (KeyValuePair<string, string> item in dictionary)
{
dictionary2.Add(item.Key, item.Value);
}
}
return new TemplatedString(str, dictionary2);
}
return existingTemplatedString;
}
}
internal static class TextHelper
{
public static string Encode(string text)
{
return EscapeNewlines(text);
}
public static string[] ReadTranslationLineAndDecode(string str)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
string[] array = new string[2];
int num = 0;
bool flag = false;
int length = str.Length;
StringBuilder stringBuilder = new StringBuilder((int)((double)length / 1.3));
for (int i = 0; i < length; i++)
{
char c = str[i];
if (flag)
{
char c2 = c;
if (c2 <= '\\')
{
if (c2 != '=' && c2 != '\\')
{
stringBuilder.Append('\\');
stringBuilder.Append(c);
flag = false;
continue;
}
stringBuilder.Append(c);
}
else
{
switch (c2)
{
default:
stringBuilder.Append('\\');
stringBuilder.Append(c);
flag = false;
continue;
case 'u':
{
if (i + 4 >= length)
{
throw new Exception("Found invalid unicode in line: " + str);
}
if (i + 1 >= length || i + 2 >= length || i + 3 >= length || i + 4 >= length)
{
throw new Exception("Invalid unicode escape sequence at position " + i + " in line: " + str);
}
int num2 = int.Parse(new string(new char[4]
{
str[i + 1],
str[i + 2],
str[i + 3],
str[i + 4]
}), NumberStyles.HexNumber);
stringBuilder.Append((char)num2);
i += 4;
break;
}
case 'r':
stringBuilder.Append('\r');
break;
case 'n':
stringBuilder.Append('\n');
break;
}
}
flag = false;
continue;
}
switch (c)
{
case '\\':
flag = true;
break;
case '=':
if (num > 1)
{
return null;
}
array[num++] = stringBuilder.ToString();
stringBuilder.Length = 0;
break;
case '%':
if (i + 2 < length && i + 1 < length && i + 2 < length && str[i + 1] == '3' && str[i + 2] == 'D')
{
stringBuilder.Append('=');
i += 2;
}
else
{
stringBuilder.Append(c);
}
break;
case '/':
{
int num3 = i + 1;
if (num3 < length && num3 < length && str[num3] == '/')
{
array[num++] = stringBuilder.ToString();
if (num == 2)
{
return array;
}
return null;
}
stringBuilder.Append(c);
break;
}
default:
stringBuilder.Append(c);
break;
}
}
if (num != 1)
{
return null;
}
array[num++] = stringBuilder.ToString();
return array;
}
internal static string EscapeNewlines(string str)
{
if (str == null || str.Length == 0)
{
return "";
}
int length = str.Length;
StringBuilder stringBuilder = new StringBuilder(length + 4);
int num = 0;
while (num < length)
{
char c = str[num];
char c2 = c;
if (c2 <= '\r')
{
switch (c2)
{
default:
stringBuilder.Append(c);
num++;
continue;
case '\r':
stringBuilder.Append("\\r");
break;
case '\n':
stringBuilder.Append("\\n");
break;
}
}
else
{
switch (c2)
{
default:
stringBuilder.Append(c);
num++;
continue;
case '\\':
stringBuilder.Append('\\');
stringBuilder.Append(c);
break;
case '=':
stringBuilder.Append('\\');
stringBuilder.Append(c);
break;
case '/':
{
int num2 = num + 1;
if (num2 < length && str[num2] == '/')
{
stringBuilder.Append('\\');
stringBuilder.Append(c);
stringBuilder.Append('\\');
stringBuilder.Append(c);
num++;
}
else
{
stringBuilder.Append(c);
}
break;
}
}
}
num++;
}
return stringBuilder.ToString();
}
}
internal class TextTranslate
{
private static readonly Dictionary<string, DateTime> _debugOutputCache = new Dictionary<string, DateTime>();
private static readonly TimeSpan _debugOutputInterval = TimeSpan.FromSeconds(10.0);
private static readonly TimeSpan _cacheCleanupInterval = TimeSpan.FromMinutes(5.0);
private static DateTime _lastCleanupTime = DateTime.Now;
public static TextTranslate Instance = new TextTranslate();
public static long ChangeTime = 0L;
public static bool ShouldOutputDebug(string text)
{
if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value)
{
return true;
}
DateTime now = DateTime.Now;
if (now - _lastCleanupTime > _cacheCleanupInterval)
{
CleanupDebugCache();
_lastCleanupTime = now;
}
if (_debugOutputCache.TryGetValue(text, out var value) && now - value < _debugOutputInterval)
{
return false;
}
_debugOutputCache[text] = now;
return true;
}
private static void CleanupDebugCache()
{
if (!TranslatePlugin.showOtherDebug.Value && !TranslatePlugin.showAvailableText.Value)
{
_debugOutputCache.Clear();
return;
}
DateTime now = DateTime.Now;
List<string> list = new List<string>();
foreach (KeyValuePair<string, DateTime> item in _debugOutputCache)
{
if (now - item.Value > _cacheCleanupInterval)
{
list.Add(item.Key);
}
}
foreach (string item2 in list)
{
_debugOutputCache.Remove(item2);
}
if (TranslatePlugin.showOtherDebug.Value && list.Count > 0)
{
TranslatePlugin.logger.LogInfo((object)$"[Debug] Cleaned up {list.Count} old debug cache entries");
}
}
internal void Hook_TextChanged(object ui)
{
if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value)
{
try
{
Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator");
if (type != null)
{
FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public);
if (field != null && field.GetValue(null) is HashSet<object> hashSet && hashSet.Contains(ui))
{
return;
}
}
}
catch
{
}
}
TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo();
bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo);
if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value)
{
string text = ui.GetText(orCreateTextTranslationInfo);
string text2 = TranslateOrQueue(ui, text, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState);
if (!string.IsNullOrEmpty(text2) && !text2.Equals(text) && IsUIObjectValid(ui))
{
SetText(ui, text2, isTranslated: true, text, orCreateTextTranslationInfo);
}
}
}
internal void Hook_TextChanged(object ui, ref string value)
{
if (TranslatePlugin.enableTerminalPatch != null && TranslatePlugin.enableTerminalPatch.Value)
{
try
{
Type type = Type.GetType("GameTranslator.Patches.TerminalPatch, GameTranslator");
if (type != null)
{
FieldInfo field = type.GetField("ig", BindingFlags.Static | BindingFlags.Public);
if (field != null && field.GetValue(null) is HashSet<object> hashSet && hashSet.Contains(ui))
{
return;
}
}
}
catch
{
}
}
TextTranslationInfo orCreateTextTranslationInfo = ui.GetOrCreateTextTranslationInfo();
bool ignoreComponentState = DiscoverComponent(ui, orCreateTextTranslationInfo);
if (TranslatePlugin.shouldTranslateSpecialText.Value || TranslatePlugin.shouldTranslateNormalText.Value)
{
string text = TranslateOrQueue(ui, value, orCreateTextTranslationInfo, TranslateConfig.normalText, TranslateConfig.text, ignoreComponentState);
if (!string.IsNullOrEmpty(text) && !text.Equals(value) && IsUIObjectValid(ui))
{
value = text;
}
}
}
public string TranslateOrQueue(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState)
{
if (info != null && (info.IsCurrentlySettingText || info.MustIgnore))
{
return null;
}
text = text ?? ui.GetText(info);
if (Utility.IsNullOrWhiteSpace(text))
{
return null;
}
if (info != null && info.IsTranslated)
{
if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime)
{
info.Reset(text);
}
else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text))
{
return info.TranslatedText;
}
}
string cachedTranslation = AsyncTranslationManager.Instance.GetCachedTranslation(text, config);
if (cachedTranslation != null)
{
if (TranslatePlugin.showOtherDebug.Value && ShouldOutputDebug("cached:" + text))
{
TranslatePlugin.logger.LogInfo((object)("[Debug] Cached translation found for: '" + text + "' -> '" + cachedTranslation + "'"));
}
if (info != null)
{
info.OriginalText = text;
info.SetTranslatedText(cachedTranslation);
}
if (!IsUIObjectValid(ui))
{
return null;
}
try
{
if (info != null)
{
info.IsCurrentlySettingText = true;
}
ui.SetText(cachedTranslation, info);
}
catch (NullReferenceException)
{
}
catch (IndexOutOfRangeException ex2)
{
TranslatePlugin.logger.LogError((object)("IndexOutOfRangeException in cached translation: " + ex2.Message));
}
catch (Exception ex3)
{
TranslatePlugin.logger.LogError((object)("Exception in cached translation: " + ex3.Message));
}
finally
{
if (info != null)
{
info.IsCurrentlySettingText = false;
}
}
return cachedTranslation;
}
if (normalText == null || normalText.IsTranslatable(text, isToken: false))
{
if (text.Length <= TranslatePlugin.syncTranslationThreshold.Value)
{
string text2 = TranslateImmediate(ui, text, info, normalText, config, ignoreComponentState);
if (text2 != null)
{
AsyncTranslationManager.Instance.GetCachedTranslation(text, config);
return text2;
}
}
else
{
if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("queued:" + text))
{
TranslatePlugin.logger.LogInfo((object)("[Debug] Queued available text: '" + text + "'"));
}
AsyncTranslationManager.Instance.QueueTranslation(ui, text, info, normalText, config, ignoreComponentState);
if (info != null && info.IsTranslated && info.TranslatedText != null)
{
return info.TranslatedText;
}
}
}
return null;
}
public string TranslateImmediate(object ui, string text, TextTranslationInfo info, NormalTextTranslator normalText, TranslateConfig.TranslateConfigFile config, bool ignoreComponentState)
{
if (info != null && (info.IsCurrentlySettingText || info.MustIgnore))
{
return null;
}
text = text ?? ui.GetText(info);
if (Utility.IsNullOrWhiteSpace(text))
{
return null;
}
if (info != null && info.IsTranslated)
{
if (!info.OriginalText.Equals(text) || info.ChangeTime != ChangeTime)
{
info.Reset(text);
}
else if (info.OriginalText.Equals(text) || info.TranslatedText.Equals(text))
{
return info.TranslatedText;
}
}
string text2 = null;
if ((normalText == null || normalText.IsTranslatable(text, isToken: false)) && (ignoreComponentState || ui.IsComponentActive()))
{
if (normalText != null && TranslatePlugin.shouldTranslateNormalText.Value)
{
if (TranslatePlugin.showAvailableText.Value && ShouldOutputDebug("available:" + text))
{
TranslatePlugin.logger.LogInfo((object)("[Debug] Found available text: '" + text + "'"));
}
text2 = normalText.TryTranslate(text);
}
if (text2 != null)
{
text2 = TranslateConfig.replaceByMap(text2, config);
SetTranslatedText(ui, text2, text, info);
}
}
return text2;
}
internal void SetTranslatedText(object ui, string translatedText, string originalText, TextTranslationInfo info)
{
if (info != null)
{
info.OriginalText = originalText;
info.SetTranslatedText(translatedText);
}
if (!IsUIObject