using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BepInEx.ConsoleUtil;
using UnityEngine;
namespace BepInEx.Logging
{
///
/// Logs entries using Unity specific outputs.
///
public class UnityLogWriter : BaseLogger
{
private delegate void WriteStringToUnityLogDelegate(string s);
private static readonly WriteStringToUnityLogDelegate WriteStringToUnityLog;
[DllImport("mono.dll", EntryPoint = "mono_lookup_internal_call")]
private static extern IntPtr MonoLookupInternalCall(IntPtr gconstpointer);
static UnityLogWriter()
{
foreach (MethodInfo methodInfo in typeof(UnityEngine.UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
{
if (MonoLookupInternalCall(methodInfo.MethodHandle.Value) == IntPtr.Zero)
continue;
WriteStringToUnityLog = (WriteStringToUnityLogDelegate) Delegate.CreateDelegate(typeof(WriteStringToUnityLogDelegate), methodInfo);
break;
}
}
///
/// Writes a string specifically to the game output log.
///
/// The value to write.
public void WriteToLog(string value)
{
WriteStringToUnityLog?.Invoke(value);
}
protected void InternalWrite(string value)
{
Console.Write(value);
WriteToLog(value);
}
///
/// Logs an entry to the Logger instance.
///
/// The level of the entry.
/// The textual value of the entry.
public override void Log(LogLevel level, object entry)
{
Kon.ForegroundColor = level.GetConsoleColor();
base.Log(level, entry);
Kon.ForegroundColor = ConsoleColor.Gray;
// If the display level got ignored, still write it to the log
if ((DisplayedLevels & level) == LogLevel.None)
WriteToLog($"[{level.GetHighestLevel()}] {entry}\r\n");
}
public override void WriteLine(string value) => InternalWrite($"{value}\r\n");
public override void Write(char value) => InternalWrite(value.ToString());
public override void Write(string value) => InternalWrite(value);
///
/// Start listening to Unity's log message events and sending the messages to BepInEx logger.
///
public static void ListenUnityLogs()
{
Type application = typeof(Application);
EventInfo logEvent = application.GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
if (logEvent != null)
{
logEvent.AddEventHandler(null, new Application.LogCallback(OnUnityLogMessageReceived));
}
else
{
MethodInfo registerLogCallback = application.GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
registerLogCallback.Invoke(null, new object[] { new Application.LogCallback(OnUnityLogMessageReceived) });
}
}
private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
{
LogLevel logLevel = LogLevel.Message;
switch (type)
{
case LogType.Error:
case LogType.Assert:
case LogType.Exception:
logLevel = LogLevel.Error;
break;
case LogType.Warning:
logLevel = LogLevel.Warning;
break;
case LogType.Log:
default:
logLevel = LogLevel.Info;
break;
}
Logger.Log(logLevel, message);
if (type == LogType.Exception)
{
Logger.Log(logLevel, $"Stack trace:\n{stackTrace}");
}
}
}
}
namespace UnityEngine
{
internal sealed class UnityLogWriter
{
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void WriteStringToUnityLogImpl(string s);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void WriteStringToUnityLog(string s);
}
}