Decompiled source of LogNeuter v1.0.3

plugins/LogNeuter.dll

Decompiled 10 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LogNeuter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LogNeuter")]
[assembly: AssemblyCopyright("Copyright © BlueAmulet 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("de27b4d1-820d-4505-a953-6001420281e4")]
[assembly: AssemblyFileVersion("1.0.3")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.3.0")]
namespace LogNeuter;

[BepInPlugin("BlueAmulet.LogNeuter", "LogNeuter", "1.0.3")]
public class LogNeuter : BaseUnityPlugin
{
	internal const string Name = "LogNeuter";

	internal const string Author = "BlueAmulet";

	internal const string Version = "1.0.3";

	private const string ID = "BlueAmulet.LogNeuter";

	private static ManualLogSource Log;

	private static ConfigFile ConfigFile;

	private const int ConfVersion = 1;

	private static ConfigEntry<int> version;

	private static ConfigEntry<bool> fixSpatializer;

	private static ConfigEntry<bool> fixLookRotation;

	private static ConfigEntry<bool> genBlockAll;

	private static bool allowSave = false;

	private static bool warnSave = true;

	private static readonly Harmony harmony = new Harmony("BlueAmulet.LogNeuter");

	private static readonly HarmonyMethod transpiler = new HarmonyMethod(AccessTools.Method(typeof(LogNeuter), "Transpiler", (Type[])null, (Type[])null));

	private static readonly Dictionary<string, HashSet<string>> staticLogs = new Dictionary<string, HashSet<string>>();

	private static readonly Dictionary<string, List<Regex>> dynamicLogs = new Dictionary<string, List<Regex>>();

	private static StreamWriter genFile;

	public void Awake()
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Expected O, but got Unknown
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Expected O, but got Unknown
		//IL_0372: Unknown result type (might be due to invalid IL or missing references)
		//IL_0379: Expected O, but got Unknown
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Expected O, but got Unknown
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0275: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		ConfigFile = ((BaseUnityPlugin)this).Config;
		SceneManager.sceneLoaded += OnSceneLoaded;
		MethodBase methodBase = AccessTools.Method(typeof(ConfigFile), "Save", (Type[])null, (Type[])null);
		HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(LogNeuter), "PrefixConfigSave", (Type[])null, (Type[])null));
		harmony.Patch(methodBase, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		if (AccessTools.DeclaredPropertyGetter(typeof(ConfigFile), "OrphanedEntries")?.Invoke(((BaseUnityPlugin)this).Config, null) is Dictionary<ConfigDefinition, string> dictionary)
		{
			ConfigDefinition key = new ConfigDefinition("Config", "Version");
			if (dictionary.Count != 0 && !dictionary.ContainsKey(key))
			{
				dictionary[key] = "0";
			}
		}
		warnSave = false;
		version = ((BaseUnityPlugin)this).Config.Bind<int>("Config", "Version", 1, "Disable broken spatialize on audio sources");
		fixSpatializer = ((BaseUnityPlugin)this).Config.Bind<bool>("Config", "FixSpatializer", true, "Disable broken spatialize on audio sources");
		fixLookRotation = ((BaseUnityPlugin)this).Config.Bind<bool>("Config", "FixLookRotation", true, "Mask \"Look rotation viewing vector is zero\" messages");
		genBlockAll = ((BaseUnityPlugin)this).Config.Bind<bool>("Config", "GenBlockAll", false, "Generate a config file that blocks all logging");
		warnSave = true;
		if (version.Value < 1)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"Configuration out of date, expected version {1}, got {version.Value}");
		}
		else if (version.Value > 1)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)$"Configuration too new, expected version {1}, got {version.Value}");
		}
		if (genBlockAll.Value)
		{
			Assembly assembly = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), (Assembly a) => a.GetName().Name == "Assembly-CSharp");
			if (assembly != null)
			{
				genFile = new StreamWriter(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), "BlueAmulet.LogNeuter.Generated.cfg"), append: false, Encoding.UTF8);
				genFile.WriteLine("## This is a generated file, it does not actively do anything and only serves as reference for the real config file");
				Harmony val2 = new Harmony("BlueAmulet.LogNeuter.Generated");
				HarmonyMethod val3 = new HarmonyMethod(AccessTools.Method(typeof(LogNeuter), "TranspilerGen", (Type[])null, (Type[])null));
				Type[] types = assembly.GetTypes();
				foreach (Type type in types)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Scanning " + type.FullName));
					MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo methodInfo in methods)
					{
						try
						{
							val2.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null, (HarmonyMethod)null);
							val2.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)3, "BlueAmulet.LogNeuter.Generated");
						}
						catch (HarmonyException)
						{
						}
					}
				}
				genFile.Close();
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not find Assembly-CSharp, generation skipped");
			}
		}
		if (fixLookRotation.Value)
		{
			MethodBase methodBase2 = AccessTools.Method(typeof(Quaternion), "LookRotation", new Type[2]
			{
				typeof(Vector3),
				typeof(Vector3)
			}, (Type[])null);
			HarmonyMethod val5 = new HarmonyMethod(AccessTools.Method(typeof(LogNeuter), "TranspilerLookRotation", (Type[])null, (Type[])null));
			harmony.Patch(methodBase2, (HarmonyMethod)null, (HarmonyMethod)null, val5, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		if (File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
		{
			MethodBase methodBase3 = null;
			string text = null;
			foreach (string item in File.ReadLines(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				string text2 = item.Trim();
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					PatchMethod(methodBase3);
					methodBase3 = null;
					staticLogs.Clear();
					text = text2.Substring(1, text2.Length - 2);
					if (!text.Contains("|"))
					{
						continue;
					}
					string[] array = text.Split(new char[1] { '|' }, 2);
					if (array.Length == 2)
					{
						string text3 = array[0];
						if (!text3.Contains(","))
						{
							text3 += ", Assembly-CSharp";
						}
						Type type2 = Type.GetType(text3);
						if (type2 == null)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find type: " + array[0]));
							continue;
						}
						try
						{
							methodBase3 = AccessTools.Method(type2, array[1], (Type[])null, (Type[])null);
							if (methodBase3 == null)
							{
								((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find method: " + text));
							}
						}
						catch (AmbiguousMatchException ex)
						{
							methodBase3 = null;
							((BaseUnityPlugin)this).Logger.LogError((object)ex);
						}
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Unknown entry: " + text));
					}
				}
				else
				{
					if (text == null)
					{
						continue;
					}
					if (text2.StartsWith("^") && text2.EndsWith("$"))
					{
						if (!dynamicLogs.ContainsKey(text))
						{
							dynamicLogs[text] = new List<Regex>();
						}
						dynamicLogs[text].Add(new Regex(text2, RegexOptions.Compiled));
					}
					else
					{
						if (!staticLogs.ContainsKey(text))
						{
							staticLogs[text] = new HashSet<string>();
						}
						staticLogs[text].Add(text2);
					}
				}
			}
			PatchMethod(methodBase3);
		}
		else
		{
			allowSave = true;
			((BaseUnityPlugin)this).Config.Save();
			allowSave = false;
		}
		int num = 0;
		foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched " + patchedMethod.DeclaringType.Name + "." + patchedMethod.Name));
			num++;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)(num + " patches applied"));
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		if (!fixSpatializer.Value)
		{
			return;
		}
		int num = 0;
		AudioSource[] array = Resources.FindObjectsOfTypeAll<AudioSource>();
		foreach (AudioSource val in array)
		{
			if (val.spatialize)
			{
				val.spatialize = false;
				num++;
			}
		}
		if (num > 0)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Fixed {num} spatilization settings");
		}
	}

	private static bool PrefixConfigSave(ref ConfigFile __instance)
	{
		if (__instance == ConfigFile && !allowSave)
		{
			if (warnSave)
			{
				Log.LogWarning((object)"Saving LogNeuter config blocked for data loss prevention");
			}
			return false;
		}
		return true;
	}

	private static IEnumerable<CodeInstruction> TranspilerLookRotation(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Expected O, but got Unknown
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		Label label = generator.DefineLabel();
		list[0].labels.Add(label);
		list.InsertRange(0, new List<CodeInstruction>
		{
			new CodeInstruction(OpCodes.Ldarg_0, (object)null),
			new CodeInstruction(OpCodes.Call, (object)AccessTools.PropertyGetter(typeof(Vector3), "zero")),
			new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Vector3), "op_Equality", (Type[])null, (Type[])null)),
			new CodeInstruction(OpCodes.Brfalse_S, (object)label),
			new CodeInstruction(OpCodes.Call, (object)AccessTools.PropertyGetter(typeof(Quaternion), "identity")),
			new CodeInstruction(OpCodes.Ret, (object)null)
		});
		return list;
	}

	private static void PatchMethod(MethodBase patchMethod)
	{
		//IL_0043: Expected O, but got Unknown
		if (!(patchMethod != null))
		{
			return;
		}
		string key = SectionName(patchMethod);
		if (staticLogs.ContainsKey(key) || dynamicLogs.ContainsKey(key))
		{
			try
			{
				harmony.Patch(patchMethod, (HarmonyMethod)null, (HarmonyMethod)null, transpiler, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (HarmonyException val)
			{
				HarmonyException val2 = val;
				Log.LogError((object)val2);
			}
		}
	}

	private static string SectionName(MethodBase method)
	{
		Type declaringType = method.DeclaringType;
		string name = declaringType.Assembly.GetName().Name;
		if (name == "Assembly-CSharp")
		{
			return declaringType.FullName + "|" + method.Name;
		}
		return declaringType.FullName + ", " + name + "|" + method.Name;
	}

	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Expected O, but got Unknown
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Expected O, but got Unknown
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Expected O, but got Unknown
		string text = SectionName(original);
		bool flag = false;
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		for (int i = 1; i < list.Count; i++)
		{
			CodeInstruction val = list[i];
			if (!(val.opcode == OpCodes.Call) || !(val.operand is MethodInfo methodInfo) || !methodInfo.IsStatic || !(methodInfo.DeclaringType == typeof(Debug)) || !methodInfo.Name.StartsWith("Log") || !(methodInfo.Name != "LogException"))
			{
				continue;
			}
			CodeInstruction val2 = list[i - 1];
			if (val2.opcode == OpCodes.Ldstr)
			{
				if (staticLogs.ContainsKey(text) && staticLogs[text].Contains((string)val2.operand))
				{
					list[i] = new CodeInstruction(OpCodes.Pop, (object)null);
					flag = true;
				}
			}
			else if (dynamicLogs.ContainsKey(text))
			{
				string text2 = methodInfo.Name + methodInfo.GetParameters().Length;
				MethodInfo methodInfo2 = AccessTools.Method(typeof(LogNeuter), text2, (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
					list.Insert(i, new CodeInstruction(OpCodes.Ldstr, (object)text));
					flag = true;
				}
			}
		}
		if (!flag)
		{
			Log.LogWarning((object)("Made no changes to method: " + text));
		}
		return list;
	}

	private static void GenWriteEntry(ref bool wroteHeader, string section, HashSet<string> strings, string log)
	{
		if (!strings.Contains(log))
		{
			strings.Add(log);
			if (!wroteHeader)
			{
				genFile.WriteLine();
				genFile.WriteLine("[" + section + "]");
				wroteHeader = true;
			}
			if (!log.Contains("="))
			{
				genFile.WriteLine(log);
			}
		}
	}

	private static IEnumerable<CodeInstruction> TranspilerGen(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		Stream baseStream = genFile.BaseStream;
		if (baseStream != null && baseStream.CanWrite)
		{
			string section = SectionName(original);
			bool wroteHeader = false;
			HashSet<string> strings = new HashSet<string>();
			for (int i = 1; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (!(val.opcode == OpCodes.Call) || !(val.operand is MethodInfo methodInfo) || !methodInfo.IsStatic || !(methodInfo.DeclaringType == typeof(Debug)) || !methodInfo.Name.StartsWith("Log"))
				{
					continue;
				}
				CodeInstruction val2 = list[i - 1];
				if (val2.opcode == OpCodes.Ldstr)
				{
					GenWriteEntry(ref wroteHeader, section, strings, (string)val2.operand);
				}
				else
				{
					if (!(val2.opcode == OpCodes.Call) || !(val2.operand is MethodInfo methodInfo2) || !methodInfo2.IsStatic || !(methodInfo2.DeclaringType == typeof(string)) || !(methodInfo2.Name == "Format"))
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo2.GetParameters();
					if (parameters.Length == 0 || !(parameters[0].ParameterType == typeof(string)))
					{
						continue;
					}
					int num = i;
					do
					{
						num--;
					}
					while ((list[num].opcode != OpCodes.Ldstr || !((string)list[num].operand).Contains("{")) && num > 0);
					CodeInstruction val3 = list[num];
					if (val3.opcode == OpCodes.Ldstr)
					{
						string text = (string)val3.operand;
						if (text.Contains("{") && text.Contains("}"))
						{
							string text2 = Regex.Escape(text);
							text2 = text2.Replace("\\ ", " ");
							text2 = Regex.Replace(text2, "\\\\{[0-9]+}", ".*?");
							GenWriteEntry(ref wroteHeader, section, strings, "^" + text2 + "$");
						}
					}
				}
			}
		}
		return list;
	}

	private static bool MatchAny(string section, string log)
	{
		if (section == null || !dynamicLogs.ContainsKey(section))
		{
			return false;
		}
		foreach (Regex item in dynamicLogs[section])
		{
			if (item.IsMatch(log))
			{
				return true;
			}
		}
		return false;
	}

	public static void Log1(object message, string section)
	{
		if (!MatchAny(section, message.ToString()))
		{
			Debug.Log(message);
		}
	}

	public static void LogWarning1(object message, string section)
	{
		if (!MatchAny(section, message.ToString()))
		{
			Debug.LogWarning(message);
		}
	}

	public static void LogWarning2(object message, Object context, string section)
	{
		if (!MatchAny(section, message.ToString()))
		{
			Debug.LogWarning(message, context);
		}
	}

	public static void LogError1(object message, string section)
	{
		if (!MatchAny(section, message.ToString()))
		{
			Debug.LogError(message);
		}
	}

	public static void LogError2(object message, Object context, string section)
	{
		if (!MatchAny(section, message.ToString()))
		{
			Debug.LogError(message, context);
		}
	}
}