Decompiled source of BetterTerminal v1.1.3
plugins/BetterTerminal/BetterTerminal.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Json; using System.Text.RegularExpressions; using AIGraph; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BetterTerminal.TerminalItems; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("STFO")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BetterTerminal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+2642f2721028b39a90b2dee3ed12b0c02c2e1d56")] [assembly: AssemblyProduct("BetterTerminal")] [assembly: AssemblyTitle("BetterTerminal")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BetterTerminal { [BepInPlugin("BetterTerminal", "BetterTerminal", "1.1.3")] public class Plugin : BasePlugin { [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "TryUpdateLineForAutoComplete")] internal static class AutoCompletePatch { private static LG_ComputerTerminalCommandInterpreter? _selectionInterpreter; private static readonly List<string> SelectionCandidates = new List<string>(); private static readonly List<string> RenderedSelectionLines = new List<string>(); private static string _selectionPrefix = string.Empty; private static string _selectionLine = string.Empty; private static int _selectionIndex; private static int _selectionScreenLineCount; private static void Postfix(LG_ComputerTerminalCommandInterpreter __instance, string input, ref string autoCompletedLine, ref bool __result) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown ManualLogSource modLog = ModLog; bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(46, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] INPUT='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(input); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' NATIVE_RESULT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(__result); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" NATIVE_OUTPUT='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(autoCompletedLine); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'"); } modLog.LogDebug(val); if (!EnableFuzzySearch.Value || string.IsNullOrWhiteSpace(input)) { ModLog.LogDebug((object)"[TAB] FUZZY DISABLED OR EMPTY INPUT"); return; } string text = input.TrimEnd().ToUpperInvariant(); int num = text.LastIndexOf('|'); int i; for (i = num + 1; i < text.Length && text[i] == ' '; i++) { } string text2 = text.Substring(0, i); string text3 = text.Substring(i); if (text3.Length == 0) { return; } int num2 = text3.IndexOf(' '); if (num2 < 0) { if (__result || !EnableCommandFuzzySearch.Value) { ModLog.LogDebug((object)"[TAB] KEEPING NATIVE COMMAND AUTOCOMPLETE"); } else { CompleteCommand(__instance, text3, text2, num >= 0, ref autoCompletedLine, ref __result); } } else if (!EnableIdentifierFuzzySearch.Value) { ModLog.LogDebug((object)"[TAB] IDENTIFIER FUZZY DISABLED"); } else { CompleteArgument(__instance, text3, num2, text2, ref autoCompletedLine, ref __result); } } private static void CompleteArgument(LG_ComputerTerminalCommandInterpreter interpreter, string input, int firstSpace, string segmentPrefix, ref string output, ref bool result) { if (!Enum.TryParse<CommandType>(input.Substring(0, firstSpace), ignoreCase: true, out var result2)) { return; } if (((uint)(result2 - 1) <= 2u || (uint)(result2 - 5) <= 1u) ? true : false) { CompleteIdentifier(interpreter, input, firstSpace, segmentPrefix, ref output, ref result); return; } string[] array = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); IEnumerable<string> enumerable = null; bool flag = (uint)(result2 - 11) <= 1u; if (flag && array.Length == 2) { enumerable = from name in Enum.GetNames<PipelineField>() select name.ToUpperInvariant(); } else if (result2 == CommandType.Sort && array.Length == 3) { enumerable = new string[1] { "DESC" }; } else if (result2 == CommandType.Help && array.Length == 2) { enumerable = from name in Enum.GetNames<HelpTopic>() select name.ToUpperInvariant(); } else if (result2 == CommandType.BetterTerminal && array.Length == 2) { enumerable = from name in Enum.GetNames<BetterTerminalAction>() select name.ToUpperInvariant(); } else if (result2 == CommandType.Docs && array.Length == 2) { enumerable = from name in Enum.GetNames<DocsAction>() select name.ToUpperInvariant(); } else if (result2 == CommandType.History && array.Length == 2) { enumerable = from name in Enum.GetNames<HistoryAction>() select name.ToUpperInvariant(); } else if (result2 == CommandType.Config && array.Length == 2) { enumerable = ConfigCommandPatch.AutoCompleteKeys; } else if (result2 == CommandType.Config && array.Length == 3) { enumerable = ConfigCommandPatch.AutoCompleteValues(array[1]); } if (enumerable != null) { int num = input.LastIndexOf(' '); string input2 = input.Substring(num + 1); if (TryFindBestMatches(input2, enumerable, "ARGUMENT", out List<string> matches)) { string prefix = BuildArgumentPrefix(segmentPrefix, input, num); ApplyMatches(interpreter, input2, prefix, matches, ref output, ref result); } } } private static void CompleteCommand(LG_ComputerTerminalCommandInterpreter interpreter, string input, string prefix, bool pipelineStage, ref string output, ref bool result) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); if (pipelineStage && EnablePipelines.Value) { PipelineStage[] values = Enum.GetValues<PipelineStage>(); for (int i = 0; i < values.Length; i++) { PipelineStage pipelineStage2 = values[i]; list.Add(pipelineStage2.ToString().ToUpperInvariant()); } } else { Enumerator<string, TERM_Command> enumerator = interpreter.m_commandsPerString.Keys.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; if (!LG_ComputerTerminalCommandInterpreter.CommandIsAlwaysHidden(interpreter.m_commandsPerString[current])) { list.Add(current.ToUpperInvariant()); } } list.AddRange(ModCommands.Names); } if (!TryFindBestMatches(input, list, "COMMAND", out List<string> matches)) { ModLog.LogDebug((object)"[TAB] NO COMMAND MATCHES"); } else { ApplyMatches(interpreter, input, prefix, matches, ref output, ref result); } } private static void CompleteIdentifier(LG_ComputerTerminalCommandInterpreter interpreter, string input, int firstSpace, string segmentPrefix, ref string output, ref bool result) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown string text = input.Substring(0, firstSpace); bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val; if (!Enum.TryParse<CommandType>(text, ignoreCase: true, out var result2)) { ManualLogSource modLog = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(46, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] UNKNOWN COMMAND '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' FOR IDENTIFIER SEARCH"); } modLog.LogDebug(val); return; } flag = (((uint)(result2 - 1) <= 2u || (uint)(result2 - 5) <= 1u) ? true : false); if (!flag) { ManualLogSource modLog2 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(51, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] COMMAND '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<CommandType>(result2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' DOES NOT SUPPORT IDENTIFIER SEARCH"); } modLog2.LogDebug(val); return; } int num = input.LastIndexOf(' '); if (num == input.Length - 1) { return; } LG_LevelInteractionManager current = LG_LevelInteractionManager.Current; Dictionary<string, iTerminalItem> val2 = ((current != null) ? current.m_terminalItemsByKeyString : null); if (val2 == null) { ModLog.LogDebug((object)"[TAB] TERMINAL ITEM DICTIONARY IS NULL"); return; } string text2 = input.Substring(num + 1); bool flag2 = text2.IndexOfAny(new char[2] { '_', '-' }) >= 0; HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Enumerator<string, iTerminalItem> enumerator = val2.Keys.GetEnumerator(); while (enumerator.MoveNext()) { string text3 = enumerator.Current.ToUpperInvariant(); if (flag2) { hashSet.Add(text3); continue; } string[] array = text3.Split(new char[2] { '_', '-' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { hashSet.Add(item); } } ManualLogSource modLog3 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(34, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] CHECKING "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(hashSet.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" IDENTIFIERS FOR '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'"); } modLog3.LogDebug(val); if (!TryFindBestMatches(text2, hashSet, "IDENTIFIER", out List<string> matches)) { ModLog.LogDebug((object)"[TAB] NO IDENTIFIER MATCHES"); return; } string prefix = BuildArgumentPrefix(segmentPrefix, input, num); ApplyMatches(interpreter, text2, prefix, matches, ref output, ref result); } private static string BuildArgumentPrefix(string segmentPrefix, string input, int activeWordSeparator) { string[] value = input.Substring(0, activeWordSeparator).Split(' ', StringSplitOptions.RemoveEmptyEntries); return segmentPrefix + string.Join(" ", value) + " "; } private static bool TryFindBestMatches(string input, IEnumerable<string> candidates, string candidateType, out List<string> matches) { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Expected O, but got Unknown string text = input.ToUpperInvariant(); matches = new List<string>(); List<string> list = new List<string>(); List<(string, int)> list2 = new List<(string, int)>(); bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val; foreach (string candidate in candidates) { if (string.IsNullOrWhiteSpace(candidate)) { continue; } string text2 = candidate.ToUpperInvariant(); if (text2.StartsWith(text, StringComparison.OrdinalIgnoreCase)) { list.Add(candidate); ManualLogSource modLog = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(40, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidateType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ITEM='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidate); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' PREFIX=True ACCEPTED=True"); } modLog.LogDebug(val); continue; } int num = Levenshtein.Compute(text, text2); bool flag2 = num <= MaximumLevenshteinDistance.Value; ManualLogSource modLog2 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(34, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidateType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ITEM='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidate); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' DISTANCE="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ACCEPTED="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag2); } modLog2.LogDebug(val); if (flag2) { list2.Add((candidate, num)); } } if (list.Count > 0) { list.Sort(StringComparer.OrdinalIgnoreCase); matches.AddRange(list); ManualLogSource modLog3 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(31, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidateType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] PREFIX_COUNT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(matches.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" MATCHES=["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", matches)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("]"); } modLog3.LogDebug(val); return true; } list2.Sort(delegate((string Candidate, int Distance) left, (string Candidate, int Distance) right) { int num2 = left.Distance.CompareTo(right.Distance); return (num2 == 0) ? StringComparer.OrdinalIgnoreCase.Compare(left.Candidate, right.Candidate) : num2; }); foreach (var item2 in list2) { string item = item2.Item1; matches.Add(item); } ManualLogSource modLog4 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(33, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(candidateType); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ACCEPTED_COUNT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(matches.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" MATCHES=["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", matches)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("]"); } modLog4.LogDebug(val); return matches.Count > 0; } private static void ApplyMatches(LG_ComputerTerminalCommandInterpreter interpreter, string input, string prefix, List<string> matches, ref string output, ref bool result) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val; if (matches.Count == 1) { ManualLogSource modLog = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(30, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] SINGLE MATCH SELECTED='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(matches[0]); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'"); } modLog.LogDebug(val); ClearSelection(); output = prefix + matches[0]; result = true; return; } if (AmbiguousMatches.Value == AmbiguousMatchMode.CommonPrefix) { ClearSelection(); string text = FindCommonPrefix(matches); ManualLogSource modLog2 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(39, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] COMMON PREFIX='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' CANDIDATE_COUNT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(matches.Count); } modLog2.LogDebug(val); if (text.Length > input.Length) { output = prefix + text; result = true; } return; } _selectionInterpreter = interpreter; _selectionPrefix = prefix; _selectionIndex = 0; SelectionCandidates.Clear(); SelectionCandidates.AddRange(matches); _selectionLine = prefix + SelectionCandidates[_selectionIndex]; ManualLogSource modLog3 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(48, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[TAB] ARROW SELECTION STARTED COUNT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SelectionCandidates.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" SELECTED='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_selectionLine); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'"); } modLog3.LogDebug(val); RenderSelectionList(); output = _selectionLine; result = true; } private static string BuildSelectionList(List<string> candidates, int selectedIndex) { List<string> list = new List<string> { "FUZZY MATCHES - USE UP/DOWN ARROWS:" }; for (int i = 0; i < candidates.Count; i++) { string value = ((i == selectedIndex) ? "->" : " "); list.Add($"{value} {i + 1}. {candidates[i]}"); } return string.Join("\n", list); } private static string FindCommonPrefix(List<string> candidates) { string text = candidates[0]; for (int i = 1; i < candidates.Count; i++) { if (text.Length <= 0) { break; } string text2 = candidates[i]; int num = Math.Min(text.Length, text2.Length); int j; for (j = 0; j < num && char.ToUpperInvariant(text[j]) == char.ToUpperInvariant(text2[j]); j++) { } text = text.Substring(0, j); } return text; } internal static bool TryStepSelection(LG_ComputerTerminalCommandInterpreter interpreter, bool stepPrevious, ref string result) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val; if (AmbiguousMatches.Value != AmbiguousMatchMode.ArrowSelection || SelectionCandidates.Count < 2) { ManualLogSource modLog = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(39, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ARROW] SELECTION INACTIVE MODE="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<AmbiguousMatchMode>(AmbiguousMatches.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" COUNT="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(SelectionCandidates.Count); } modLog.LogDebug(val); return false; } int num = ((!stepPrevious) ? 1 : (-1)); _selectionIndex = (_selectionIndex + num + SelectionCandidates.Count) % SelectionCandidates.Count; _selectionLine = _selectionPrefix + SelectionCandidates[_selectionIndex]; ManualLogSource modLog2 = ModLog; val = new BepInExDebugLogInterpolatedStringHandler(37, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ARROW] DIRECTION="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(stepPrevious ? "UP" : "DOWN"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" INDEX="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_selectionIndex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" SELECTED='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_selectionLine); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'"); } modLog2.LogDebug(val); RenderSelectionList(); result = _selectionLine; return true; } private static void RenderSelectionList() { RemoveSelectionList(); if (_selectionInterpreter != null && SelectionCandidates.Count >= 2) { string[] array = BuildSelectionList(SelectionCandidates, _selectionIndex).Split('\n'); string[] array2 = array; foreach (string text in array2) { _selectionInterpreter.m_screenBuffer.Add(text); RenderedSelectionLines.Add(text); } _selectionScreenLineCount = array.Length; } } private static void RemoveSelectionList() { if (_selectionInterpreter == null || _selectionScreenLineCount == 0) { return; } for (int num = RenderedSelectionLines.Count - 1; num >= 0; num--) { int num2 = _selectionInterpreter.m_screenBuffer.Count - 1; if (num2 < 0 || !string.Equals(_selectionInterpreter.m_screenBuffer[num2], RenderedSelectionLines[num], StringComparison.Ordinal)) { ModLog.LogDebug((object)"[SELECTION] SCREEN BUFFER CHANGED; SKIPPING UNSAFE LINE REMOVAL"); break; } _selectionInterpreter.m_screenBuffer.RemoveAt(num2); } RenderedSelectionLines.Clear(); _selectionScreenLineCount = 0; } internal static void ClearSelection() { RemoveSelectionList(); _selectionInterpreter = null; _selectionPrefix = string.Empty; _selectionLine = string.Empty; _selectionIndex = 0; _selectionScreenLineCount = 0; SelectionCandidates.Clear(); RenderedSelectionLines.Clear(); } } [HarmonyPatch(typeof(LG_ComputerTerminal), "Update")] internal static class TerminalUpdatePatch { private static bool Prefix(LG_ComputerTerminal __instance) { if (!EnableHistorySearch.Value || TerminalInputReservationRegistry.IsReserved(__instance.SyncID)) { return true; } if (HistorySearchManager.IsActive(__instance.SyncID)) { return !HistorySearchManager.HandleUpdate(__instance); } if (InputMapper.GetButtonDown.Invoke((InputAction)15, (eFocusState)8) && HistorySearchManager.TryOpen(__instance)) { return false; } return true; } private static void Postfix(LG_ComputerTerminal __instance) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown if (HistorySearchManager.IsActive(__instance.SyncID) || TerminalInputReservationRegistry.IsReserved(__instance.SyncID)) { return; } bool flag = InputMapper.GetButtonDown.Invoke((InputAction)15, (eFocusState)8); bool flag2 = InputMapper.GetButtonDown.Invoke((InputAction)16, (eFocusState)8); if (flag || flag2) { ManualLogSource modLog = ModLog; bool flag3 = default(bool); BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(33, 3, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ARROW INPUT] UP="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" DOWN="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" TERMINAL="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(__instance.SyncID); } modLog.LogDebug(val); string result = string.Empty; if (AutoCompletePatch.TryStepSelection(__instance.m_command, flag, ref result)) { __instance.m_currentLine = result; __instance.m_command.m_inputBufferStep = 0; } } } } internal const string PluginVersion = "1.1.3"; private Harmony? _harmony; internal static ConfigFile Settings { get; private set; } internal static ManualLogSource ModLog { get; private set; } internal static ConfigEntry<bool> EnableFuzzySearch { get; private set; } internal static ConfigEntry<bool> EnableCommandFuzzySearch { get; private set; } internal static ConfigEntry<bool> EnableIdentifierFuzzySearch { get; private set; } internal static ConfigEntry<AmbiguousMatchMode> AmbiguousMatches { get; private set; } internal static ConfigEntry<int> MaximumLevenshteinDistance { get; private set; } internal static ConfigEntry<bool> LogConsumablePickups { get; private set; } internal static ConfigEntry<bool> EnableConsumableTerminalItems { get; private set; } internal static ConfigEntry<bool> ShowConsumablesInNativeList { get; private set; } internal static ConfigEntry<bool> EnableGlowStickTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableCFoamGrenadeTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableCFoamMineTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableExplosiveMineTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableFogRepellerTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableLockMelterTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableLongRangeFlashlightTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableHealthSyringeTerminalItems { get; private set; } internal static ConfigEntry<bool> EnableBoostSyringeTerminalItems { get; private set; } internal static ConfigEntry<bool> EnablePipelines { get; private set; } internal static ConfigEntry<bool> EnableHistorySearch { get; private set; } internal static ConfigEntry<int> MaximumHistoryEntries { get; private set; } internal static ConfigEntry<int> MaximumVisibleHistoryResults { get; private set; } internal static ConfigEntry<HistoryPersistenceMode> HistoryPersistence { get; private set; } internal static ConfigEntry<string> Language { get; private set; } internal static ConfigEntry<bool> ShowNeofetchOnTerminalStart { get; private set; } internal static ConfigEntry<NeofetchMode> NeofetchDisplayMode { get; private set; } internal static ConfigEntry<bool> EnableIdleScreenImage { get; private set; } internal static ConfigEntry<IdleScreenColorMode> IdleScreenColors { get; private set; } internal static ConfigEntry<int> ImageIntensityPercent { get; private set; } internal static ConfigEntry<int> ImageOpacityPercent { get; private set; } internal static ConfigEntry<int> ImageRotationDegrees { get; private set; } internal static ConfigEntry<bool> ImageFlipHorizontal { get; private set; } internal static ConfigEntry<bool> ImageFlipVertical { get; private set; } internal static ConfigEntry<bool> EnableTerminalBackground { get; private set; } internal static ConfigEntry<string> TerminalTextColor { get; private set; } internal static ConfigEntry<int> TerminalTextIntensityPercent { get; private set; } public override void Load() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown Settings = ((BasePlugin)this).Config; ModLog = ((BasePlugin)this).Log; BindConfiguration(); LocalizationService.Initialize(); HistoryStore.Initialize(); NeofetchService.Initialize(); IdleScreenService.Initialize(); _harmony = new Harmony("BetterTerminal"); _harmony.PatchAll(); ((BasePlugin)this).Log.LogInfo((object)"BetterTerminal loaded"); } private void BindConfiguration() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Expected O, but got Unknown //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Expected O, but got Unknown //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Expected O, but got Unknown //IL_03fa: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Expected O, but got Unknown //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Expected O, but got Unknown Language = ((BasePlugin)this).Config.Bind<string>("Localization", "Language", "en", "JSON language filename without extension. Example: en, es."); EnableFuzzySearch = ((BasePlugin)this).Config.Bind<bool>("Fuzzy Search", "Enabled", true, "Enables or disables all terminal fuzzy search features."); EnableCommandFuzzySearch = ((BasePlugin)this).Config.Bind<bool>("Fuzzy Search", "Commands", true, "Allows autocomplete to correct command names."); EnableIdentifierFuzzySearch = ((BasePlugin)this).Config.Bind<bool>("Fuzzy Search", "Identifiers", true, "Allows autocomplete to correct active identifier words used by LIST, QUERY, PING, MARK, and UNMARK."); AmbiguousMatches = ((BasePlugin)this).Config.Bind<AmbiguousMatchMode>("Fuzzy Search", "AmbiguousMatches", AmbiguousMatchMode.CommonPrefix, "CommonPrefix completes the shared prefix. ArrowSelection lets the user choose with arrow keys."); MaximumLevenshteinDistance = ((BasePlugin)this).Config.Bind<int>("Fuzzy Search", "MaximumLevenshteinDistance", 2, new ConfigDescription("Maximum fuzzy-search distance. Higher values produce less precise matches.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); LogConsumablePickups = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "DiagnosticLogging", false, "Logs uncollected consumable data for diagnostics and type identification."); EnableConsumableTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "Enabled", true, "Enables LIST, QUERY, and PING for uncollected consumables."); ShowConsumablesInNativeList = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "ShowInNativeList", false, "Includes registered consumables in broad native LIST searches such as LIST U. LIST CONSUMABLES and pipelines remain available when disabled."); EnableGlowStickTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "GlowSticks", true, "Registers uncollected glow sticks with terminals."); EnableCFoamGrenadeTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "CFoamGrenades", true, "Registers uncollected C-Foam grenades with terminals."); EnableCFoamMineTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "CFoamMines", true, "Registers uncollected C-Foam tripmines with terminals."); EnableExplosiveMineTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "Mines", true, "Registers uncollected explosive tripmines with terminals."); EnableFogRepellerTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "FogRepellers", true, "Registers uncollected fog repellers with terminals."); EnableLockMelterTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "LockMelters", true, "Registers uncollected lock melters with terminals."); EnableLongRangeFlashlightTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "LongRangeFlashlights", true, "Registers uncollected long-range flashlights with terminals."); EnableHealthSyringeTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "I2LPSyringes", true, "Registers uncollected I2-LP syringes with terminals."); EnableBoostSyringeTerminalItems = ((BasePlugin)this).Config.Bind<bool>("Terminal Items", "IIxSyringes", true, "Registers uncollected IIx syringes with terminals."); EnablePipelines = ((BasePlugin)this).Config.Bind<bool>("Pipelines", "Enabled", true, "Enables typed command pipelines."); EnableHistorySearch = ((BasePlugin)this).Config.Bind<bool>("History", "Enabled", true, "Opens interactive history search when Up is pressed on an empty line."); MaximumHistoryEntries = ((BasePlugin)this).Config.Bind<int>("History", "MaximumEntries", 500, new ConfigDescription("Maximum commands retained during the session.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 5000), Array.Empty<object>())); MaximumVisibleHistoryResults = ((BasePlugin)this).Config.Bind<int>("History", "MaximumVisibleResults", 15, new ConfigDescription("Maximum visible results in history search.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 50), Array.Empty<object>())); HistoryPersistence = ((BasePlugin)this).Config.Bind<HistoryPersistenceMode>("History", "Persistence", HistoryPersistenceMode.Session, "Session keeps commands only until GTFO closes. Disk restores history between sessions."); ShowNeofetchOnTerminalStart = ((BasePlugin)this).Config.Bind<bool>("Neofetch", "ShowOnTerminalStart", true, "Shows neofetch when terminal interaction begins."); NeofetchDisplayMode = ((BasePlugin)this).Config.Bind<NeofetchMode>("Neofetch", "Mode", NeofetchMode.Normal, "Normal uses bundled artwork. Custom reads BepInEx/config/BetterTerminal/neofetch.txt."); EnableIdleScreenImage = ((BasePlugin)this).Config.Bind<bool>("Idle Screen", "Enabled", true, "Shows idle-screen.png on the login display while the terminal is not in use."); IdleScreenColors = ((BasePlugin)this).Config.Bind<IdleScreenColorMode>("Idle Screen", "ColorMode", IdleScreenColorMode.Image, "Image preserves PNG RGB colors. Terminal applies the original GTFO display tint."); ImageIntensityPercent = ((BasePlugin)this).Config.Bind<int>("Terminal Appearance", "ImageIntensityPercent", 35, new ConfigDescription("Image RGB intensity.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); ImageOpacityPercent = ((BasePlugin)this).Config.Bind<int>("Terminal Appearance", "ImageOpacityPercent", 25, new ConfigDescription("Interactive background opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ImageRotationDegrees = ((BasePlugin)this).Config.Bind<int>("Terminal Appearance", "ImageRotationDegrees", 0, "Additional rotation: 0, 90, 180, or 270 degrees."); ImageFlipHorizontal = ((BasePlugin)this).Config.Bind<bool>("Terminal Appearance", "FlipHorizontal", false, "Flips the image horizontally after correcting GTFO display UVs."); ImageFlipVertical = ((BasePlugin)this).Config.Bind<bool>("Terminal Appearance", "FlipVertical", false, "Flips the image vertically after correcting GTFO display UVs."); EnableTerminalBackground = ((BasePlugin)this).Config.Bind<bool>("Terminal Appearance", "InteractiveBackground", false, "Shows idle-screen.png as a background during terminal interaction."); TerminalTextColor = ((BasePlugin)this).Config.Bind<string>("Terminal Appearance", "TextColor", "DEFAULT", "Terminal text color as RGB/RGBA hex, for example #80FFD0. DEFAULT preserves GTFO colors."); TerminalTextIntensityPercent = ((BasePlugin)this).Config.Bind<int>("Terminal Appearance", "TextIntensityPercent", 100, new ConfigDescription("Custom text RGB intensity. Lower values reduce bloom without changing hue.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); } } public enum CommandType { Unknown, Ping, Query, List, Config, Mark, Unmark, Commands, Help, Neofetch, IdleScreen, Where, Sort, Take, Count, Run, Stfo, BetterTerminal, Docs, History } public enum AmbiguousMatchMode { CommonPrefix, ArrowSelection } public enum HelpTopic { BetterTerminal, Search, History, Pipelines, Markers, Consumables, Appearance, Config, Stfo, Docs } public enum BetterTerminalAction { Status } public enum DocsAction { Open } public enum ConfigProfile { Vanilla, Convenient, Full } public enum HistoryPersistenceMode { Session, Disk } public enum HistoryAction { Clear } public enum TerminalItemCategory { All, Consumable, Native } public static class CommandTypeExtensions { public static string Token(this CommandType command) { return command.ToString().ToUpperInvariant(); } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class CommandsListPatch { private static void Postfix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { CommandType result; bool flag = !Enum.TryParse<CommandType>(inputString.Trim().ToUpperInvariant(), ignoreCase: true, out result); if (!flag) { bool flag2 = (uint)(result - 7) <= 1u; flag = !flag2; } if (!flag) { string[] array = ModCommands.BuildCommandsSection().Split('\n'); foreach (string text in array) { __instance.m_screenBuffer.Add(text); } __instance.ResetLinesSinceCommand(); } } internal static IReadOnlyList<string> BuildLines(LG_ComputerTerminalCommandInterpreter interpreter, CommandType commandType) { List<string> list = new List<string>(); if (commandType == CommandType.Help) { list.Add(LocalizationService.Text("help.intro")); list.Add(LocalizationService.Text("help.navigation")); list.Add(string.Empty); } list.Add(LocalizationService.Text("commands.available")); list.Add(string.Empty); list.AddRange(BuildNativeCommands(interpreter)); list.AddRange(ModCommands.BuildCommandsSection().Split('\n')); return list; } private static IEnumerable<string> BuildNativeCommands(LG_ComputerTerminalCommandInterpreter interpreter) { SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase); Enumerator<string, TERM_Command> enumerator = interpreter.m_commandsPerString.Keys.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; sortedSet.Add(current.ToUpperInvariant()); } foreach (string item in sortedSet) { yield return item; } } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class ConfigCommandPatch { internal static readonly string[] AutoCompleteKeys = new string[39] { "RELOAD", "SEARCH", "HISTORY", "ITEMS", "APPEARANCE", "PROFILE", "RESET", "ENABLED", "COMMANDS", "IDENTIFIERS", "GLOWSTICKS", "CFOAMGRENADES", "CFOAMMINES", "MINES", "FOGREPELLERS", "LOCKMELTERS", "FLASHLIGHTS", "I2LPSYRINGES", "IIXSYRINGES", "CONSUMABLESINLIST", "PIPELINES", "HISTORYLIMIT", "HISTORYVISIBLE", "HISTORYMODE", "LANGUAGE", "NEOFETCH", "NEOFETCHMODE", "IDLESCREEN", "IDLECOLORS", "BACKGROUND", "IMAGEINTENSITY", "IMAGEOPACITY", "IMAGEROTATION", "IMAGEFLIPH", "IMAGEFLIPV", "TEXTCOLOR", "TEXTINTENSITY", "DISTANCE", "MODE" }; internal static IEnumerable<string>? AutoCompleteValues(string key) { switch (key) { case "PROFILE": return from name in Enum.GetNames<ConfigProfile>() select name.ToUpperInvariant(); case "RESET": return new string[2] { "ALL", "APPEARANCE" }; case "NEOFETCHMODE": return from name in Enum.GetNames<NeofetchMode>() select name.ToUpperInvariant(); case "IDLECOLORS": return from name in Enum.GetNames<IdleScreenColorMode>() select name.ToUpperInvariant(); case "MODE": return from name in Enum.GetNames<AmbiguousMatchMode>() select name.ToUpperInvariant(); case "HISTORYMODE": return from name in Enum.GetNames<HistoryPersistenceMode>() select name.ToUpperInvariant(); case "IMAGEROTATION": return new string[4] { "0", "90", "180", "270" }; case "TEXTCOLOR": return new string[1] { "DEFAULT" }; case "LANGUAGE": return new string[2] { "EN", "ES" }; case "ENABLED": case "HISTORY": case "ITEMS": case "MINES": case "FOGREPELLERS": case "I2LPSYRINGES": case "BACKGROUND": case "GLOWSTICKS": case "IDLESCREEN": case "IMAGEFLIPH": case "IMAGEFLIPV": case "CFOAMMINES": case "IDENTIFIERS": case "LOCKMELTERS": case "FLASHLIGHTS": case "IIXSYRINGES": case "CFOAMGRENADES": case "PIPELINES": case "COMMANDS": case "NEOFETCH": case "CONSUMABLESINLIST": return new string[2] { "ON", "OFF" }; default: return null; } } private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { string text = inputString.Trim().ToUpperInvariant(); string[] array = text.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0 || !Enum.TryParse<CommandType>(array[0], ignoreCase: true, out var result) || result != CommandType.Config) { return true; } Plugin.AutoCompletePatch.ClearSelection(); WriteImmediate(__instance, text, Execute(array)); return false; } private static void WriteImmediate(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string response) { TerminalCommandOutput.WriteImmediate(interpreter, commandLine, response.Split('\n')); } private static string Execute(string[] arguments) { if (arguments.Length == 1) { return CurrentConfiguration(); } if (arguments.Length == 2 && arguments[1] == "RELOAD") { Plugin.Settings.Reload(); HistoryStore.ReloadForPersistenceChange(); LocalizationService.Reload(); NeofetchService.Reload(); IdleScreenService.Reload(); TerminalAppearanceService.RefreshAll(); ConsumableTerminalRegistry.RefreshAll(); return LocalizationService.Text("config.reloaded") + "\n" + CurrentConfiguration(); } bool flag = arguments.Length == 2; if (flag) { bool flag2; switch (arguments[1]) { case "SEARCH": case "HISTORY": case "ITEMS": case "APPEARANCE": flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { return Section(arguments[1]); } if (arguments.Length == 2 && arguments[1] == "RESET") { return ResetConfiguration("ALL"); } if (arguments.Length == 3 && arguments[1] == "RESET") { return ResetConfiguration(arguments[2]); } if (arguments.Length == 3 && arguments[1] == "PROFILE") { return ApplyProfile(arguments[2]); } if (arguments.Length != 3) { return Help(); } return arguments[1] switch { "ENABLED" => SetBoolean(Plugin.EnableFuzzySearch, arguments[2], "ENABLED"), "COMMANDS" => SetBoolean(Plugin.EnableCommandFuzzySearch, arguments[2], "COMMANDS"), "IDENTIFIERS" => SetBoolean(Plugin.EnableIdentifierFuzzySearch, arguments[2], "IDENTIFIERS"), "ITEMS" => SetTerminalItemBoolean(Plugin.EnableConsumableTerminalItems, arguments[2], "ITEMS"), "GLOWSTICKS" => SetTerminalItemBoolean(Plugin.EnableGlowStickTerminalItems, arguments[2], "GLOWSTICKS"), "CFOAMGRENADES" => SetTerminalItemBoolean(Plugin.EnableCFoamGrenadeTerminalItems, arguments[2], "CFOAMGRENADES"), "CFOAMMINES" => SetTerminalItemBoolean(Plugin.EnableCFoamMineTerminalItems, arguments[2], "CFOAMMINES"), "MINES" => SetTerminalItemBoolean(Plugin.EnableExplosiveMineTerminalItems, arguments[2], "MINES"), "FOGREPELLERS" => SetTerminalItemBoolean(Plugin.EnableFogRepellerTerminalItems, arguments[2], "FOGREPELLERS"), "LOCKMELTERS" => SetTerminalItemBoolean(Plugin.EnableLockMelterTerminalItems, arguments[2], "LOCKMELTERS"), "FLASHLIGHTS" => SetTerminalItemBoolean(Plugin.EnableLongRangeFlashlightTerminalItems, arguments[2], "FLASHLIGHTS"), "I2LPSYRINGES" => SetTerminalItemBoolean(Plugin.EnableHealthSyringeTerminalItems, arguments[2], "I2LPSYRINGES"), "IIXSYRINGES" => SetTerminalItemBoolean(Plugin.EnableBoostSyringeTerminalItems, arguments[2], "IIXSYRINGES"), "CONSUMABLESINLIST" => SetTerminalItemBoolean(Plugin.ShowConsumablesInNativeList, arguments[2], "CONSUMABLESINLIST"), "PIPELINES" => SetBoolean(Plugin.EnablePipelines, arguments[2], "PIPELINES"), "HISTORY" => SetBoolean(Plugin.EnableHistorySearch, arguments[2], "HISTORY"), "HISTORYLIMIT" => SetInteger(Plugin.MaximumHistoryEntries, arguments[2], "HISTORYLIMIT", 10, 5000), "HISTORYVISIBLE" => SetInteger(Plugin.MaximumVisibleHistoryResults, arguments[2], "HISTORYVISIBLE", 3, 50), "HISTORYMODE" => SetHistoryPersistence(arguments[2]), "LANGUAGE" => SetLanguage(arguments[2]), "NEOFETCH" => SetBoolean(Plugin.ShowNeofetchOnTerminalStart, arguments[2], "NEOFETCH"), "NEOFETCHMODE" => SetNeofetchMode(arguments[2]), "IDLESCREEN" => SetIdleScreen(arguments[2]), "IDLECOLORS" => SetIdleScreenColorMode(arguments[2]), "BACKGROUND" => SetAppearanceBoolean(Plugin.EnableTerminalBackground, arguments[2], "BACKGROUND"), "IMAGEFLIPH" => SetAppearanceBoolean(Plugin.ImageFlipHorizontal, arguments[2], "IMAGEFLIPH"), "IMAGEFLIPV" => SetAppearanceBoolean(Plugin.ImageFlipVertical, arguments[2], "IMAGEFLIPV"), "IMAGEINTENSITY" => SetAppearanceInteger(Plugin.ImageIntensityPercent, arguments[2], "IMAGEINTENSITY", 1, 100), "IMAGEOPACITY" => SetAppearanceInteger(Plugin.ImageOpacityPercent, arguments[2], "IMAGEOPACITY", 0, 100), "IMAGEROTATION" => SetImageRotation(arguments[2]), "TEXTCOLOR" => SetTextColor(arguments[2]), "TEXTINTENSITY" => SetAppearanceInteger(Plugin.TerminalTextIntensityPercent, arguments[2], "TEXTINTENSITY", 1, 100), "DISTANCE" => SetDistance(arguments[2]), "MODE" => SetMode(arguments[2]), _ => Help(), }; } private static string SetTerminalItemBoolean(ConfigEntry<bool> entry, string value, string key) { string result = SetBoolean(entry, value, key); if ((value == "ON" || value == "OFF") ? true : false) { ConsumableTerminalRegistry.RefreshAll(); } return result; } private static string SetBoolean(ConfigEntry<bool> entry, string value, string key) { if ((!(value == "ON") && !(value == "OFF")) || 1 == 0) { return "INVALID VALUE FOR " + key + ". USE ON OR OFF."; } entry.Value = value == "ON"; return key + ": " + value; } private static string SetDistance(string value) { int result; bool flag = !int.TryParse(value, out result); if (!flag) { bool flag2 = ((result < 1 || result > 10) ? true : false); flag = flag2; } if (flag) { return "INVALID DISTANCE. USE A VALUE FROM 1 TO 10."; } Plugin.MaximumLevenshteinDistance.Value = result; return $"MAXIMUM LEVENSHTEIN DISTANCE: {result}"; } private static string SetInteger(ConfigEntry<int> entry, string value, string key, int minimum, int maximum) { if (int.TryParse(value, out var result) && result >= minimum && result <= maximum) { entry.Value = result; return $"{key}: {result}"; } return $"INVALID {key}. USE A VALUE FROM {minimum} TO {maximum}."; } private static string SetMode(string value) { if (!Enum.TryParse<AmbiguousMatchMode>(value, ignoreCase: true, out var result)) { return "INVALID MODE. USE COMMONPREFIX OR ARROWSELECTION."; } Plugin.AmbiguousMatches.Value = result; return "AMBIGUOUS MATCHES: " + result.ToString().ToUpperInvariant(); } private static string SetHistoryPersistence(string value) { if (!Enum.TryParse<HistoryPersistenceMode>(value, ignoreCase: true, out var result)) { return LocalizationService.Text("history.mode_invalid"); } Plugin.HistoryPersistence.Value = result; HistoryStore.ReloadForPersistenceChange(); return LocalizationService.Text("history.mode_changed", result.ToString().ToUpperInvariant()); } private static string SetLanguage(string value) { if (!LocalizationService.TrySetLanguage(value)) { return LocalizationService.Text("config.language_missing", value.ToUpperInvariant()); } return LocalizationService.Text("config.language_changed", value.ToUpperInvariant()); } private static string SetNeofetchMode(string value) { if (!Enum.TryParse<NeofetchMode>(value, ignoreCase: true, out var result)) { return "INVALID NEOFETCH MODE. USE NORMAL OR CUSTOM."; } Plugin.NeofetchDisplayMode.Value = result; NeofetchService.Reload(); return "NEOFETCH MODE: " + result.ToString().ToUpperInvariant(); } private static string SetIdleScreen(string value) { string result = SetBoolean(Plugin.EnableIdleScreenImage, value, "IDLESCREEN"); IdleScreenService.RefreshAll(); return result; } private static string SetIdleScreenColorMode(string value) { if (!Enum.TryParse<IdleScreenColorMode>(value, ignoreCase: true, out var result)) { return LocalizationService.Text("config.idle_colors_invalid"); } Plugin.IdleScreenColors.Value = result; IdleScreenService.Reload(); return LocalizationService.Text("config.idle_colors_changed", result.ToString().ToUpperInvariant()); } private static string SetAppearanceBoolean(ConfigEntry<bool> entry, string value, string key) { string result = SetBoolean(entry, value, key); if ((value == "ON" || value == "OFF") ? true : false) { RefreshAppearance(); } return result; } private static string SetAppearanceInteger(ConfigEntry<int> entry, string value, string key, int minimum, int maximum) { string result = SetInteger(entry, value, key, minimum, maximum); if (int.TryParse(value, out var result2) && result2 >= minimum && result2 <= maximum) { RefreshAppearance(); } return result; } private static string SetImageRotation(string value) { int result; bool flag = !int.TryParse(value, out result); if (!flag) { bool flag2; switch (result) { case 0: case 90: case 180: case 270: flag2 = true; break; default: flag2 = false; break; } flag = !flag2; } if (flag) { return LocalizationService.Text("config.image_rotation_invalid"); } Plugin.ImageRotationDegrees.Value = result; RefreshAppearance(); return $"IMAGE ROTATION: {result}"; } private static string SetTextColor(string value) { Color val = default(Color); if (!value.Equals("DEFAULT", StringComparison.OrdinalIgnoreCase) && !ColorUtility.TryParseHtmlString(value.StartsWith('#') ? value : ("#" + value), ref val)) { return LocalizationService.Text("config.text_color_invalid"); } Plugin.TerminalTextColor.Value = value; TerminalAppearanceService.RefreshAll(); return "TEXT COLOR: " + value.ToUpperInvariant(); } private static string Section(string section) { return section switch { "SEARCH" => string.Join("\n", $"ENABLED: {Plugin.EnableFuzzySearch.Value}", $"COMMANDS: {Plugin.EnableCommandFuzzySearch.Value}", $"IDENTIFIERS: {Plugin.EnableIdentifierFuzzySearch.Value}", $"MODE: {Plugin.AmbiguousMatches.Value}", $"DISTANCE: {Plugin.MaximumLevenshteinDistance.Value}"), "HISTORY" => string.Join("\n", $"ENABLED: {Plugin.EnableHistorySearch.Value}", "MODE: " + Plugin.HistoryPersistence.Value.ToString().ToUpperInvariant(), $"LIMIT: {Plugin.MaximumHistoryEntries.Value}", $"VISIBLE RESULTS: {Plugin.MaximumVisibleHistoryResults.Value}"), "ITEMS" => string.Join("\n", $"ENABLED: {Plugin.EnableConsumableTerminalItems.Value}", $"GLOW STICKS: {Plugin.EnableGlowStickTerminalItems.Value}", $"C-FOAM GRENADES: {Plugin.EnableCFoamGrenadeTerminalItems.Value}", $"C-FOAM MINES: {Plugin.EnableCFoamMineTerminalItems.Value}", $"EXPLOSIVE MINES: {Plugin.EnableExplosiveMineTerminalItems.Value}", $"FOG REPELLERS: {Plugin.EnableFogRepellerTerminalItems.Value}", $"LOCK MELTERS: {Plugin.EnableLockMelterTerminalItems.Value}", $"FLASHLIGHTS: {Plugin.EnableLongRangeFlashlightTerminalItems.Value}", $"CONSUMABLES IN NATIVE LIST: {Plugin.ShowConsumablesInNativeList.Value}"), "APPEARANCE" => string.Join("\n", $"NEOFETCH: {Plugin.ShowNeofetchOnTerminalStart.Value}", $"IDLE SCREEN: {Plugin.EnableIdleScreenImage.Value}", $"BACKGROUND: {Plugin.EnableTerminalBackground.Value}", "TEXT COLOR: " + Plugin.TerminalTextColor.Value, $"TEXT INTENSITY: {Plugin.TerminalTextIntensityPercent.Value}%"), _ => Help(), }; } private static string ApplyProfile(string profile) { bool flag; switch (profile) { case "VANILLA": case "CONVENIENT": case "FULL": flag = true; break; default: flag = false; break; } if (!flag) { return "INVALID PROFILE. USE VANILLA, CONVENIENT OR FULL."; } flag = ((profile == "CONVENIENT" || profile == "FULL") ? true : false); bool value = flag; bool value2 = profile == "FULL"; Plugin.EnableFuzzySearch.Value = true; Plugin.EnableCommandFuzzySearch.Value = true; Plugin.EnableIdentifierFuzzySearch.Value = true; Plugin.EnableHistorySearch.Value = true; Plugin.EnablePipelines.Value = value; Plugin.EnableConsumableTerminalItems.Value = value; Plugin.ShowNeofetchOnTerminalStart.Value = value2; Plugin.EnableIdleScreenImage.Value = value2; Plugin.EnableTerminalBackground.Value = false; Plugin.Settings.Save(); ConsumableTerminalRegistry.RefreshAll(); RefreshAppearance(); return "PROFILE APPLIED: " + profile; } private static string ResetConfiguration(string section) { string normalized = section.ToUpperInvariant(); IEnumerable<ConfigEntryBase> enumerable = from pair in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)Plugin.Settings where normalized == "ALL" || pair.Key.Section.Equals(normalized, StringComparison.OrdinalIgnoreCase) || (normalized == "APPEARANCE" && pair.Key.Section == "Terminal Appearance") select pair.Value; int num = 0; foreach (ConfigEntryBase item in enumerable) { item.BoxedValue = item.DefaultValue; num++; } Plugin.Settings.Save(); LocalizationService.Reload(); ConsumableTerminalRegistry.RefreshAll(); RefreshAppearance(); if (num != 0) { return $"CONFIGURATION RESET: {normalized} ({num} SETTINGS)"; } return "UNKNOWN CONFIGURATION SECTION."; } private static void RefreshAppearance() { IdleScreenService.Reload(); TerminalAppearanceService.RefreshAll(); } private static string CurrentConfiguration() { return string.Join("\n", LocalizationService.Text("config.header"), "LANGUAGE: " + Plugin.Language.Value.ToUpperInvariant(), "ENABLED: " + Plugin.EnableFuzzySearch.Value.ToString().ToUpperInvariant(), "COMMANDS: " + Plugin.EnableCommandFuzzySearch.Value.ToString().ToUpperInvariant(), "IDENTIFIERS: " + Plugin.EnableIdentifierFuzzySearch.Value.ToString().ToUpperInvariant(), "AMBIGUOUS MATCHES: " + Plugin.AmbiguousMatches.Value.ToString().ToUpperInvariant(), $"MAXIMUM LEVENSHTEIN DISTANCE: {Plugin.MaximumLevenshteinDistance.Value}", "TERMINAL ITEMS: " + Plugin.EnableConsumableTerminalItems.Value.ToString().ToUpperInvariant(), "GLOW STICKS: " + Plugin.EnableGlowStickTerminalItems.Value.ToString().ToUpperInvariant(), "C-FOAM GRENADES: " + Plugin.EnableCFoamGrenadeTerminalItems.Value.ToString().ToUpperInvariant(), "C-FOAM MINES: " + Plugin.EnableCFoamMineTerminalItems.Value.ToString().ToUpperInvariant(), "MINES: " + Plugin.EnableExplosiveMineTerminalItems.Value.ToString().ToUpperInvariant(), "FOG REPELLERS: " + Plugin.EnableFogRepellerTerminalItems.Value.ToString().ToUpperInvariant(), "LOCK MELTERS: " + Plugin.EnableLockMelterTerminalItems.Value.ToString().ToUpperInvariant(), "LONG RANGE FLASHLIGHTS: " + Plugin.EnableLongRangeFlashlightTerminalItems.Value.ToString().ToUpperInvariant(), "I2-LP SYRINGES: " + Plugin.EnableHealthSyringeTerminalItems.Value.ToString().ToUpperInvariant(), "IIX SYRINGES: " + Plugin.EnableBoostSyringeTerminalItems.Value.ToString().ToUpperInvariant(), "CONSUMABLES IN NATIVE LIST: " + Plugin.ShowConsumablesInNativeList.Value.ToString().ToUpperInvariant(), "PIPELINES: " + Plugin.EnablePipelines.Value.ToString().ToUpperInvariant(), "HISTORY SEARCH: " + Plugin.EnableHistorySearch.Value.ToString().ToUpperInvariant(), "HISTORY MODE: " + Plugin.HistoryPersistence.Value.ToString().ToUpperInvariant(), $"HISTORY LIMIT: {Plugin.MaximumHistoryEntries.Value}", $"HISTORY VISIBLE RESULTS: {Plugin.MaximumVisibleHistoryResults.Value}", "NEOFETCH ON TERMINAL START: " + Plugin.ShowNeofetchOnTerminalStart.Value.ToString().ToUpperInvariant(), "NEOFETCH MODE: " + Plugin.NeofetchDisplayMode.Value.ToString().ToUpperInvariant(), "NEOFETCH NORMAL FILE: " + NeofetchService.DefaultFilePath, "NEOFETCH CUSTOM FILE: " + NeofetchService.FilePath, "IDLE SCREEN: " + Plugin.EnableIdleScreenImage.Value.ToString().ToUpperInvariant(), LocalizationService.Text("config.idle_colors_value", Plugin.IdleScreenColors.Value.ToString().ToUpperInvariant()), "IDLE SCREEN FILE: " + IdleScreenService.FilePath, "INTERACTIVE BACKGROUND: " + Plugin.EnableTerminalBackground.Value.ToString().ToUpperInvariant(), $"IMAGE INTENSITY: {Plugin.ImageIntensityPercent.Value}%", $"IMAGE OPACITY: {Plugin.ImageOpacityPercent.Value}%", $"IMAGE ROTATION: {Plugin.ImageRotationDegrees.Value}", "IMAGE FLIP HORIZONTAL: " + Plugin.ImageFlipHorizontal.Value.ToString().ToUpperInvariant(), "IMAGE FLIP VERTICAL: " + Plugin.ImageFlipVertical.Value.ToString().ToUpperInvariant(), "TEXT COLOR: " + Plugin.TerminalTextColor.Value.ToUpperInvariant(), $"TEXT INTENSITY: {Plugin.TerminalTextIntensityPercent.Value}%"); } private static string Help() { return string.Join("\n", LocalizationService.Text("config.help_header"), "CONFIG", "CONFIG RELOAD", "CONFIG SEARCH|HISTORY|ITEMS|APPEARANCE", "CONFIG PROFILE VANILLA|CONVENIENT|FULL", "CONFIG RESET [APPEARANCE|ALL]", "CONFIG ENABLED ON|OFF", "CONFIG COMMANDS ON|OFF", "CONFIG IDENTIFIERS ON|OFF", "CONFIG ITEMS ON|OFF", "CONFIG GLOWSTICKS ON|OFF", "CONFIG CFOAMGRENADES ON|OFF", "CONFIG CFOAMMINES ON|OFF", "CONFIG MINES ON|OFF", "CONFIG FOGREPELLERS ON|OFF", "CONFIG LOCKMELTERS ON|OFF", "CONFIG FLASHLIGHTS ON|OFF", "CONFIG I2LPSYRINGES ON|OFF", "CONFIG IIXSYRINGES ON|OFF", "CONFIG CONSUMABLESINLIST ON|OFF", "CONFIG PIPELINES ON|OFF", "CONFIG HISTORY ON|OFF", "CONFIG HISTORYMODE SESSION|DISK", "HISTORY CLEAR", "CONFIG HISTORYLIMIT 10-5000", "CONFIG HISTORYVISIBLE 3-50", "CONFIG LANGUAGE NAME", "CONFIG NEOFETCH ON|OFF", "CONFIG NEOFETCHMODE NORMAL|CUSTOM", "CONFIG IDLESCREEN ON|OFF", "CONFIG IDLECOLORS IMAGE|TERMINAL", "CONFIG BACKGROUND ON|OFF", "CONFIG IMAGEINTENSITY 1-100", "CONFIG IMAGEOPACITY 0-100", "CONFIG IMAGEROTATION 0|90|180|270", "CONFIG IMAGEFLIPH ON|OFF", "CONFIG IMAGEFLIPV ON|OFF", "CONFIG TEXTCOLOR DEFAULT|RRGGBB|RRGGBBAA", "CONFIG TEXTINTENSITY 1-100", "CONFIG MODE COMMONPREFIX|ARROWSELECTION", "CONFIG DISTANCE 1-10"); } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class HelpAndDocsCommandPatch { internal const string DocumentationUrl = "https://alexandermakunin.github.io/BetterTerminal/"; private static readonly HashSet<string> Topics = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "BETTERTERMINAL", "SEARCH", "HISTORY", "PIPELINES", "MARKERS", "CONSUMABLES", "APPEARANCE", "CONFIG", "STFO", "DOCS" }; [HarmonyPriority(800)] private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown string[] array = inputString.Trim().ToUpperInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return true; } if (array[0] == CommandType.Help.Token() && array.Length == 2 && Topics.Contains(array[1])) { Write(__instance, inputString, HelpTopic(array[1])); return false; } if (array[0] == CommandType.BetterTerminal.Token()) { Write(__instance, inputString, (array.Length == 2 && array[1] == "STATUS") ? Status() : HelpTopic("BETTERTERMINAL")); return false; } if (array[0] != CommandType.Docs.Token()) { return true; } List<string> list = new List<string>(); list.Add(LocalizationService.Text("docs.url", "https://alexandermakunin.github.io/BetterTerminal/")); List<string> list2 = list; if (array.Length == 2 && array[1] == "OPEN") { try { Process.Start(new ProcessStartInfo("https://alexandermakunin.github.io/BetterTerminal/") { UseShellExecute = true }); list2.Add(LocalizationService.Text("docs.opened")); } catch (Exception ex) { ManualLogSource modLog = Plugin.ModLog; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(20, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DOCS] OPEN FAILED: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } modLog.LogWarning(val); list2.Add(LocalizationService.Text("docs.open_failed")); } } else if (array.Length > 1) { list2.Add(LocalizationService.Text("docs.usage")); } Write(__instance, inputString, list2); return false; } private static IEnumerable<string> HelpTopic(string topic) { return topic switch { "SEARCH" => Lines("help.search"), "HISTORY" => Lines("help.history"), "PIPELINES" => Lines("help.pipelines"), "MARKERS" => Lines("help.markers"), "CONSUMABLES" => Lines("help.consumables"), "APPEARANCE" => Lines("help.appearance"), "CONFIG" => Lines("help.config"), "STFO" => Lines("help.stfo"), "DOCS" => Lines("help.docs"), _ => Lines("help.betterterminal"), }; } private static IEnumerable<string> Lines(string key) { return LocalizationService.Text(key).Split('\n'); } private static IEnumerable<string> Status() { bool stfoInstalled = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly assembly) => assembly.GetName().Name?.Equals("STFO", StringComparison.OrdinalIgnoreCase) ?? false); yield return "BETTERTERMINAL 1.1.3"; yield return "LANGUAGE: " + Plugin.Language.Value.ToUpperInvariant(); yield return $"FUZZY SEARCH: {Plugin.EnableFuzzySearch.Value}"; yield return $"PIPELINES: {Plugin.EnablePipelines.Value}"; yield return $"HISTORY: {Plugin.EnableHistorySearch.Value}"; yield return $"REGISTERED CONSUMABLES: {ConsumableTerminalRegistry.RegisteredCount}"; yield return $"ACTIVE MAP MARKERS: {ConsumableTerminalRegistry.ActiveMapMarkerCount}"; yield return "STFO: " + (stfoInstalled ? "INSTALLED" : "NOT INSTALLED"); yield return "CONFIG: " + Plugin.Settings.ConfigFilePath; yield return "LOCALIZATION: " + LocalizationService.DirectoryPath; yield return "DOCS: https://alexandermakunin.github.io/BetterTerminal/"; } private static void Write(LG_ComputerTerminalCommandInterpreter interpreter, string input, IEnumerable<string> lines) { TerminalCommandOutput.WriteImmediate(interpreter, input.Trim().ToUpperInvariant(), lines); } } internal sealed class HistorySearchSession { internal string Query { get; set; } = string.Empty; internal int SelectedIndex { get; set; } internal List<string> Matches { get; } = new List<string>(); internal List<string> RenderedLines { get; } = new List<string>(); } internal static class HistorySearchManager { private static readonly Dictionary<uint, HistorySearchSession> Sessions = new Dictionary<uint, HistorySearchSession>(); internal static bool IsActive(uint terminalId) { return Sessions.ContainsKey(terminalId); } internal static bool TryOpen(LG_ComputerTerminal terminal) { if (!string.IsNullOrEmpty(terminal.m_currentLine) || HistoryStore.GetRecent().Count == 0) { return false; } Sessions[terminal.SyncID] = new HistorySearchSession(); terminal.m_command.ShowInputLine = false; terminal.m_command.ShowCustomLine = true; terminal.m_command.CustomLineBlinkEnabled = true; Refresh(terminal); return true; } internal static bool HandleUpdate(LG_ComputerTerminal terminal) { if (!Sessions.TryGetValue(terminal.SyncID, out HistorySearchSession value)) { return false; } if (InputMapper.GetButtonDown.Invoke((InputAction)20, (eFocusState)8)) { Close(terminal, null); return true; } bool flag = InputMapper.GetButtonDown.Invoke((InputAction)15, (eFocusState)8); bool flag2 = InputMapper.GetButtonDown.Invoke((InputAction)16, (eFocusState)8); if (flag || flag2) { if (value.Matches.Count > 0) { value.SelectedIndex = (value.SelectedIndex + ((!flag) ? 1 : (-1)) + value.Matches.Count) % value.Matches.Count; Render(terminal, value); } return true; } string inputString = Input.inputString; if (string.IsNullOrEmpty(inputString)) { return false; } string text = inputString; foreach (char c in text) { bool flag3; switch (c) { case '\b': if (value.Query.Length > 0) { HistorySearchSession historySearchSession = value; string query = value.Query; historySearchSession.Query = query.Substring(0, query.Length - 1); } continue; case '\n': case '\r': flag3 = true; break; default: flag3 = false; break; } if (flag3) { string selected = ((value.Matches.Count == 0) ? null : value.Matches[value.SelectedIndex]); Close(terminal, selected); return true; } if (!char.IsControl(c)) { value.Query += char.ToUpperInvariant(c); } } Refresh(terminal); return true; } private static void Refresh(LG_ComputerTerminal terminal) { HistorySearchSession session = Sessions[terminal.SyncID]; session.Matches.Clear(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] source = session.Query.Split(' ', StringSplitOptions.RemoveEmptyEntries); IReadOnlyList<string> recent = HistoryStore.GetRecent(); for (int num = recent.Count - 1; num >= 0; num--) { string command = recent[num]; if (command.Length != 0 && hashSet.Add(command) && !source.Any((string term) => !command.Contains(term, StringComparison.OrdinalIgnoreCase))) { session.Matches.Add(command); } } session.Matches.Sort(delegate(string left, string right) { bool flag = left.StartsWith(session.Query, StringComparison.OrdinalIgnoreCase); bool flag2 = right.StartsWith(session.Query, StringComparison.OrdinalIgnoreCase); return (flag != flag2) ? ((!flag) ? 1 : (-1)) : 0; }); session.SelectedIndex = Math.Clamp(session.SelectedIndex, 0, Math.Max(0, session.Matches.Count - 1)); Render(terminal, session); } private static void Render(LG_ComputerTerminal terminal, HistorySearchSession session) { RemoveRenderedLines(terminal.m_command, session); session.RenderedLines.Add(LocalizationService.Text("history.title")); int num = Math.Max(1, Plugin.MaximumVisibleHistoryResults.Value); int num2 = Math.Max(0, session.SelectedIndex - num + 1); int num3 = Math.Min(session.Matches.Count, num2 + num); for (int i = num2; i < num3; i++) { string text = ((i == session.SelectedIndex) ? "->" : " "); session.RenderedLines.Add(text + " " + session.Matches[i]); } if (session.Matches.Count == 0) { session.RenderedLines.Add(LocalizationService.Text("history.no_matches")); } foreach (string renderedLine in session.RenderedLines) { terminal.m_command.m_screenBuffer.Add(renderedLine); } terminal.m_command.CustomLineText = "HISTORY> " + session.Query; terminal.m_command.ResetLinesSinceCommand(); } private static void Close(LG_ComputerTerminal terminal, string? selected) { if (Sessions.Remove(terminal.SyncID, out HistorySearchSession value)) { RemoveRenderedLines(terminal.m_command, value); terminal.m_command.ShowCustomLine = false; terminal.m_command.CustomLineBlinkEnabled = false; terminal.m_command.CustomLineText = string.Empty; terminal.m_command.ShowInputLine = true; terminal.m_currentLine = selected ?? string.Empty; terminal.m_command.m_inputBufferStep = 0; } } private static void RemoveRenderedLines(LG_ComputerTerminalCommandInterpreter interpreter, HistorySearchSession session) { int num = interpreter.m_screenBuffer.Count - session.RenderedLines.Count; bool flag = num >= 0; int num2 = 0; while (flag && num2 < session.RenderedLines.Count) { flag = string.Equals(interpreter.m_screenBuffer[num + num2], session.RenderedLines[num2], StringComparison.Ordinal); num2++; } if (flag && session.RenderedLines.Count > 0) { interpreter.m_screenBuffer.RemoveRange(num, session.RenderedLines.Count); } else if (session.RenderedLines.Count > 0) { Plugin.ModLog.LogWarning((object)"[HISTORY] SCREEN BUFFER CHANGED; TEMPORARY HISTORY BLOCK WAS NOT REMOVED."); } session.RenderedLines.Clear(); } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class HistoryRecorderPatch { private static void Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { if (Plugin.EnableHistorySearch.Value && !HistoryStore.RecordingSuppressed && __instance.m_terminal != null && !TerminalInputReservationRegistry.IsReserved(__instance.m_terminal.SyncID)) { string[] array = inputString.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length != 2 || !Enum.TryParse<CommandType>(array[0], ignoreCase: true, out var result) || result != CommandType.History || !Enum.TryParse<HistoryAction>(array[1], ignoreCase: true, out var result2) || result2 != HistoryAction.Clear) { HistoryStore.Record(inputString); } } } } internal static class HistoryStore { private static readonly List<string> Entries = new List<string>(); private static string FilePath => Path.Combine(Paths.ConfigPath, "BetterTerminal", "history.txt"); internal static bool RecordingSuppressed { get; set; } internal static void Initialize() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown Entries.Clear(); if (Plugin.HistoryPersistence.Value == HistoryPersistenceMode.Session) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); if (File.Exists(FilePath)) { Entries.AddRange((from command in File.ReadLines(FilePath) select command.Trim().ToUpperInvariant() into command where command.Length > 0 select command).TakeLast(Plugin.MaximumHistoryEntries.Value)); } } catch (Exception ex) { ManualLogSource modLog = Plugin.ModLog; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HISTORY] COULD NOT LOAD HISTORY: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } modLog.LogWarning(val); } } internal static IReadOnlyList<string> GetRecent() { return Entries; } internal static void Record(string command) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown string text = command.Trim().ToUpperInvariant(); if (text.Length == 0) { return; } Entries.Add(text); int num = Entries.Count - Plugin.MaximumHistoryEntries.Value; if (num > 0) { Entries.RemoveRange(0, num); } if (Plugin.HistoryPersistence.Value != HistoryPersistenceMode.Disk) { return; } try { File.WriteAllLines(FilePath, Entries); } catch (Exception ex) { ManualLogSource modLog = Plugin.ModLog; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HISTORY] COULD NOT SAVE HISTORY: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } modLog.LogWarning(val); } } internal static void Clear() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Entries.Clear(); try { if (File.Exists(FilePath)) { File.Delete(FilePath); } } catch (Exception ex) { ManualLogSource modLog = Plugin.ModLog; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HISTORY] COULD NOT DELETE HISTORY: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message); } modLog.LogWarning(val); } } internal static void ReloadForPersistenceChange() { Initialize(); } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class HistoryCommandPatch { private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { string[] array = inputString.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0 || !Enum.TryParse<CommandType>(array[0], ignoreCase: true, out var result) || result != CommandType.History) { return true; } string text; if (array.Length == 2 && Enum.TryParse<HistoryAction>(array[1], ignoreCase: true, out var result2) && result2 == HistoryAction.Clear) { HistoryStore.Clear(); text = LocalizationService.Text("history.cleared"); } else { text = LocalizationService.Text("history.usage"); } TerminalCommandOutput.WriteImmediate(__instance, inputString.Trim(), new string[1] { text }); return false; } } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] internal static class LimitedListPatch { private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown string text = inputString.Trim().ToUpperInvariant(); string[] array = text.Split(' ', StringSplitOptions.RemoveEmptyEntries); CommandType result = default(CommandType); bool flag = array.Length != 3 || !Enum.TryParse<CommandType>(array[0], ignoreCase: true, out result); if (!flag) { bool flag2 = (uint)(result - 2) <= 1u; flag = !flag2; } if (flag) { return true; } if (!int.TryParse(array[2], out var result2) || result2 <= 0) { return true; } List<TerminalItemMatch> list = TerminalItemSearch.Find(array[1]); int num = Math.Min(result2, list.Count); BepInExDebugLogInterpolatedStringHandler val; if (result == CommandType.Query) { ManualLogSource modLog = Plugin.ModLog; val = new BepInExDebugLogInterpolatedStringHandler(40, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[QUERY LIMIT] TYPE="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(array[1]); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" REQUESTED="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(result2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" RETURNED="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num); } modLog.LogDebug(val); if (num == 0) { WriteNoResults(__instance, text, array[1]); return false; } for (int i = 0; i < num; i++) { TerminalCommandOutput.EvaluateNativeWithoutHistory(__instance, CommandType.Query.Token() + " " + list[i].Identifier); } __instance.m_inputBuffer.Add(text); __instance.m_inputBufferStep = 0; return false; } __instance.m_inputBuffer.Add(text); __instance.m_inputBufferStep = 0; __instance.m_screenBuffer.Add(text); __instance.m_screenBuffer.Add(LocalizationService.Text("items.first_ids", num, array[1])); for (int j = 0; j < num; j++) { __instance.m_screenBuffer.Add(list[j].Identifier); } if (num == 0) { __instance.m_screenBuffer.Add(LocalizationService.Text("items.none", array[1])); } if (__instance.m_terminal != null) { __instance.m_terminal.m_currentLine = string.Empty; } __instance.ResetLinesSinceCommand(); ManualLogSource modLog2 = Plugin.ModLog; val = new BepInExDebugLogInterpolatedStringHandler(39, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[LIST LIMIT] TYPE="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(array[1]); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" REQUESTED="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(result2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" RETURNED="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num); } modLog2.LogDebug(val); return false; } private static void WriteNoResults(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string itemType) { interpreter.m_inputBuffer.Add(commandLine); interpreter.m_inputBufferStep = 0; interpreter.m_screenBuffer.Add(commandLine); interpreter.m_screenBuffer.Add(LocalizationService.Text("items.none", itemType)); if (interpreter.m_terminal != null) { interpreter.m_terminal.m_currentLine = string.Empty; } interpreter.ResetLinesSinceCommand(); } } internal sealed record ModCommand(string Name, string DescriptionKey); internal static class ModCommands { private static readonly List<ModCommand> All = new List<ModCommand> { new ModCommand(CommandType.Config.Token(), "commands.config"), new ModCommand(CommandType.History.Token(), "commands.history_command"), new ModCommand(CommandType.Neofetch.Token(), "commands.neofetch"), new ModCommand(CommandType.IdleScreen.Token(), "commands.idlescreen"), new ModCommand(CommandType.Mark.Token(), "commands.mark"), new ModCommand(CommandType.Unmark.Token(), "commands.unmark"), new ModCommand(CommandType.BetterTerminal.Token(), "commands.betterterminal"), new ModCommand(CommandType.Docs.Token(), "commands.docs") }; internal static IEnumerable<string> Names => All.Select((ModCommand command) => command.Name); internal static void Register(string name, string description) { if (!All.Any((ModCommand command) => command.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) { All.Add(new ModCommand(name.ToUpperInvariant(), description.ToUpperInvariant())); } } internal static string BuildCommandsSection() { List<string> list = new List<string> { string.Empty, "---------------- BETTERTERMINAL ----------------" }; foreach (ModCommand item in All) { list.Add($"{item.Name,-16} {LocalizationService.Text(item.DescriptionKey)}"); } list.Add(CommandType.Query.Token() + " TYPE_MIN-MAX " + LocalizationService.Text("commands.query_range")); list.Add(CommandType.Query.Token() + " TYPE COUNT " + LocalizationService.Text("commands.query_count")); list.Add(CommandType.List.Token() + " TYPE_MIN-MAX " + LocalizationService.Text("commands.list_range")); list.Add(CommandType.List.Token() + " TYPE COUNT " + LocalizationService.Text("commands.list_count")); list.Add($"{CommandType.List.Token()} | {CommandType.Where.Token()} FIELD VALUE | {CommandType.Sort.Token()} FIELD | {CommandType.Take.Token()} N"); list.Add(LocalizationService.Text("commands.pipeline_outputs")); list.Add("UP ON EMPTY LINE " + LocalizationService.Text("commands.history")); list.Add(LocalizationService.Text("commands.config_hint")); list.Add("------------------------------------------------"); return string.Join("\n", list); } } internal enum PipelineStage { Where, Sort, Take, Count, Query, Ping, Mark, Unmark } internal enum PipelineField { Text, Id, Type, Zone, Area, Status } internal sealed record PipelineItem(string Id, string Type, string Zone, string Area, string Status, iTerminalItem Item); internal enum PipelineDataType { Items, Text } [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")] [HarmonyPriority(800)] internal static class PipelineCommandPatch { private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown if (!Plugin.EnablePipelines.Value || !inputString.Contains('|')) { return true; } if (!CanHandle(inputString)) { return true; } int count = __instance.m_screenBuffer.Count; try { return Execute(__instance, inputString); } catch (Exception ex) { int num = __instance.m_screenBuffer.Count - count; if (num > 0) { __instance.m_screenBuffer.RemoveRange(count, num); } ManualLogSource modLog = Plugin.ModLog; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(59, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[PIPELINE] FAILED BEFORE COMPLETION; FALLING BACK TO GTFO: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex); } modLog.LogError(val); return true; } } private static bool Execute(LG_ComputerTerminalCommandInterpreter __instance, string inputString) { string text = inputString.Trim().ToUpperInvariant(); string[] array = text.Split('|', StringSplitOptions.TrimEntries); if (array.Length < 2) { WriteError(__instance, text, LocalizationService.Text("pipeline.must_start_list")); return false; } if (!TryCreateSource(__instance, array[0], out PipelineDataType dataType, out List<PipelineItem> items, out List<string> textLines, out string error)) { WriteError(__instance, text, error); return false; } bool flag = false; List<string> list = new List<string>(); for (int i = 1; i < array.Length; i++) { string[] array2 = array[i].Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 0) { continue; } if (!Enum.TryParse<PipelineStage>(array2[0], ignoreCase: true, out var result)) { if (PipelineExtensionRegistry.TryExecute(array2[0], array2, items, out PipelineExtensionResult result2)) { list.AddRange(result2.OutputLines); if (!result2.Succeeded) { TerminalCommandOutput.WriteImmediate(__instance, text, list); return false; } items = result2.Items; continue; } WriteError(__instance, text, LocalizationService.Text("pipeline.unknown_stage", array2[0])); return false; } switch (result) { case PipelineStage.Where: if (dataType == PipelineDataType.Text) { if (!TryApplyTextWhere(textLines, array[i], out textLines, out error)) { WriteError(__instance, text, error); return false; } } else if (!TryApplyWhere(items, array[i], out items, out error)) { WriteError(__instance, text, error); return false; } break; case PipelineStage.Sort: if (dataType == PipelineDataType.Text) { textLines = ((array2.Length == 2 && array2[1].Equals("DESC", StringComparison.OrdinalIgnoreCase)) ? textLines.OrderByDescending<string, string>((string line) => line, StringComparer.OrdinalIgnoreCase).ToList() : textLines.OrderBy<string, string>((string line) => line, StringComparer.OrdinalIgnoreCase).ToList()); } else if (!TryApplySort(items, array2, out items, out error)) { WriteError(__instance, text, error); return false; } break; case PipelineStage.Take: { if (array2.Length != 2 || !int.TryParse(array2[1], out var result3) || result3 < 0) { WriteError(__instance, text, "USE: TAKE N"); return false; } if (dataType == PipelineDataType.Text) { textLines = textLines.Take(result3).ToList(); } else { items = items.Take(result3).ToList(); } break; } case PipelineStage.Count: if (i != array.Length - 1) { WriteError(__instance, text, LocalizationService.Text("pipeline.count_last")); return false; } TerminalCommandOutput.WriteImmediate(__instance, text, new string[1] { $"COUNT: {((dataType == PipelineDataType.Text) ? textLines.Count : items.Count)}" }); return false; case PipelineStage.Query: if (dataType != PipelineDataType.Items) { WriteError(__instance, text, "QUERY REQUIRES AN ITEM STREAM."); return false; } if (i != array.Length - 1) { WriteError(__instance, text, LocalizationService.Text("pipeline.query_last")); return false; } flag = true; break; case PipelineStage.Ping: { if (dataType != PipelineDataType.Items) { WriteError(__instance, text, "PING REQUIRES AN ITEM STREAM."); return false; } if (i != array.Length - 1 || items.Count != 1) { WriteError(__instance, text, LocalizationService.Text("pipeline.ping_single")); return false; } if (!ConsumableTerminalRegistry.TryPlayPing(items[0].Id, out string message)) { items[0].Item.PlayPing(); message = LocalizationService.Text("pipeline.ping_started", items[0].Id); } TerminalCommandOutput.WriteImmediate(__instance, text, new string[1] { message }); return false; } case PipelineStage.Mark: { if (dataType != PipelineDataType.Items) { WriteError(__instance, text, "MARK REQUIRES AN ITEM STREAM."); return false; } if (i != array.Length - 1) { WriteError(__instance, text, LocalizationService.Text("pipeline.mark_last")); return false; } List<string> list2 = new List<string>(); foreach (PipelineItem item in items) { if (ConsumableTerminalRegistry.TrySetMapMarker(item.Id, item.Item, out string message2, replaceExisting: false)) { list2.Add(item.Id); } else { list.Add(message2); } } list.Add(LocalizationService.Text("pipeline.marked", list2.Count)); if (list2.Count > 1) { object[] array3 = new object[1]; array3[0] = list2[list2.Count - 1]; list.Add(LocalizationService.Text("pipeline.last_ping", array3)); } TerminalCommandOutput.WriteImmediate(__instance, text, list); return false; } case PipelineStage.Unmark: if (dataType != PipelineDataType.Items) { WriteError(__instance, text, "UNMARK REQUIRES AN ITEM STREAM."); return false; } if (i != array.Length - 1) { WriteError(__instance, text, LocalizationService.Text("pipeline.unmark_last")); return false; } foreach (PipelineItem item2 in items) { list.Add(ConsumableTerminalRegistry.ClearMapMarker(item2.Id)); } TerminalCommandOutput.WriteImmediate(__instance, text, list); return false; } } object obj; if (dataType != PipelineDataType.Text) { obj = (flag ? BuildQueryLines(__instance, items) : BuildListLines(items)); } else { IEnumerable<string> enumerable = textLines; obj = enumerable; } IEnumerable<string> second = (IEnumerable<string>)obj; TerminalCommandOutput.WriteImmediate(__instance, text, list.Concat(second)); return false; } private static bool CanHandle(string inputString) { string[] array = inputString.Split('|', StringSplitOptions.TrimEntries); if (array.Length < 2 || array.Any(string.IsNullOrWhiteSpace)) { return false; } string[] array2 = array[0].Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (array2.Length == 0 || !Enum.TryParse<CommandType>(array2[0], ignoreCase: true, out var result) || result == CommandType.Unknown) { return false; } for (int i = 1; i < array.Length; i++) { string text = array[i].Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)[0]; if (!Enum.TryParse<PipelineStage>(text, ignoreCase: true, out var _) && !PipelineExtensionRegistry.IsRegistered(text)) { return false; } } return true; } private static bool TryCreateSource(LG_ComputerTerminalCommandInterpreter interpreter, string source, out PipelineDataType dataType, out List<PipelineItem> items, out List<string> textLines, out string error) { //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) dataType = PipelineDataType.Items; items = new List<PipelineItem>(); textLines = new List<string>(); error = string.Empty; string[] array = source.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0 || !Enum.TryParse<CommandType>(array[0], ignoreCase: true, out var result)) { error = LocalizationService.Text("pipeline.unknown_stage", array.FirstOrDefault() ?? string.Empty); return false; } if ((uint)(result - 7) <= 1u) { if (array.Length != 1) { error = "USE: " + result.ToString().ToUpperInvariant(); return false; } dataType = PipelineDataType.Text; textLines = CommandsListPatch.BuildLines(interpreter, result).ToList(); return true; } if (result != CommandType.List) { int count = interpreter.m_screenBuffer.Count; TerminalCommandOutput.EvaluateNativeWithoutHistory(interpreter, source); int num = interpreter.m_screenBuffer.Count - count; if (num > 0) { for (int i = count; i < interpreter.m_screenBuffer.Count; i++) { textLines.Add(interpreter.m_screenBuffer[i]); } interpreter.m_screenBuffer.RemoveRange(count, num); } dataType = PipelineDataType.Text; return true; } TerminalItemCategory terminalItemCategory = TerminalItemCategory.All; List<string> list = new List<string>(); foreach (string item in array.Skip(1)) { if (TryParseCategory(item, out var category)) { terminalItemCategory = category; } else { list.Add(item); } } ConsumableTerminalRegistry.RefreshAll(); LG_LevelInteractionManager current2 = LG_LevelInteractionManager.Current; Dictionary<string, iTerminalItem> val = ((current2 != null) ? current2.m_terminalItemsByKeyString : null); if (val == null) { error = LocalizationService.Text("pipeline.registry_unavailable"); return false; } Enumerator<string, iTerminalItem> enumerator2 = val.Keys.GetEnumerator(); while (enumerator2.MoveNext()) { string current3 = enumerator2.Current; string normalizedId = current3.ToUpperInvariant(); bool flag = ConsumableTerminalRegistry.IsConsumableIdentifier(normalizedId); if ((terminalItemCategory != TerminalItemCategory.Consumable || flag) && !(terminalItemCategory == TerminalItemCategory.Native && flag) && !list.Any((string filter) => !normalizedId.Contains(filter, StringComparison.OrdinalIgnoreCase))) { iTerminalItem val2 = val[current3]; AIG_CourseNode spawnNode = val2.SpawnNode; string zone = ((spawnNode == null) ? "UNKNOWN" : spawnNode.m_zone.Alias.ToString()); object area; if (spawnNode == null) { area = null; } else { LG_Area area2 = spawnNode.m_area; area = ((area2 != null) ? area2.m_geoArea : null); } string area3 = NormalizeArea((string?)area); items.Add(new PipelineItem(normalizedId, GetTypeFromId(normalizedId), zone, area3, ((object)val2.FloorItemStatus/*cast due to .constrained prefix*/).ToString().ToUpperInvariant(), val2)); } } items.Sort((PipelineItem left, PipelineItem right) => StringComparer.Ordinal.Compare(left.Id, right.Id)); return true; } private static bool TryApplyTextWhere(List<string> source, string stage, out List<string> result, out string error) { result = source; error = string.Empty; string text = stage.Substring(5).Trim(); string text2 = PipelineField.Text.ToString(); if (!text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { error = "USE: WHERE TEXT VALUE, WHERE TEXT_VALUE OR WHERE TEXT-VALUE"; return false; } string text3 = text.Substring(text2.Length); bool flag = text3.Length < 2; if (!flag) { char c = text3[0]; bool flag2 = ((c == ' ' || c == '-' || c == '_') ? true : false); flag = !flag2; } if (flag) { error = "USE: WHERE TEXT VALUE, WHERE TEXT_VALUE OR WHERE TEXT-VALUE"; return false; } string value = text3.Substring(1).Trim().Trim('"'); if (value.Length == 0) { error = "WHERE TEXT REQUIRES A VALUE."; return false; } result = source.Where((string line) => line.Contains(value, StringComparison.OrdinalIgnoreCase)).ToList(); return true; } private static bool TryApplyWhere(List<PipelineItem> source, string stage, out List<PipelineItem> result, out string error) { result = source; error = string.Empty; if (!TryParseWhereExpression(stage.Substring(5).Trim(), out PipelineField field, out string value)) { error = "USE: WHERE FIELD VALUE, WHERE FIELD_VALUE OR WHERE FIELD-VALUE"; return false; } result = source.Where((PipelineItem item) => GetField(item, field).Contains(value, StringComparison.OrdinalIgnoreCase)).ToList(); return true; } private static bool TryApplySort(List<PipelineItem> source, string[] arguments, out List<PipelineItem> result, out string error) { result = source; error = string.Empty; int num = arguments.Length; bool flag = ((num < 2 || num > 3) ? true : false); if (flag || !Enum.TryParse<PipelineField>(arguments[1], ignoreCase: true, out var field)) { error = "USE: SORT ID|TYPE|ZONE|AREA|STATUS [DESC]"; return false; } bool flag2 = arguments.Length == 3 && arguments[2] == "DESC"; if (arguments.Length == 3 && !flag2) { error = "SORT DIRECTION MUST BE DESC."; return false; } if (field == PipelineField.Zone) { result = (flag2 ? source.OrderByDescending((PipelineItem item) => ParseZone(item.Zone)).ToList() : source.OrderBy((PipelineItem item) => ParseZone(item.Zone)).ToList()); } else { result = (flag2 ? source.OrderByDescending<PipelineItem, string>((PipelineItem item) => GetField(item, field), StringComparer.OrdinalIgnoreCase).ToList() : source.OrderBy<PipelineItem, string>((PipelineItem item) => GetField(item, field), StringComparer.OrdinalIgnoreCase).ToList()); } return true; } private static IEnumerable<string> BuildListLines(List<PipelineItem> items) { yield return $"{"ID",-30} {"TYPE",-24} {"ZONE",-8} {"AREA",-6} STATUS"; foreach (PipelineItem item in items) { yield return $"{item.Id,-30} {item.Type,-24} {item.Zone,-8} {item.Area,-6} {item.Status}"; } yield return $"COUNT: {items.Count}"; } private static bool TryParseWhereExpression(string expression, out PipelineField field, out string value) { field = PipelineField.Text; value = string.Empty; PipelineField[] values = Enum.GetValues<PipelineField>(); for (int i = 0; i < values.Length; i++) { PipelineField pipelineField = values[i]; string text = pipelineField.ToString().ToUpperInvariant(); if (expression.StartsWith(text, StringComparison.Ordinal)) { string text2 = expression.Substring(text.Length); bool flag = text2.Length == 0; if (!flag) { char c = text2[0]; bool flag2 = ((c == ' ' || c == '-' || c == '_') ? true : false); flag = !flag2; } if (!flag) { field = pipelineField; value = text2.Substring(1).Trim().Trim('"') .Replace(' ', '_'); return value.Length > 0; } } } return false; } private static int ParseZone(string zone) { if (!int.TryParse(zone, out var result)) { return int.MaxValue; } return result; } private static string NormalizeArea(string? area) { if (string.IsNullOrWhiteSpace(area)) { return "UNKNOWN"; } string text = area.Trim().ToUpperInvariant(); if (text.StartsWith("AREA ", StringComparison.Ordinal)) { text = text.Substring(5); } return text.Replace(' ', '_'); } private static IEnumerable<string> BuildQueryLines(LG_ComputerTerminalCommandInterpreter interpreter, List<PipelineItem> items) { foreach (PipelineItem item in items) { if (ConsumableTerminalRegistry.TryGetQueryLines(item.Id, out IReadOnlyList<string> lines)) { foreach (string item2 in lines) { yield return item2; } yield return string.Empty; continue; } foreach (string item3 in CaptureNativeQuery(interpreter, item)) { yield return item3; } yield return string.Empty; } } private static IEnumerable<string> CaptureNativeQuery(LG_ComputerTerminalCommandInterpreter interpreter, PipelineItem item) { int count = interpreter.m_screenBuffer.Count; TerminalCommandOutput.EvaluateNativeWithoutHistory(interpreter, CommandType.Query.Token() + " " + item.Id); int num = interpreter.m_screenBuffer.Count - count; if (num > 0) { string[] array = new string[num]; for (int i = 0; i < num; i++) { array[i] = interpreter.m_screenBuffer[count + i]; } interpreter.m_screenBuffer.RemoveRange(count, num); string[] array2 = array; for (int j = 0; j < array2.Length; j++) { yield return array2[j]; } } else { yield return "ID: " + item.Id; yield return "TYPE: " + item.Type; yield return "LOCATION: ZONE_" + item.Zone + " AREA_" + item.Area; yield return "STATUS: " + item.Status; } } private static bool TryParseCategory(string token, out TerminalItemCategory category) { if (Enum.TryParse<TerminalItemCategory>(token.Trim().TrimEnd('S'), ignoreCase: true, out category) && category != TerminalItemCategory.All) { return true; } category = TerminalItemCategory.All; return false; } private static string GetTypeFromId(string id) { int num = id.LastIndexOf('_'); if (num <= 0 || !int.TryParse(id.Substring(num + 1), out var _)) { return id; } return id.Substring(0, num); } private static string GetField(PipelineItem item, PipelineField field) { return field s