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);
}
}
}