diff --git a/BuildTools b/BuildTools index e0cefce0..de919c49 160000 --- a/BuildTools +++ b/BuildTools @@ -1 +1 @@ -Subproject commit e0cefce0a1bb1aacc0afdbf305df1fa65da55464 +Subproject commit de919c49496a7e7e11382bd876473018ddceaa1a diff --git a/IPA.Injector/IPA.Injector.csproj b/IPA.Injector/IPA.Injector.csproj index c088d9f0..14e11358 100644 --- a/IPA.Injector/IPA.Injector.csproj +++ b/IPA.Injector/IPA.Injector.csproj @@ -56,7 +56,6 @@ - diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs index d883e16e..fae10e9f 100644 --- a/IPA.Injector/Injector.cs +++ b/IPA.Injector/Injector.cs @@ -35,14 +35,14 @@ namespace IPA.Injector SetupLibraryLoading(); - EnsureUserData(); + EnsureDirectories(); // this is weird, but it prevents Mono from having issues loading the type. // IMPORTANT: NO CALLS TO ANY LOGGER CAN HAPPEN BEFORE THIS var unused = StandardLogger.PrintFilter; #region // Above hack explaination /* - * Due to an unknown bug in the version of Mono that Unity 2018.1.8 uses, if the first access to StandardLogger + * Due to an unknown bug in the version of Mono that Unity uses, if the first access to StandardLogger * is a call to a constructor, then Mono fails to load the type correctly. However, if the first access is to * the above static property (or maybe any, but I don't really know) it behaves as expected and works fine. */ @@ -66,11 +66,13 @@ namespace IPA.Injector } } - private static void EnsureUserData() + private static void EnsureDirectories() { string path; if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "UserData"))) Directory.CreateDirectory(path); + if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "Plugins"))) + Directory.CreateDirectory(path); } private static void SetupLibraryLoading() diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 91170d48..b839eea7 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -73,6 +73,8 @@ + + diff --git a/IPA.Injector/ConsoleWindow.cs b/IPA.Loader/Logging/ConsoleWindow.cs similarity index 81% rename from IPA.Injector/ConsoleWindow.cs rename to IPA.Loader/Logging/ConsoleWindow.cs index a44e8d24..f8e655ae 100644 --- a/IPA.Injector/ConsoleWindow.cs +++ b/IPA.Loader/Logging/ConsoleWindow.cs @@ -3,11 +3,22 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -namespace IPA.Injector +namespace IPA.Logging { // https://stackoverflow.com/a/48864902/3117125 internal static class WinConsole { + internal static TextWriter ConOut; + internal static TextReader ConIn; + + private static SafeFileHandle outHandle; + private static SafeFileHandle inHandle; + + internal static IntPtr OutHandle => outHandle.DangerousGetHandle(); + internal static IntPtr InHandle => inHandle.DangerousGetHandle(); + + internal static bool IsInitialized; + public static void Initialize(bool alwaysCreateNewConsole = true) { bool consoleAttached = true; @@ -21,6 +32,7 @@ namespace IPA.Injector if (consoleAttached) { InitializeStreams(); + IsInitialized = true; } } @@ -32,10 +44,11 @@ namespace IPA.Injector private static void InitializeOutStream() { - var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write); + var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write, out outHandle); if (fs != null) { var writer = new StreamWriter(fs) { AutoFlush = true }; + ConOut = writer; Console.SetOut(writer); Console.SetError(writer); } @@ -43,22 +56,25 @@ namespace IPA.Injector private static void InitializeInStream() { - var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read); + var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read, out inHandle); if (fs != null) { - Console.SetIn(new StreamReader(fs)); + Console.SetIn(ConIn = new StreamReader(fs)); } } private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, - FileAccess dotNetFileAccess) + FileAccess dotNetFileAccess, out SafeFileHandle handle) { var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true); if (!file.IsInvalid) { + handle = file; var fs = new FileStream(file, dotNetFileAccess); return fs; } + + handle = null; return null; } diff --git a/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs b/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs index 39e6b22f..2cab7277 100644 --- a/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs +++ b/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace IPA.Logging.Printers { @@ -7,7 +8,7 @@ namespace IPA.Logging.Printers /// public class ColoredConsolePrinter : LogPrinter { - Logger.LogLevel filter = Logger.LogLevel.All; + private Logger.LogLevel filter = Logger.LogLevel.All; /// /// A filter for this specific printer. @@ -19,7 +20,7 @@ namespace IPA.Logging.Printers public ConsoleColor Color { get; set; } = Console.ForegroundColor; /// - /// Prints an entry to the associated file. + /// Prints an entry to the console window. /// /// the of the message /// the the message was recorded at @@ -28,10 +29,79 @@ namespace IPA.Logging.Printers public override void Print(Logger.Level level, DateTime time, string logName, string message) { if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; - Console.ForegroundColor = Color; + EnsureDefaultsPopulated(WinConsole.OutHandle); + SetColor(Color, WinConsole.OutHandle); foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) - Console.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()); - Console.ResetColor(); + WinConsole.ConOut.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()); + ResetColor(WinConsole.OutHandle); } + + private static bool _haveReadDefaultColors; + private static short _defaultColors; + + private void EnsureDefaultsPopulated(IntPtr handle, bool force = false) + { + if (!_haveReadDefaultColors | force) + { + GetConsoleScreenBufferInfo(handle, out var info); + _defaultColors = (short)(info.Attribute & ~15); + _haveReadDefaultColors = true; + } + } + + private void ResetColor(IntPtr handle) + { + GetConsoleScreenBufferInfo(handle, out var info); + var otherAttrs = (short)(info.Attribute & ~15); + SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors)); + } + + private void SetColor(ConsoleColor col, IntPtr handle) + { + GetConsoleScreenBufferInfo(handle, out var info); + var attr = GetAttrForeground(info.Attribute, col); + SetConsoleTextAttribute(handle, attr); + } + + private static short GetAttrForeground(int attr, ConsoleColor color) + { + attr &= ~15; + return (short)(attr | (int)color); + } + + + // ReSharper disable NotAccessedField.Local +#pragma warning disable 649 + private struct Coordinate + { + public short X; + public short Y; + } + + private struct SmallRect + { + public short Left; + public short Top; + public short Right; + public short Bottom; + } + + private struct ConsoleScreenBufferInfo + { + public Coordinate Size; + public Coordinate CursorPosition; + public short Attribute; + public SmallRect Window; + public Coordinate MaxWindowSize; + } + #pragma warning restore 649 + // ReSharper restore NotAccessedField.Local + + + [DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool GetConsoleScreenBufferInfo(IntPtr handle, out ConsoleScreenBufferInfo info); + + [DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool SetConsoleTextAttribute(IntPtr handle, short attribute); } } diff --git a/IPA.Loader/Logging/Printers/ColorlessConsolePrinter.cs b/IPA.Loader/Logging/Printers/ColorlessConsolePrinter.cs new file mode 100644 index 00000000..754b75ed --- /dev/null +++ b/IPA.Loader/Logging/Printers/ColorlessConsolePrinter.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IPA.Logging.Printers +{ + /// + /// A colorless version of , that indiscriminantly prints to standard out. + /// + public class ColorlessConsolePrinter : LogPrinter + { + /// + /// A filter for this specific printer. + /// + public override Logger.LogLevel Filter { get; set; } + + /// + /// Prints an entry to standard out. + /// + /// the of the message + /// the the message was recorded at + /// the name of the log that sent the message + /// the message to print + public override void Print(Logger.Level level, DateTime time, string logName, string message) + { + if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; + foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + Console.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()); + } + } +} diff --git a/IPA.Loader/Logging/StandardLogger.cs b/IPA.Loader/Logging/StandardLogger.cs index 83ff7dd3..197c3ac9 100644 --- a/IPA.Loader/Logging/StandardLogger.cs +++ b/IPA.Loader/Logging/StandardLogger.cs @@ -24,39 +24,57 @@ namespace IPA.Logging { private static readonly List defaultPrinters = new List() { - new ColoredConsolePrinter() - { - Filter = LogLevel.DebugOnly, - Color = ConsoleColor.Green, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.InfoOnly, - Color = ConsoleColor.White, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.NoticeOnly, - Color = ConsoleColor.Cyan - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.WarningOnly, - Color = ConsoleColor.Yellow, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.ErrorOnly, - Color = ConsoleColor.Red, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.CriticalOnly, - Color = ConsoleColor.Magenta, - }, new GlobalLogFilePrinter() }; + static StandardLogger() + { + ConsoleColorSupport(); + } + + private static bool addedConsolePrinters; + private static bool finalizedDefaultPrinters; + internal static void ConsoleColorSupport() + { + if (!addedConsolePrinters && !finalizedDefaultPrinters && WinConsole.IsInitialized ) + { + defaultPrinters.AddRange(new [] + { + new ColoredConsolePrinter() + { + Filter = LogLevel.DebugOnly, + Color = ConsoleColor.Green, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.InfoOnly, + Color = ConsoleColor.White, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.NoticeOnly, + Color = ConsoleColor.Cyan + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.WarningOnly, + Color = ConsoleColor.Yellow, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.ErrorOnly, + Color = ConsoleColor.Red, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.CriticalOnly, + Color = ConsoleColor.Magenta, + } + }); + addedConsolePrinters = true; + } + } + /// /// The for writing directly to the console window, or stdout if no window open. /// @@ -112,6 +130,15 @@ namespace IPA.Logging internal StandardLogger(string name) { + ConsoleColorSupport(); + if (!finalizedDefaultPrinters) + { + if (!addedConsolePrinters) + AddDefaultPrinter(new ColorlessConsolePrinter()); + + finalizedDefaultPrinters = true; + } + logName = name; printers.Add(new PluginLogFilePrinter(name)); diff --git a/IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache b/IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache index 0e65ef0f..d16fba0d 100644 --- a/IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache +++ b/IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -0a3222410c8b7f878f3aac05d926ef18ba74d12a +5d76d76cc5c14257f2b9071c928c27b3edd80cc0 diff --git a/Refs/UnityEngine.CoreModule.dll b/Refs/UnityEngine.CoreModule.dll index ec2d2901..f6478af7 100644 Binary files a/Refs/UnityEngine.CoreModule.dll and b/Refs/UnityEngine.CoreModule.dll differ