Decompiled source of ChatBoxPlus v1.0.2

plugins\ChatBoxPlus.dll

Decompiled a day ago
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("<decoding failed>", 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<MethodInfo> list = new List<MethodInfo>();
		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);
		}
	}
}