Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimConsoleCapture v1.0.0
ValheimConsoleCapture.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimConsoleCapture")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3c0ba889c8abd636a53a4096f301836f6c3c1582")] [assembly: AssemblyProduct("ValheimConsoleCapture")] [assembly: AssemblyTitle("ValheimConsoleCapture")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimConsoleCapture { internal static class ConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Copy; public static ConsoleEvent <1>__Dump; public static ConsoleEvent <2>__DumpCommand; public static ConsoleEvent <3>__Clear; public static ConsoleEvent <4>__Capture; public static ConsoleOptionsFetcher <5>__Options; } private const char ChainSeparator = '\u001e'; internal static void ProtectChain(ref string text) { if (string.IsNullOrEmpty(text)) { return; } int i; for (i = 0; i < text.Length && char.IsWhiteSpace(text[i]); i++) { } if (text.Length - i < "dumpcommand".Length || !string.Equals(text.Substring(i, "dumpcommand".Length), "dumpcommand", StringComparison.OrdinalIgnoreCase)) { return; } int num = i + "dumpcommand".Length; if (num < text.Length && !char.IsWhiteSpace(text[num])) { return; } int num2 = text.IndexOf('"', num); int num3 = text.LastIndexOf('"'); if (num2 < 0 || num3 <= num2) { return; } char[] array = text.ToCharArray(); for (int j = num2 + 1; j < num3; j++) { if (array[j] == ';') { array[j] = '\u001e'; } } text = new string(array); } internal static void Register() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown object obj = <>O.<0>__Copy; if (obj == null) { ConsoleEvent val = Copy; <>O.<0>__Copy = val; obj = (object)val; } new ConsoleCommand("copyconsole", "Copy buffered F5 lines to the clipboard. Optional count: copyconsole 50", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>O.<1>__Dump; if (obj2 == null) { ConsoleEvent val2 = Dump; <>O.<1>__Dump = val2; obj2 = (object)val2; } new ConsoleCommand("dumpconsole", "Write buffered F5 lines to BepInEx/ConsoleCaptures. Optional count: dumpconsole 100", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>O.<2>__DumpCommand; if (obj3 == null) { ConsoleEvent val3 = DumpCommand; <>O.<2>__DumpCommand = val3; obj3 = (object)val3; } new ConsoleCommand("dumpcommand", "Run one or more console commands and dump only their output. dumpcommand altbioms dumpcommand \"pos; seed\"", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>O.<3>__Clear; if (obj4 == null) { ConsoleEvent val4 = Clear; <>O.<3>__Clear = val4; obj4 = (object)val4; } new ConsoleCommand("clearconsolebuffer", "Clear the in-memory console capture buffer. Does not clear the F5 console or delete the log.", (ConsoleEvent)obj4, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = <>O.<4>__Capture; if (obj5 == null) { ConsoleEvent val5 = Capture; <>O.<4>__Capture = val5; obj5 = (object)val5; } object obj6 = <>O.<5>__Options; if (obj6 == null) { ConsoleOptionsFetcher val6 = Options; <>O.<5>__Options = val6; obj6 = (object)val6; } new ConsoleCommand("consolecapture", "Show capture status, or toggle it: consolecapture on / consolecapture off", (ConsoleEvent)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj6, true, false, false); } private static List<string> Options() { return new List<string> { "on", "off" }; } private static void Copy(ConsoleEventArgs args) { if (!TryParseCount(Rest(args), "copyconsole", out var count, out var error)) { Reply(args, error); return; } ConsoleCapture.TryCopy(count, out var message); Reply(args, message); } private static void Dump(ConsoleEventArgs args) { if (!TryParseCount(Rest(args), "dumpconsole", out var count, out var error)) { Reply(args, error); return; } ConsoleCapture.TryDump(count, out var message); Reply(args, message); } private static void DumpCommand(ConsoleEventArgs args) { string[] array = SplitChain(CommandText(args)); if (array.Length == 0) { Reply(args, "Usage: dumpcommand <command> or dumpcommand \"command; command\""); return; } for (int i = 0; i < array.Length; i++) { string a = array[i].Split(' ')[0]; if (string.Equals(a, "dumpcommand", StringComparison.OrdinalIgnoreCase)) { Reply(args, "dumpcommand cannot run itself."); return; } } if (ConsoleCapture.IsScoping()) { Reply(args, "dumpcommand is already running."); return; } Terminal val = args?.Context; if ((Object)(object)val == (Object)null) { val = (Terminal)(object)Console.instance; } if ((Object)(object)val == (Object)null) { Reply(args, "Console is not available."); return; } ConsoleCapture.BeginScope(); string text = null; try { for (int j = 0; j < array.Length; j++) { val.TryRunCommand(ShieldSegment(array[j]), false, false); } } catch (Exception ex) { text = ((array.Length == 1) ? array[0] : "the command chain"); if (ValheimConsoleCapturePlugin.ModLogger != null) { ValheimConsoleCapturePlugin.ModLogger.LogError((object)("dumpcommand failed: " + ex.Message)); } } string[] array2 = ConsoleCapture.EndScope(); if (array2.Length == 0) { Reply(args, (text != null) ? ("Failed to run " + text + ".") : "The command produced no console lines."); return; } ConsoleCapture.TryWriteDump(array2, ChainHint(array), out var message); Reply(args, message); if (text != null) { Reply(args, "Failed to run " + text + "."); } } internal static string ShieldSegment(string command) { if (string.IsNullOrEmpty(command) || command.IndexOf(';') < 0) { return command; } return command.Replace(';', '\u001e'); } internal static void RestoreChain(ConsoleEventArgs args) { if (args == null) { return; } args.FullLine = Restore(args.FullLine); args.ArgsAll = Restore(args.ArgsAll); if (args.Args != null) { for (int i = 0; i < args.Args.Length; i++) { args.Args[i] = Restore(args.Args[i]); } } } private static string Restore(string text) { if (string.IsNullOrEmpty(text) || text.IndexOf('\u001e') < 0) { return text; } return text.Replace('\u001e', ';'); } private static string CommandText(ConsoleEventArgs args) { string text = ((args == null || args.ArgsAll == null) ? "" : args.ArgsAll.Trim()); if (text.IndexOf('\u001e') >= 0) { text = text.Replace('\u001e', ';'); } if (text.Length >= 2 && text[0] == '"' && text[text.Length - 1] == '"') { text = text.Substring(1, text.Length - 2).Trim(); } return text; } private static string[] SplitChain(string text) { if (string.IsNullOrWhiteSpace(text)) { return Array.Empty<string>(); } List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (char c in text) { switch (c) { case '"': flag = !flag; stringBuilder.Append(c); continue; case ';': if (!flag) { string text2 = stringBuilder.ToString().Trim(); if (text2.Length > 0) { list.Add(text2); } stringBuilder.Length = 0; continue; } break; } stringBuilder.Append(c); } string text3 = stringBuilder.ToString().Trim(); if (text3.Length > 0) { list.Add(text3); } return list.ToArray(); } private static string ChainHint(string[] commands) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < commands.Length; i++) { string text = commands[i].Split(' ')[0]; StringBuilder stringBuilder2 = new StringBuilder(); for (int j = 0; j < text.Length; j++) { if (stringBuilder2.Length >= 20) { break; } char c = text[j]; if (char.IsLetterOrDigit(c)) { stringBuilder2.Append(char.ToLowerInvariant(c)); } } if (stringBuilder2.Length == 0) { continue; } if (stringBuilder.Length > 0) { if (stringBuilder.Length + 1 + stringBuilder2.Length > 40) { break; } stringBuilder.Append('-'); } stringBuilder.Append(stringBuilder2); } return stringBuilder.ToString(); } private static void Clear(ConsoleEventArgs args) { Reply(args, ConsoleCapture.ClearBuffer()); } private static void Capture(ConsoleEventArgs args) { string[] array = Rest(args); if (array.Length > 1) { Reply(args, "Usage: consolecapture [on|off]"); return; } if (array.Length == 1) { if (string.Equals(array[0], "on", StringComparison.OrdinalIgnoreCase)) { ConsoleCapture.SetCaptureEnabled(enabled: true); } else { if (!string.Equals(array[0], "off", StringComparison.OrdinalIgnoreCase)) { Reply(args, "Usage: consolecapture [on|off]"); return; } ConsoleCapture.SetCaptureEnabled(enabled: false); } } string[] array2 = ConsoleCapture.StatusLines(); for (int i = 0; i < array2.Length; i++) { Reply(args, array2[i]); } } private static string[] Rest(ConsoleEventArgs args) { if (args == null || args.Args == null || args.Args.Length <= 1) { return Array.Empty<string>(); } string[] array = new string[args.Args.Length - 1]; Array.Copy(args.Args, 1, array, 0, array.Length); return array; } private static bool TryParseCount(string[] args, string command, out int? count, out string error) { count = null; error = null; if (args == null || args.Length == 0) { return true; } if (args.Length == 1 && int.TryParse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result >= 1) { count = result; return true; } error = "Usage: " + command + " [count]"; return false; } private static void Reply(ConsoleEventArgs args, string message) { Terminal val = args?.Context; if ((Object)(object)val != (Object)null) { val.AddString(message); return; } Console instance = Console.instance; if ((Object)(object)instance != (Object)null) { ((Terminal)instance).AddString(message); } else if (ValheimConsoleCapturePlugin.ModLogger != null) { ValheimConsoleCapturePlugin.ModLogger.LogInfo((object)message); } } } internal static class ConsoleCapture { private static readonly Regex RichTextTags = new Regex("</?(?:b|i|u|s|color|size|material|quad|sprite|link|mark|noparse)(?:\\s[^>]*|=[^>]*)?>", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly object Gate = new object(); private static readonly Queue<string> Lines = new Queue<string>(); private static StreamWriter _writer; private static bool _runtimeEnabled; private static bool _expectCommandEcho; private static bool _shutdown; private static bool _inRecord; private static bool _fileFault; private static bool _loggedFault; private static string _lastAutocomplete; private static List<string> _scope; internal static void Start() { _shutdown = false; _runtimeEnabled = ValheimConsoleCapturePlugin.Enabled == null || ValheimConsoleCapturePlugin.Enabled.Value; if (_runtimeEnabled) { lock (Gate) { EnsureWriter(); } } } internal static void Shutdown() { lock (Gate) { _shutdown = true; _runtimeEnabled = false; CloseWriter(); } } internal static void SetCaptureEnabled(bool enabled) { lock (Gate) { _runtimeEnabled = enabled; if (!enabled) { _lastAutocomplete = null; } else { EnsureWriter(); } } } internal static void ExpectCommandEcho() { _expectCommandEcho = true; } internal static void EndCommandEcho() { _expectCommandEcho = false; } internal static bool IsScoping() { lock (Gate) { return _scope != null; } } internal static void BeginScope() { lock (Gate) { _scope = new List<string>(); } } internal static string[] EndScope() { lock (Gate) { string[] result = ((_scope == null) ? Array.Empty<string>() : _scope.ToArray()); _scope = null; return result; } } internal static void OnTerminalLine(string text) { bool expectCommandEcho = _expectCommandEcho; _expectCommandEcho = false; if (text == null) { return; } bool flag = IsScoping(); bool flag2 = _runtimeEnabled; if (flag2) { if (expectCommandEcho) { if (ValheimConsoleCapturePlugin.CaptureCommands != null && !ValheimConsoleCapturePlugin.CaptureCommands.Value) { flag2 = false; } } else if (ValheimConsoleCapturePlugin.CaptureOutput != null && !ValheimConsoleCapturePlugin.CaptureOutput.Value) { flag2 = false; } } if (!flag2 && !flag) { return; } foreach (string item in SplitLines(text)) { string line = ApplyRichText(item); if (flag2) { Record(line); } else { RememberScope(line); } } } internal static void OnAutocomplete(string text) { if (!_runtimeEnabled || ValheimConsoleCapturePlugin.CaptureAutocomplete == null || !ValheimConsoleCapturePlugin.CaptureAutocomplete.Value) { return; } if (string.IsNullOrEmpty(text)) { _lastAutocomplete = null; return; } string text2 = ApplyRichText(text); if (text2.Length != 0 && !(text2 == _lastAutocomplete)) { _lastAutocomplete = text2; Record(text2); } } internal static string[] StatusLines() { int count; int num; string text; lock (Gate) { count = Lines.Count; num = MaxLines(); text = FileStatus(); } return new string[3] { "Console capture: " + (_runtimeEnabled ? "Enabled" : "Disabled"), "File logging: " + text, "Buffered lines: " + count.ToString(CultureInfo.InvariantCulture) + " / " + num.ToString(CultureInfo.InvariantCulture) }; } internal static bool TryCopy(int? count, out string message) { if (ValheimConsoleCapturePlugin.EnableClipboard != null && !ValheimConsoleCapturePlugin.EnableClipboard.Value) { message = "Clipboard copying is disabled in config."; return false; } string[] array = CopyRecent(count); if (array.Length == 0) { message = "Console buffer is empty."; return false; } try { GUIUtility.systemCopyBuffer = string.Join(Environment.NewLine, array); } catch (Exception ex) { if (ValheimConsoleCapturePlugin.ModLogger != null) { ValheimConsoleCapturePlugin.ModLogger.LogError((object)("Clipboard copy failed: " + ex.Message)); } message = "Failed to copy console lines to the clipboard."; return false; } message = ((array.Length == 1) ? "Copied 1 console line to clipboard." : ("Copied " + array.Length.ToString(CultureInfo.InvariantCulture) + " console lines to clipboard.")); return true; } internal static bool TryDump(int? count, out string message) { string[] array = CopyRecent(count); if (array.Length == 0) { message = "Console buffer is empty."; return false; } return TryWriteDump(array, null, out message); } internal static bool TryWriteDump(string[] lines, string nameHint, out string message) { if (lines == null || lines.Length == 0) { message = "Nothing to dump."; return false; } try { string text = ResolveUnderGame(Path.Combine("BepInEx", "ConsoleCaptures")); Directory.CreateDirectory(text); string text2 = DateTime.Now.ToString("yyyy-MM-dd-HHmmss", CultureInfo.InvariantCulture); string text3 = SanitizeFileHint(nameHint); string text4 = (string.IsNullOrEmpty(text3) ? ("console-" + text2) : ("console-" + text3 + "-" + text2)); string text5 = Path.Combine(text, text4 + ".txt"); int num = 2; while (File.Exists(text5)) { text5 = Path.Combine(text, text4 + "-" + num.ToString(CultureInfo.InvariantCulture) + ".txt"); num++; } File.WriteAllText(text5, string.Join(Environment.NewLine, lines) + Environment.NewLine, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); string text6 = lines.Length.ToString(CultureInfo.InvariantCulture); message = "Dumped " + text6 + " console " + ((lines.Length == 1) ? "line" : "lines") + " to " + DisplayPath(text5) + "."; return true; } catch (Exception ex) { if (ValheimConsoleCapturePlugin.ModLogger != null) { ValheimConsoleCapturePlugin.ModLogger.LogError((object)("Console dump failed: " + ex.Message)); } message = "Failed to write the console dump."; return false; } } internal static string ClearBuffer() { lock (Gate) { Lines.Clear(); _lastAutocomplete = null; } return "Console buffer cleared."; } private static void Record(string line) { if (_inRecord) { return; } _inRecord = true; try { lock (Gate) { if (_scope != null) { _scope.Add(line ?? ""); } if (_runtimeEnabled && !_shutdown) { Lines.Enqueue(line ?? ""); Trim(); WriteLine(line ?? ""); } } } finally { _inRecord = false; } } private static void RememberScope(string line) { lock (Gate) { if (_scope != null) { _scope.Add(line ?? ""); } } } private static string SanitizeFileHint(string nameHint) { if (string.IsNullOrWhiteSpace(nameHint)) { return ""; } string text = nameHint.Trim().Split(' ')[0]; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < text.Length; i++) { if (stringBuilder.Length >= 40) { break; } char c = text[i]; if (char.IsLetterOrDigit(c) || c == '-' || c == '_') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static void Trim() { int num = MaxLines(); while (Lines.Count > num) { Lines.Dequeue(); } } private static int MaxLines() { if (ValheimConsoleCapturePlugin.BufferedLines == null) { return 1000; } int value = ValheimConsoleCapturePlugin.BufferedLines.Value; if (value >= 1) { return value; } return 1; } private static string[] CopyRecent(int? count) { lock (Gate) { Trim(); string[] array = Lines.ToArray(); if (array.Length == 0) { return Array.Empty<string>(); } int num = array.Length; if (count.HasValue && count.Value < num) { num = count.Value; } if (num == array.Length) { return array; } string[] array2 = new string[num]; Array.Copy(array, array.Length - num, array2, 0, num); return array2; } } private static void EnsureWriter() { if (_shutdown || _writer != null || ValheimConsoleCapturePlugin.LogToFile == null || !ValheimConsoleCapturePlugin.LogToFile.Value) { return; } try { string path = ResolveUnderGame((ValheimConsoleCapturePlugin.LogPath == null) ? "BepInEx/ValheimConsole.log" : ValheimConsoleCapturePlugin.LogPath.Value); string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } FileMode mode = ((ValheimConsoleCapturePlugin.AppendMode == null || ValheimConsoleCapturePlugin.AppendMode.Value) ? FileMode.Append : FileMode.Create); FileStream stream = new FileStream(path, mode, FileAccess.Write, FileShare.ReadWrite); _writer = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); _fileFault = false; string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); _writer.WriteLine("============================================================"); _writer.WriteLine("Valheim Console Capture"); _writer.WriteLine("Session started: " + text); _writer.WriteLine("============================================================"); _writer.Flush(); } catch (Exception ex) { NoteFault(ex); } } private static void WriteLine(string line) { if (ValheimConsoleCapturePlugin.LogToFile == null || !ValheimConsoleCapturePlugin.LogToFile.Value) { return; } EnsureWriter(); if (_writer == null) { return; } try { if (ValheimConsoleCapturePlugin.TimestampLines == null || ValheimConsoleCapturePlugin.TimestampLines.Value) { string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); _writer.WriteLine("[" + text + "] " + line); } else { _writer.WriteLine(line); } if (ValheimConsoleCapturePlugin.FlushImmediately == null || ValheimConsoleCapturePlugin.FlushImmediately.Value) { _writer.Flush(); } } catch (Exception ex) { NoteFault(ex); } } private static void CloseWriter() { if (_writer != null) { try { _writer.Flush(); _writer.Dispose(); } catch (Exception ex) { NoteFault(ex); } _writer = null; } } private static string FileStatus() { if (ValheimConsoleCapturePlugin.LogToFile == null || !ValheimConsoleCapturePlugin.LogToFile.Value) { return "Disabled"; } if (_fileFault) { return "Error"; } return "Enabled"; } private static void NoteFault(Exception ex) { _fileFault = true; if (!_loggedFault && ValheimConsoleCapturePlugin.ModLogger != null) { _loggedFault = true; ValheimConsoleCapturePlugin.ModLogger.LogError((object)("Console log write failed: " + ex)); } } private static string ApplyRichText(string line) { if (string.IsNullOrEmpty(line)) { return line ?? ""; } if (ValheimConsoleCapturePlugin.StripRichText == null || !ValheimConsoleCapturePlugin.StripRichText.Value) { return line; } return RichTextTags.Replace(line, ""); } private static IEnumerable<string> SplitLines(string text) { if (text.Length == 0) { yield return ""; yield break; } string text2 = text.Replace("\r\n", "\n").Replace('\r', '\n'); bool flag = text2[text2.Length - 1] == '\n'; string[] parts = text2.Split('\n'); int count = parts.Length; if (flag && count > 1) { count--; } for (int i = 0; i < count; i++) { yield return parts[i]; } } private static string ResolveUnderGame(string relativeOrAbsolute) { if (string.IsNullOrWhiteSpace(relativeOrAbsolute)) { relativeOrAbsolute = "BepInEx/ValheimConsole.log"; } if (Path.IsPathRooted(relativeOrAbsolute)) { return Path.GetFullPath(relativeOrAbsolute); } string text = relativeOrAbsolute.Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar); string text2 = "BepInEx" + Path.DirectorySeparatorChar; if (text.Equals("BepInEx", StringComparison.OrdinalIgnoreCase)) { return Paths.BepInExRootPath; } if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { return Path.GetFullPath(Path.Combine(Paths.BepInExRootPath, text.Substring(text2.Length))); } return Path.GetFullPath(Path.Combine(Paths.GameRootPath, text)); } private static string DisplayPath(string fullPath) { if (string.IsNullOrEmpty(fullPath)) { return fullPath; } string bepInExRootPath = Paths.BepInExRootPath; if (!string.IsNullOrEmpty(bepInExRootPath)) { string text = bepInExRootPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; if (fullPath.StartsWith(text, StringComparison.OrdinalIgnoreCase)) { return "BepInEx/" + fullPath.Substring(text.Length).Replace('\\', '/'); } } string gameRootPath = Paths.GameRootPath; if (!string.IsNullOrEmpty(gameRootPath)) { string text2 = gameRootPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; if (fullPath.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { return fullPath.Substring(text2.Length).Replace('\\', '/'); } } return fullPath; } } [HarmonyPatch(typeof(Terminal), "AddString", new Type[] { typeof(string) })] internal static class TerminalAddStringPatch { private static void Postfix(Terminal __instance, string text) { if (__instance is Console) { ConsoleCapture.OnTerminalLine(text); } } } [HarmonyPatch(typeof(Terminal), "InputText")] internal static class TerminalInputTextPatch { private static void Prefix(Terminal __instance) { if (__instance is Console) { ConsoleCapture.ExpectCommandEcho(); } } private static void Postfix() { ConsoleCapture.EndCommandEcho(); } } [HarmonyPatch(typeof(Terminal), "TryRunCommand")] internal static class TerminalTryRunCommandPatch { [HarmonyPriority(800)] private static void Prefix(ref string text) { ConsoleCommands.ProtectChain(ref text); } } [HarmonyPatch(typeof(ConsoleCommand), "RunAction")] internal static class TerminalRunActionPatch { [HarmonyPriority(800)] private static void Prefix(ConsoleEventArgs args) { ConsoleCommands.RestoreChain(args); } } [HarmonyPatch(typeof(Terminal), "updateSearch")] internal static class TerminalAutocompletePatch { private static void Postfix(Terminal __instance) { if (__instance is Console) { TMP_Text search = __instance.m_search; if (!((Object)(object)search == (Object)null)) { ConsoleCapture.OnAutocomplete(search.text); } } } } [BepInPlugin("com.sonicdm.valheim.valheimconsolecapture", "ValheimConsoleCapture", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class ValheimConsoleCapturePlugin : BaseUnityPlugin { public const string PluginGuid = "com.sonicdm.valheim.valheimconsolecapture"; public const string PluginName = "ValheimConsoleCapture"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource ModLogger; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> LogToFile; internal static ConfigEntry<string> LogPath; internal static ConfigEntry<bool> AppendMode; internal static ConfigEntry<bool> TimestampLines; internal static ConfigEntry<bool> FlushImmediately; internal static ConfigEntry<int> BufferedLines; internal static ConfigEntry<bool> EnableClipboard; internal static ConfigEntry<bool> CaptureCommands; internal static ConfigEntry<bool> CaptureOutput; internal static ConfigEntry<bool> StripRichText; internal static ConfigEntry<bool> CaptureAutocomplete; private Harmony _harmony; private void Awake() { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown ModLogger = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Capture F5 console lines when the game starts. consolecapture on/off overrides this until the next launch."); LogToFile = ((BaseUnityPlugin)this).Config.Bind<bool>("File", "LogToFile", true, "Append captured lines to the console log file."); LogPath = ((BaseUnityPlugin)this).Config.Bind<string>("File", "LogPath", "BepInEx/ValheimConsole.log", "Log file path. A BepInEx/... path is inside the active BepInEx folder (the Gale profile when using a mod manager). Other relative paths start at the game folder. Applied when the file is opened."); AppendMode = ((BaseUnityPlugin)this).Config.Bind<bool>("File", "AppendMode", true, "Append to an existing log. When false, the file is replaced at the start of the session. Applied when the file is opened."); TimestampLines = ((BaseUnityPlugin)this).Config.Bind<bool>("File", "TimestampLines", true, "Prefix each logged line with the local time. Clipboard and dump files stay unstamped."); FlushImmediately = ((BaseUnityPlugin)this).Config.Bind<bool>("File", "FlushImmediately", true, "Flush the log after every line so a crash still keeps the output."); BufferedLines = ((BaseUnityPlugin)this).Config.Bind<int>("Buffer", "BufferedLines", 1000, new ConfigDescription("How many recent console lines to keep in memory for copyconsole and dumpconsole.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), Array.Empty<object>())); EnableClipboard = ((BaseUnityPlugin)this).Config.Bind<bool>("Clipboard", "EnableClipboard", true, "Allow copyconsole to write to the system clipboard."); CaptureCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "CaptureCommands", true, "Record the command line Valheim echoes when you press Enter."); CaptureOutput = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "CaptureOutput", true, "Record command results and other F5 console lines."); StripRichText = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "StripRichText", false, "Remove Unity rich-text tags such as <color=yellow> from the log, clipboard, and dumps."); CaptureAutocomplete = ((BaseUnityPlugin)this).Config.Bind<bool>("Filtering", "CaptureAutocomplete", true, "Record the live suggestion bar. Tab completion writes that text field directly and does not call Terminal.AddString."); ConsoleCapture.Start(); _harmony = new Harmony("com.sonicdm.valheim.valheimconsolecapture"); try { _harmony.PatchAll(typeof(ValheimConsoleCapturePlugin).Assembly); ConsoleCommands.Register(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ValheimConsoleCapture 1.0.0 loaded."); } catch (Exception ex) { ConsoleCapture.Shutdown(); ((BaseUnityPlugin)this).Logger.LogError((object)("ValheimConsoleCapture failed to start: " + ex)); } } private void OnDestroy() { if (_harmony != null) { try { _harmony.UnpatchSelf(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Unpatch failed: " + ex.Message)); } _harmony = null; } ConsoleCapture.Shutdown(); } } }