using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Logging; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace ChatBoxPlus; [BepInPlugin("repo.chatboxplus", "ChatBoxPlus", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class ChatBoxPlusPlugin : BaseUnityPlugin { public const string PluginGuid = "repo.chatboxplus"; public const string PluginName = "ChatBoxPlus"; public const string PluginVersion = "1.0.2"; public const string ChatBoxGuid = "ChatBox-UnloadedHangar"; private const string TargetPatchMethodName = "ChatManagerTTSPatch"; private static ManualLogSource _log; private bool _restored; private bool _chatBoxExtensionsPatched; private void Awake() { _log = ((BaseUnityPlugin)this).Logger; RestoreTts("Awake"); PatchChatBoxExtensions("Awake"); } private void Start() { RestoreTts("Start"); PatchChatBoxExtensions("Start"); } private void RestoreTts(string phase) { if (_restored) { return; } try { Type type = FindType("HarmonyLib.Harmony"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"HarmonyLib.Harmony was not found; TTS patch removal will be retried later."); return; } MethodInfo method = type.GetMethod("GetAllPatchedMethods", BindingFlags.Static | BindingFlags.Public); MethodInfo method2 = type.GetMethod("GetPatchInfo", BindingFlags.Static | BindingFlags.Public); MethodInfo methodInfo = FindSpecificUnpatchMethod(type); if (method == null || method2 == null || methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Harmony API shape was not recognized; cannot remove ChatBox TTS patch."); return; } object harmonyInstance = Activator.CreateInstance(type, "repo.chatboxplus"); if (!(method.Invoke(null, null) is IEnumerable enumerable)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Harmony returned no patched methods list."); return; } int num = 0; foreach (object item in enumerable) { MethodBase methodBase = item as MethodBase; if (!(methodBase == null)) { object obj = method2.Invoke(null, new object[1] { methodBase }); if (obj != null) { num += RemoveMatchingPatches(harmonyInstance, methodInfo, methodBase, obj, "Prefixes"); num += RemoveMatchingPatches(harmonyInstance, methodInfo, methodBase, obj, "Postfixes"); num += RemoveMatchingPatches(harmonyInstance, methodInfo, methodBase, obj, "Transpilers"); num += RemoveMatchingPatches(harmonyInstance, methodInfo, methodBase, obj, "Finalizers"); num += RemoveMatchingPatches(harmonyInstance, methodInfo, methodBase, obj, "ILManipulators"); } } } if (num > 0) { _restored = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Removed " + num + " ChatBox TTS-disabling Harmony patch(es) during " + phase + ".")); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("No ChatBox TTS-disabling Harmony patches were found during " + phase + ".")); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to restore ChatBox TTS during " + phase + ": " + ex)); } } private void PatchChatBoxExtensions(string phase) { if (_chatBoxExtensionsPatched) { return; } try { Type type = FindType("HarmonyLib.Harmony"); Type type2 = FindType("HarmonyLib.HarmonyMethod"); Type type3 = FindType("ChatBox.NonPatch.NonPatches"); if (type == null || type2 == null || type3 == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"ChatBox/Harmony types were not found; ChatBoxPlus patches will be retried later."); return; } MethodInfo methodInfo = FindHarmonyPatchMethod(type); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Harmony.Patch API shape was not recognized; cannot apply ChatBoxPlus patches."); return; } object harmonyInstance = Activator.CreateInstance(type, "repo.chatboxplus"); MethodInfo method = type3.GetMethod("BridgedMessage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method2 = type3.GetMethod("AddChatMessageSystem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null || method2 == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"ChatBox methods were not found; cannot apply ChatBoxPlus patches."); return; } PatchWithPrefix(harmonyInstance, methodInfo, type2, method, typeof(ChatBoxPlusPlugin).GetMethod("GhostOnlyBridgedMessagePrefix", BindingFlags.Static | BindingFlags.NonPublic)); PatchWithPrefix(harmonyInstance, methodInfo, type2, method2, typeof(ChatBoxPlusPlugin).GetMethod("SuppressOutOfRangeSystemMessagePrefix", BindingFlags.Static | BindingFlags.NonPublic)); _chatBoxExtensionsPatched = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied ChatBoxPlus chat visibility patches during " + phase + ".")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to apply ChatBoxPlus patches during " + phase + ": " + ex)); } } private static bool GhostOnlyBridgedMessagePrefix(string message, string playerHexColor, string playerName, bool dead) { if (!dead) { return true; } if (!IsLocalPlayerDead()) { return false; } try { AddChatBoxDeadMessage(message, playerHexColor, playerName); } catch (Exception ex) { LogWarning("Failed to show ghost-only ChatBox message: " + ex); } return false; } private static bool SuppressOutOfRangeSystemMessagePrefix(string chatMessage, string nameOfUserWhoTyped) { if (string.Equals(nameOfUserWhoTyped, "Unknown", StringComparison.OrdinalIgnoreCase) && chatMessage != null && chatMessage.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } return true; } private static void PatchWithPrefix(object harmonyInstance, MethodInfo harmonyPatchMethod, Type harmonyMethodType, MethodBase original, MethodInfo prefix) { object obj = Activator.CreateInstance(harmonyMethodType, prefix); ParameterInfo[] parameters = harmonyPatchMethod.GetParameters(); object[] array = new object[parameters.Length]; array[0] = original; array[1] = obj; harmonyPatchMethod.Invoke(harmonyInstance, array); } private static bool IsLocalPlayerDead() { try { Type type = FindType("SemiFunc"); if (type == null) { return false; } MethodInfo method = type.GetMethod("PlayerAvatarLocal", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return false; } object obj = method.Invoke(null, null); if (obj == null) { return false; } FieldInfo field = obj.GetType().GetField("deadSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return false; } object value = field.GetValue(obj); return value is bool && (bool)value; } catch (Exception ex) { LogWarning("Failed to read local player death state: " + ex); return false; } } private static void AddChatBoxDeadMessage(string message, string playerHexColor, string playerName) { Type type = FindType("ChatBox.NonPatch.NonPatches"); if (type == null) { return; } object chatBoxNonPatchesInstance = GetChatBoxNonPatchesInstance(type); if (chatBoxNonPatchesInstance != null) { MethodInfo method = type.GetMethod("AddChatMessageDead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method == null)) { method.Invoke(chatBoxNonPatchesInstance, new object[3] { message, playerHexColor, playerName }); } } } private static object GetChatBoxNonPatchesInstance(Type nonPatchesType) { PropertyInfo property = nonPatchesType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(null, null); } MethodInfo method = nonPatchesType.GetMethod("get_Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(method != null)) { return null; } return method.Invoke(null, null); } private static MethodInfo FindHarmonyPatchMethod(Type harmonyType) { MethodInfo[] methods = harmonyType.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "Patch")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length >= 5 && parameters[0].ParameterType == typeof(MethodBase) && parameters[1].ParameterType.FullName == "HarmonyLib.HarmonyMethod") { return methodInfo; } } } return null; } private static int RemoveMatchingPatches(object harmonyInstance, MethodInfo unpatchSpecificMethod, MethodBase original, object patchInfo, string collectionName) { object memberValue = GetMemberValue(patchInfo, collectionName); if (!(memberValue is IEnumerable enumerable)) { return 0; } List list = new List(); foreach (object item in enumerable) { MethodInfo methodInfo = GetMemberValue(item, "PatchMethod") as MethodInfo; if (IsChatBoxTtsPatch(methodInfo)) { list.Add(methodInfo); } } for (int i = 0; i < list.Count; i++) { MethodInfo methodInfo2 = list[i]; unpatchSpecificMethod.Invoke(harmonyInstance, new object[2] { original, methodInfo2 }); } return list.Count; } private static bool IsChatBoxTtsPatch(MethodInfo patchMethod) { if (patchMethod == null || patchMethod.DeclaringType == null) { return false; } if (patchMethod.Name == "ChatManagerTTSPatch") { return true; } Type declaringType = patchMethod.DeclaringType; string text = declaringType.FullName ?? string.Empty; string text2 = declaringType.Name ?? string.Empty; if (!(text2 == "ChatManagerTTSPatch")) { return text.EndsWith(".ChatManagerTTSPatch", StringComparison.Ordinal); } return true; } private static MethodInfo FindSpecificUnpatchMethod(Type harmonyType) { MethodInfo[] methods = harmonyType.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "Unpatch")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(MethodBase) && parameters[1].ParameterType == typeof(MethodInfo)) { return methodInfo; } } } return null; } private static object GetMemberValue(object instance, string memberName) { Type type = instance.GetType(); PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(instance, null); } FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } return null; } private static Type FindType(string fullTypeName) { Type type = Type.GetType(fullTypeName + ", 0Harmony", throwOnError: false); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { type = assemblies[i].GetType(fullTypeName, throwOnError: false); if (type != null) { return type; } } return null; } private static void LogWarning(string message) { if (_log != null) { _log.LogWarning((object)message); } } }