Decompiled source of GovernmentTerminal v0.6.10

BepInEx/plugins/GovernmentTerminal.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GovernmentTerminal")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.6.9.0")]
[assembly: AssemblyInformationalVersion("0.6.9+0a581adfabc2f8489e595075d1a671771681d055")]
[assembly: AssemblyProduct("GovernmentTerminal")]
[assembly: AssemblyTitle("GovernmentTerminal")]
[assembly: AssemblyVersion("0.6.9.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GovernmentTerminal
{
	[HarmonyPatch(typeof(DesktopIconController), "Setup")]
	internal static class DesktopIconPatch
	{
		private static Sprite? sprite;

		[HarmonyPostfix]
		private static void Postfix(DesktopIconController __instance, CruncherAppPreset __1)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			ConfigEntry<bool>? enabled = Plugin.Enabled;
			if (enabled == null || !enabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__instance.icon == (Object)null)
			{
				return;
			}
			try
			{
				if (IsGovernmentDatabase(__1))
				{
					Sprite val = Load();
					if (!((Object)(object)val == (Object)null))
					{
						__instance.icon.sprite = val;
						__instance.icon.preserveAspect = true;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(35, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Terminal desktop icon not applied: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val2);
				}
			}
		}

		private static bool IsGovernmentDatabase(CruncherAppPreset? preset)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected I4, but got Unknown
			if ((SoCustomComparison)(object)preset == (SoCustomComparison)null || preset.appContent == null)
			{
				return false;
			}
			Enumerator<GameObject> enumerator = preset.appContent.GetEnumerator();
			while (enumerator.MoveNext())
			{
				GameObject current = enumerator.Current;
				if (!((Object)(object)current == (Object)null))
				{
					DatabaseApp componentInChildren = current.GetComponentInChildren<DatabaseApp>(true);
					if ((Object)(object)componentInChildren != (Object)null && TerminalCommands.ReplacesPool((int)componentInChildren.citizenPool))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static Sprite? Load()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sprite != (Object)null)
			{
				return sprite;
			}
			using Stream stream = typeof(DesktopIconPatch).Assembly.GetManifestResourceStream("GovernmentTerminal.TerminalIcon.png");
			if (stream == null)
			{
				ManualLogSource? logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)"Terminal icon resource missing.");
				}
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1
			};
			if (!ImageConversion.LoadImage(val, new Il2CppStructArray<byte>(memoryStream.ToArray()), false))
			{
				return null;
			}
			((Object)val).hideFlags = (HideFlags)32;
			sprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)sprite).hideFlags = (HideFlags)32;
			return sprite;
		}
	}
	[BepInPlugin("chris.governmentterminal", "Government Terminal", "0.6.7")]
	public sealed class Plugin : BasePlugin
	{
		public const string Id = "chris.governmentterminal";

		internal static ManualLogSource? Logger;

		internal static ConfigEntry<bool>? Enabled;

		private Harmony? harmony;

		public override void Load()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Replace only the Government Database with a keyboard terminal. F8 restores its original interface for the current window.");
			harmony = new Harmony("chris.governmentterminal");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BasePlugin)this).Log.LogInfo((object)"Government Terminal 0.6.7 loaded; full-display terminal compiled against supplied 42.04 interop.");
		}

		public override bool Unload()
		{
			TerminalWindow.RestoreAll();
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(DatabaseApp), "OnSetup")]
	internal static class SetupPatch
	{
		[HarmonyPostfix]
		private static void Postfix(DatabaseApp __instance)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected I4, but got Unknown
			ConfigEntry<bool>? enabled = Plugin.Enabled;
			if (enabled == null || !enabled.Value || !TerminalCommands.ReplacesPool((int)__instance.citizenPool))
			{
				return;
			}
			try
			{
				TerminalWindow.Attach(__instance);
			}
			catch (Exception ex)
			{
				TerminalWindow.Restore(__instance);
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(51, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Terminal setup failed; original database retained: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
					}
					logger.LogError(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(DatabaseApp), "Update")]
	internal static class UpdatePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(DatabaseApp __instance)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			TerminalWindow terminalWindow = TerminalWindow.Find(__instance);
			if (terminalWindow == null)
			{
				return true;
			}
			ConfigEntry<bool>? enabled = Plugin.Enabled;
			if (enabled == null || !enabled.Value)
			{
				TerminalWindow.Restore(__instance);
				return true;
			}
			try
			{
				terminalWindow.Tick();
				return TerminalWindow.Find(__instance) == null;
			}
			catch (Exception ex)
			{
				TerminalWindow.Restore(__instance);
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(45, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Terminal failed; original database restored: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
					}
					logger.LogError(val);
				}
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(DatabaseApp), "OnDestroy")]
	internal static class DestroyPatch
	{
		[HarmonyPrefix]
		private static void Prefix(DatabaseApp __instance)
		{
			TerminalWindow.Forget(__instance);
		}
	}
	[HarmonyPatch(typeof(DatabaseApp), "KeyboardButton")]
	internal static class KeyboardPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(DatabaseApp __instance, string __0)
		{
			TerminalWindow terminalWindow = TerminalWindow.Find(__instance);
			if (terminalWindow == null)
			{
				return true;
			}
			terminalWindow.Type(__0);
			return false;
		}
	}
	[HarmonyPatch(typeof(DatabaseApp), "BackspaceButton")]
	internal static class BackspacePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(DatabaseApp __instance)
		{
			TerminalWindow terminalWindow = TerminalWindow.Find(__instance);
			if (terminalWindow == null)
			{
				return true;
			}
			terminalWindow.Backspace();
			return false;
		}
	}
	[HarmonyPatch(typeof(BioScreenController), "SelectSlot")]
	internal static class SlotHotkeyPatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !TerminalWindow.CapturingInput();
		}
	}
	[HarmonyPatch(typeof(SessionData), "PauseGame")]
	internal static class CaseBoardHotkeyPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(bool openDesktopMode)
		{
			if (openDesktopMode)
			{
				return !TerminalWindow.TypingAtScreen();
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(BioScreenController), "SetInventoryOpen")]
	internal static class WeaponWheelHotkeyPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(bool val)
		{
			if (val)
			{
				return !TerminalWindow.TypingAtScreen();
			}
			return true;
		}
	}
	public readonly record struct TerminalCommand(string Verb, string Argument);
	public static class TerminalCommands
	{
		public const int MaximumCommandLength = 120;

		public const int ResultsPerPage = 10;

		public const int Columns = 46;

		public const int Rows = 15;

		public const int VisibleLines = 12;

		public const int ListingLines = 12;

		public const int MaximumListingLines = 21;

		public static readonly string[] Help = new string[11]
		{
			"HELP          List commands", "SEARCH <name> Citizen data", "SHOWALL       All citizens A-Z", "SIFT <clues>  e.g. sift eyes green shoe 12", "SIFTHELP      Sift filter list", "OPEN <n>      Open a record", "PRINT         Prints the record", "NEXT/PREV     Page results", "CLEAR/CLS     Clear screen", "QUIT/Q        Close database",
			"OLDDB/F8      Original window"
		};

		public static readonly string[] SiftHelp = new string[15]
		{
			"SIFT <filter> <value> [filter value ...]", "age         30", "gender      m / f / nb", "height      tall / medium / short", "build       skinny / average / large", "hair        bald / short / long; brown", "eyes        blue / green / brown ...", "shoe        12", "glasses     yes / no", "facialhair  yes / no",
			"bloodtype   a / a+ / o ...", "job title   \"police officer\"", "salary      30000 Crows (¢r)", "Numbers: 12 / 20-30 / >40 ", "Combine: SIFT eyes green, shoes 12"
		};

		public static readonly IReadOnlyDictionary<string, string> FilterFields = new Dictionary<string, string>
		{
			["eyes"] = "eyes",
			["eye"] = "eyes",
			["shoe"] = "shoe",
			["shoes"] = "shoe",
			["blood"] = "blood",
			["gender"] = "gender",
			["sex"] = "gender",
			["height"] = "height",
			["hair"] = "hair",
			["build"] = "build",
			["age"] = "age",
			["job"] = "job",
			["work"] = "job",
			["address"] = "address",
			["home"] = "address",
			["name"] = "name",
			["initial"] = "initials",
			["initials"] = "initials",
			["bloodtype"] = "blood",
			["title"] = "job",
			["jobtitle"] = "job",
			["glasses"] = "glasses",
			["facialhair"] = "facialhair",
			["beard"] = "facialhair",
			["salary"] = "salary",
			["pay"] = "salary"
		};

		public static bool ReplacesPool(int pool)
		{
			return pool == 0;
		}

		public static int ListingPages(int count, int lines)
		{
			int num = Math.Max(1, lines);
			return Math.Max(1, (count + num - 1) / num);
		}

		public static IEnumerable<string> Banner(string? city, string title = "GOVERNMENT DATABASE")
		{
			string text = (city ?? string.Empty).Trim();
			if (text.Length == 0)
			{
				yield return title;
				yield break;
			}
			text = text.ToUpperInvariant();
			if ((text + " " + title).Length <= 46)
			{
				yield return text + " " + title;
				yield break;
			}
			yield return text;
			yield return title;
		}

		public static TerminalCommand Parse(string line)
		{
			line = (line ?? string.Empty).Trim();
			int num = line.IndexOf(' ');
			TerminalCommand terminalCommand;
			if (num >= 0)
			{
				string verb = line.Substring(0, num).ToLowerInvariant();
				string text = line;
				int num2 = num + 1;
				terminalCommand = new TerminalCommand(verb, text.Substring(num2, text.Length - num2).Trim().Trim('"'));
			}
			else
			{
				terminalCommand = new TerminalCommand(line.ToLowerInvariant(), string.Empty);
			}
			TerminalCommand terminalCommand2 = terminalCommand;
			return terminalCommand2 with
			{
				Verb = terminalCommand2.Verb switch
				{
					"find" => "sift", 
					"filter" => "sift", 
					"close" => "quit", 
					"exit" => "quit", 
					"q" => "quit", 
					"cls" => "clear", 
					"vanilla" => "olddb", 
					_ => terminalCommand2.Verb, 
				}
			};
		}

		public static int AutoOpenIndex(IReadOnlyList<string> names, string query)
		{
			if (names.Count == 1)
			{
				return 0;
			}
			int num = -1;
			for (int i = 0; i < names.Count; i++)
			{
				if (string.Equals(names[i].Trim(), query.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					if (num >= 0)
					{
						return -1;
					}
					num = i;
				}
			}
			return num;
		}

		public static string PrintIdentifier(int loop)
		{
			if (loop < 0)
			{
				return "Unknown";
			}
			if (loop < 26)
			{
				return ((char)(65 + loop)).ToString();
			}
			int num = loop - 26;
			int num2 = num / 26;
			if (num2 <= 25)
			{
				return $"{(ushort)(65 + num2)}{(ushort)(65 + num % 26)}";
			}
			return loop.ToString();
		}

		public static string BloodType(string value)
		{
			return value switch
			{
				"Apos" => "A+", 
				"Aneg" => "A-", 
				"Bpos" => "B+", 
				"Bneg" => "B-", 
				"ABpos" => "AB+", 
				"ABneg" => "AB-", 
				"Opos" => "O+", 
				"Oneg" => "O-", 
				_ => "Unknown", 
			};
		}

		public static string Label(string value, string suffix = "")
		{
			if (suffix.Length > 0 && value.EndsWith(suffix, StringComparison.OrdinalIgnoreCase) && value.Length > suffix.Length)
			{
				string text = value;
				int length = suffix.Length;
				value = text.Substring(0, text.Length - length);
			}
			if (value.Length > 1 && value[0] == 'h' && char.IsUpper(value[1]))
			{
				string text = value;
				value = text.Substring(1, text.Length - 1);
			}
			StringBuilder stringBuilder = new StringBuilder();
			char value2;
			for (int i = 0; i < value.Length; stringBuilder.Append(value2), i++)
			{
				char c = value[i];
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(value[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				if (stringBuilder.Length != 0)
				{
					if (stringBuilder[stringBuilder.Length - 1] != ' ')
					{
						value2 = c;
						continue;
					}
				}
				value2 = char.ToUpperInvariant(c);
			}
			return stringBuilder.ToString();
		}

		public static List<string> Tokenize(string text)
		{
			List<string> tokens = new List<string>();
			StringBuilder sb = new StringBuilder();
			bool flag = false;
			bool wasQuoted = false;
			string text2 = text ?? "";
			foreach (char c in text2)
			{
				if (c == '"' || c == '“' || c == '”')
				{
					if (flag)
					{
						flag = false;
						continue;
					}
					Flush();
					flag = true;
					wasQuoted = true;
				}
				else if (!flag && (c == ' ' || c == ','))
				{
					Flush();
				}
				else
				{
					sb.Append(c);
				}
			}
			Flush();
			return tokens;
			void Flush()
			{
				if (sb.Length > 0 || wasQuoted)
				{
					tokens.Add(wasQuoted ? ("\"" + sb?.ToString() + "\"") : sb.ToString());
				}
				sb.Clear();
				wasQuoted = false;
			}
		}

		public static List<(string Field, string Value)>? ParseFilters(string argument, out string error)
		{
			List<(string, string)> list = new List<(string, string)>();
			error = "";
			foreach (string item in Tokenize(argument ?? ""))
			{
				bool num = item.StartsWith("\"");
				string text = (num ? item.Trim('"') : item);
				string text2 = text.ToLowerInvariant();
				if (!num && FilterFields.TryGetValue(text2, out string value))
				{
					if (list.Count > 0)
					{
						if (list[list.Count - 1].Item1 == value)
						{
							if (list[list.Count - 1].Item2.Length == 0)
							{
								continue;
							}
						}
					}
					list.Add((value, ""));
				}
				else if (text.Length != 0 && ((!(text2 == "and") && !(text2 == "&")) || 1 == 0))
				{
					if (list.Count == 0)
					{
						error = "Unknown filter '" + text + "'.";
						return null;
					}
					(string, string) tuple = list[list.Count - 1];
					list[list.Count - 1] = (tuple.Item1, (tuple.Item2.Length == 0) ? text : (tuple.Item2 + " " + text));
				}
			}
			if (list.Count == 0)
			{
				error = "No filters given.";
				return null;
			}
			foreach (var item2 in list)
			{
				if (item2.Item2.Length == 0)
				{
					error = "Missing value for '" + item2.Item1 + "'.";
					return null;
				}
			}
			return list;
		}

		public static bool Matches(IReadOnlyDictionary<string, string> p, string field, string value)
		{
			switch (field)
			{
			case "shoe":
			case "age":
				return NumberMatches(Get(field).Replace(",", ""), value.Replace(",", ""));
			case "salary":
				return NumberMatches(Get(field).Replace(",", ""), value.Replace(",", "").Replace("¢r", "", StringComparison.OrdinalIgnoreCase).Replace("crows", "", StringComparison.OrdinalIgnoreCase)
					.Trim());
			case "height":
			{
				if (NumberMatches(Get("height"), value))
				{
					return true;
				}
				if (IsNumeric(value))
				{
					return false;
				}
				string text5 = value.ToLowerInvariant().Trim();
				bool flag;
				switch (text5)
				{
				case "medium":
				case "average":
				case "avg":
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (flag)
				{
					text5 = "average";
				}
				return WordsMatch(Get("heightclass"), text5);
			}
			case "gender":
			{
				string text6 = Squash(value).Replace("-", "");
				string text7 = Squash(Get("gender")).Replace("-", "");
				string text4;
				switch (text6)
				{
				case "m":
					text4 = "male";
					break;
				case "f":
					text4 = "female";
					break;
				case "nb":
				case "n":
				case "enby":
				case "nonbinary":
					text4 = "nonbinary";
					break;
				default:
					text4 = text6;
					break;
				}
				text6 = text4;
				return text7 == text6;
			}
			case "build":
			{
				string text8 = value.ToLowerInvariant().Trim();
				string text9 = Get("build").ToLowerInvariant();
				bool flag;
				switch (text8)
				{
				case "large":
				case "big":
				case "heavy":
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (flag)
				{
					if (text9 == "overweight" || text9 == "muscular")
					{
						return true;
					}
					return false;
				}
				if ((text8 == "thin" || text8 == "slim") ? true : false)
				{
					text8 = "skinny";
				}
				if (text8 == "medium")
				{
					text8 = "average";
				}
				return text9 == text8;
			}
			case "hair":
			{
				string[] words = (Get("hair") + " " + Get("hairstyle")).ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries);
				return value.ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(delegate(string w)
				{
					bool flag5;
					switch (w)
					{
					case "none":
					case "nohair":
					case "no":
					case "shaved":
						flag5 = true;
						break;
					default:
						flag5 = false;
						break;
					}
					return (!flag5) ? w : "bald";
				})
					.All((string w) => words.Contains(w));
			}
			case "glasses":
			case "facialhair":
			{
				string text10 = value.ToLowerInvariant().Trim();
				bool flag;
				switch (text10)
				{
				case "yes":
				case "y":
				case "true":
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				bool? flag2;
				if (flag)
				{
					flag2 = true;
				}
				else
				{
					bool flag3;
					switch (text10)
					{
					case "no":
					case "n":
					case "none":
					case "false":
						flag3 = true;
						break;
					default:
						flag3 = false;
						break;
					}
					flag2 = (flag3 ? new bool?(false) : ((bool?)null));
				}
				bool? flag4 = flag2;
				if (flag4.HasValue)
				{
					return Get(field) == "yes" == flag4.Value;
				}
				return false;
			}
			case "blood":
			{
				string text11 = Squash(value.ToLowerInvariant().Replace("positive", "+").Replace("negative", "-")
					.Replace("pos", "+")
					.Replace("neg", "-"));
				string text12 = Squash(Get("blood"));
				if (text12.Length == 0)
				{
					return false;
				}
				if (!text11.EndsWith("+") && !text11.EndsWith("-"))
				{
					return text12.TrimEnd('+', '-') == text11;
				}
				return text12 == text11;
			}
			case "name":
			case "job":
			case "address":
				return Get(field).IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
			case "initials":
			{
				string text = Squash(value).Replace(".", "");
				string text2 = Squash(Get("first"));
				string text3 = Squash(Get("last"));
				if (text.Length == 1)
				{
					if (!text2.StartsWith(text))
					{
						return text3.StartsWith(text);
					}
					return true;
				}
				if (text.Length == 2 && text2.StartsWith(text.Substring(0, 1)))
				{
					string text4 = text;
					return text3.StartsWith(text4.Substring(1, text4.Length - 1));
				}
				return false;
			}
			default:
				return WordsMatch(Get(field), value);
			}
			string Get(string key)
			{
				if (!p.TryGetValue(key, out string value2) || value2 == null)
				{
					return "";
				}
				return value2;
			}
		}

		private static string Squash(string s)
		{
			return s.Replace(" ", "").ToLowerInvariant();
		}

		private static bool IsNumeric(string s)
		{
			if (!double.TryParse(s.Trim().TrimStart('<', '>', '='), NumberStyles.Float, CultureInfo.InvariantCulture, out var _))
			{
				double result2;
				if (s.Contains('-'))
				{
					return s.Split('-').All((string x) => double.TryParse(x, NumberStyles.Float, CultureInfo.InvariantCulture, out result2));
				}
				return false;
			}
			return true;
		}

		private static bool WordsMatch(string label, string value)
		{
			string[] words = label.ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries);
			return value.ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries).All((string w) => words.Contains(w));
		}

		public static bool NumberMatches(string actual, string query)
		{
			CultureInfo inv = CultureInfo.InvariantCulture;
			NumberStyles style = NumberStyles.Float;
			if (!double.TryParse(actual, style, inv, out var result))
			{
				return false;
			}
			query = query.Replace(" ", "");
			if (query.StartsWith(">="))
			{
				string text = query;
				if (Num(text.Substring(2, text.Length - 2), out var n))
				{
					return result >= n;
				}
			}
			if (query.StartsWith("<="))
			{
				string text = query;
				if (Num(text.Substring(2, text.Length - 2), out var n2))
				{
					return result <= n2;
				}
			}
			if (query.StartsWith(">"))
			{
				string text = query;
				if (Num(text.Substring(1, text.Length - 1), out var n3))
				{
					return result > n3;
				}
			}
			if (query.StartsWith("<"))
			{
				string text = query;
				if (Num(text.Substring(1, text.Length - 1), out var n4))
				{
					return result < n4;
				}
			}
			int num = query.IndexOf('-', 1);
			if (num > 0 && Num(query.Substring(0, num), out var n5))
			{
				string text = query;
				int num2 = num + 1;
				if (Num(text.Substring(num2, text.Length - num2), out var n6))
				{
					if (result >= Math.Min(n5, n6))
					{
						return result <= Math.Max(n5, n6);
					}
					return false;
				}
			}
			if (Num(query, out var n7))
			{
				return Math.Abs(result - n7) < 0.01;
			}
			return false;
			bool Num(string s, out double result2)
			{
				return double.TryParse(s, style, inv, out result2);
			}
		}

		public static int PageCount(int count)
		{
			return Math.Max(1, (count + 10 - 1) / 10);
		}

		public static IEnumerable<string> Wrap(string text, int width)
		{
			if (width < 1)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			string[] array = text.Replace("\r", "").Replace("\t", "    ").Split('\n');
			foreach (string text2 in array)
			{
				string line = text2;
				while (line.Length > width)
				{
					int cut = line.LastIndexOf(' ', width - 1, width);
					if (cut < 1)
					{
						cut = width;
					}
					yield return line.Substring(0, cut);
					string text3 = line;
					int num = cut;
					line = text3.Substring(num, text3.Length - num).TrimStart();
				}
				yield return line;
			}
		}
	}
	internal sealed class TerminalWindow
	{
		private const int Columns = 46;

		private const int Rows = 15;

		private static readonly Dictionary<int, TerminalWindow> Windows = new Dictionary<int, TerminalWindow>();

		private readonly DatabaseApp app;

		private readonly List<(GameObject Object, bool Active)> originals = new List<(GameObject, bool)>();

		private readonly List<string> output = new List<string>();

		private readonly List<string> history = new List<string>();

		private readonly List<Human> matches = new List<Human>();

		private GameObject? panel;

		private TextMeshProUGUI? text;

		private string buffer = "";

		private string draft = "";

		private int historyIndex;

		private int page;

		private int? siftHelpPage;

		private int scroll;

		private int listingLines = 12;

		private Human? selected;

		private bool cursor;

		private bool closing;

		private static readonly DataKey[] RevealedKeys;

		private TerminalWindow(DatabaseApp app)
		{
			this.app = app;
		}

		internal static TerminalWindow? Find(DatabaseApp app)
		{
			if (!Windows.TryGetValue(((Object)app).GetInstanceID(), out TerminalWindow value))
			{
				return null;
			}
			return value;
		}

		internal static void Attach(DatabaseApp app)
		{
			if (Find(app) == null)
			{
				TerminalWindow terminalWindow = new TerminalWindow(app);
				Windows.Add(((Object)app).GetInstanceID(), terminalWindow);
				terminalWindow.CreateScreen();
			}
		}

		private void CreateScreen()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = ((Component)app).GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null || (Object)(object)app.searchText == (Object)null)
			{
				throw new InvalidOperationException("Database screen or font unavailable.");
			}
			ComputerController controller = ((CruncherAppContent)app).controller;
			object obj;
			if (controller == null)
			{
				obj = null;
			}
			else
			{
				Canvas osCanvas = controller.osCanvas;
				obj = ((osCanvas != null) ? ((Component)osCanvas).GetComponent<RectTransform>() : null);
			}
			if (obj == null)
			{
				obj = component;
			}
			RectTransform val = (RectTransform)obj;
			for (int i = 0; i < ((Transform)component).childCount; i++)
			{
				GameObject gameObject = ((Component)((Transform)component).GetChild(i)).gameObject;
				originals.Add((gameObject, gameObject.activeSelf));
			}
			panel = new GameObject("GovernmentTerminal");
			panel.layer = ((Component)app).gameObject.layer;
			RectTransform val2 = panel.AddComponent<RectTransform>();
			((Transform)val2).SetParent((Transform)(object)val, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			Vector2 offsetMin = (val2.offsetMax = Vector2.zero);
			val2.offsetMin = offsetMin;
			((Transform)val2).SetAsLastSibling();
			Image obj2 = panel.AddComponent<Image>();
			((Graphic)obj2).color = new Color(0.035f, 0.035f, 0.035f, 1f);
			((Graphic)obj2).raycastTarget = true;
			GameObject val3 = new GameObject("TerminalText");
			val3.layer = panel.layer;
			RectTransform obj3 = val3.AddComponent<RectTransform>();
			((Transform)obj3).SetParent((Transform)(object)val2, false);
			obj3.anchorMin = new Vector2(0.025f, 0.02f);
			obj3.anchorMax = new Vector2(0.975f, 0.985f);
			offsetMin = (obj3.offsetMax = Vector2.zero);
			obj3.offsetMin = offsetMin;
			text = val3.AddComponent<TextMeshProUGUI>();
			((TMP_Text)text).font = ((TMP_Text)app.searchText).font;
			((TMP_Text)text).fontSharedMaterial = ((TMP_Text)app.searchText).fontSharedMaterial;
			((Graphic)text).color = Color.white;
			((Graphic)text).raycastTarget = false;
			((TMP_Text)text).alignment = (TextAlignmentOptions)257;
			((TMP_Text)text).enableAutoSizing = false;
			((TMP_Text)text).enableWordWrapping = false;
			((TMP_Text)text).overflowMode = (TextOverflowModes)3;
			((TMP_Text)text).richText = true;
			Rect rect = val.rect;
			float num = Math.Abs(((Rect)(ref rect)).height) * 0.965f;
			rect = val.rect;
			float num2 = Math.Abs(((Rect)(ref rect)).width) * 0.95f;
			if (num <= 0f || num2 <= 0f)
			{
				throw new InvalidOperationException("Database screen has zero size.");
			}
			((TMP_Text)text).fontSize = Math.Min(num / 24f, num2 / 30.36f);
			MeasureListingLines(num);
			foreach (var original in originals)
			{
				original.Object.SetActive(false);
			}
			foreach (string item in TerminalCommands.Banner(CityName()))
			{
				Write(item);
			}
			Write(new string('-', 46));
			Write("SECURE SESSION ESTABLISHED");
			Write("Type HELP for commands. F8 restores the original screen.");
			Write("Up/Down: earlier commands. PageUp: scroll.");
			Write("");
			Render();
		}

		private void MeasureListingLines(float areaHeight)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)this.text == (Object)null)
			{
				return;
			}
			try
			{
				float num;
				if ((Object)(object)((TMP_Text)this.text).rectTransform != (Object)null)
				{
					Rect rect = ((TMP_Text)this.text).rectTransform.rect;
					if (((Rect)(ref rect)).height > 0f)
					{
						rect = ((TMP_Text)this.text).rectTransform.rect;
						num = Math.Abs(((Rect)(ref rect)).height);
						goto IL_0064;
					}
				}
				num = areaHeight;
				goto IL_0064;
				IL_0064:
				float num2 = num;
				if (!(num2 <= 0f))
				{
					string text = ((TMP_Text)this.text).text;
					((TMP_Text)this.text).text = string.Join("\n", Enumerable.Repeat("M", 6));
					((TMP_Text)this.text).ForceMeshUpdate(false, false);
					float num3 = ((TMP_Text)this.text).preferredHeight / 6f;
					((TMP_Text)this.text).text = text;
					if (!(num3 <= 0f) && !float.IsNaN(num3) && !float.IsInfinity(num3))
					{
						int value = (int)Math.Floor(num2 / num3) - 2;
						listingLines = Math.Clamp(value, 12, 21);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(31, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Line count measurement failed: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
			}
		}

		private static string CityName()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			try
			{
				CityData instance = CityData.Instance;
				return ((instance != null) ? instance.cityName : null) ?? "";
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(23, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("City name unavailable: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
				return "";
			}
		}

		private void Close()
		{
			if (!closing)
			{
				closing = true;
				Forget(app);
				app.ExitButton();
			}
		}

		internal static bool CapturingInput()
		{
			foreach (TerminalWindow value in Windows.Values)
			{
				try
				{
					if ((Object)(object)value.app != (Object)null && (Object)(object)((CruncherAppContent)value.app).controller != (Object)null && ((CruncherAppContent)value.app).controller.playerControlled && value.PlayerUsesScreen())
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		internal static bool TypingAtScreen()
		{
			foreach (TerminalWindow value in Windows.Values)
			{
				try
				{
					if ((Object)(object)value.app != (Object)null && (Object)(object)((CruncherAppContent)value.app).controller != (Object)null && ((CruncherAppContent)value.app).controller.playerControlled && value.LookingAtScreen())
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		internal void Tick()
		{
			if ((Object)(object)((CruncherAppContent)app).controller == (Object)null || !((CruncherAppContent)app).controller.playerControlled || (Object)(object)SessionData.Instance == (Object)null || !SessionData.Instance.play || !LookingAtScreen())
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)289))
			{
				Restore(app);
				return;
			}
			if (Input.GetKeyDown((KeyCode)273))
			{
				History(-1);
			}
			if (Input.GetKeyDown((KeyCode)274))
			{
				History(1);
			}
			if (Input.GetKeyDown((KeyCode)280))
			{
				scroll = Math.Min(Math.Max(0, output.Count - listingLines), scroll + 10);
				Render();
			}
			if (Input.GetKeyDown((KeyCode)281))
			{
				scroll = Math.Max(0, scroll - 10);
				Render();
			}
			if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)108))
			{
				output.Clear();
				scroll = 0;
				Render();
			}
			Type(Input.inputString ?? "", physical: true);
			bool flag = (int)(Time.unscaledTime * 2f) % 2 == 0;
			if (flag != cursor)
			{
				cursor = flag;
				Render();
			}
		}

		private bool LookingAtScreen()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ComputerController controller = ((CruncherAppContent)app).controller;
				MeshCollider val = ((controller != null) ? controller._screenCollider : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				InteractionController instance = InteractionController.Instance;
				RaycastHit? val2 = ((instance != null) ? new RaycastHit?(instance.playerCurrentRaycastHit) : ((RaycastHit?)null));
				RaycastHit value = default(RaycastHit);
				if (val2.HasValue)
				{
					value = val2.Value;
					if ((Object)(object)((RaycastHit)(ref value)).collider != (Object)null)
					{
						value = val2.Value;
						if ((Object)(object)((RaycastHit)(ref value)).collider == (Object)(object)val)
						{
							return true;
						}
					}
				}
				InteractionController instance2 = InteractionController.Instance;
				InteractableController obj = ((instance2 != null) ? instance2.currentLookingAtInteractable : null);
				ComputerController controller2 = ((CruncherAppContent)app).controller;
				if ((Object)(object)obj == (Object)(object)((controller2 != null) ? controller2.ic : null))
				{
					return true;
				}
				CameraController instance3 = CameraController.Instance;
				Camera val3 = ((instance3 != null) ? instance3.cam : null);
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = Camera.main;
				}
				if ((Object)(object)val3 == (Object)null)
				{
					return false;
				}
				Ray val4 = default(Ray);
				((Ray)(ref val4))..ctor(((Component)val3).transform.position, ((Component)val3).transform.forward);
				return ((Collider)val).Raycast(val4, ref value, 3f);
			}
			catch
			{
				return false;
			}
		}

		private bool PlayerUsesScreen()
		{
			if (LookingAtScreen())
			{
				return true;
			}
			try
			{
				ComputerController controller = ((CruncherAppContent)app).controller;
				InteractableController val = ((controller != null) ? controller.ic : null);
				InteractionController instance = InteractionController.Instance;
				if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null)
				{
					return false;
				}
				return (Object)(object)instance.currentInteractable == (Object)(object)val || (Object)(object)instance.currentLookingAtInteractable == (Object)(object)val;
			}
			catch
			{
				return false;
			}
		}

		private void KeySound()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ComputerController controller = ((CruncherAppContent)app).controller;
				object obj;
				if (controller == null)
				{
					obj = null;
				}
				else
				{
					InteractableController ic = controller.ic;
					obj = ((ic != null) ? ic.interactable : null);
				}
				Interactable val = (Interactable)obj;
				AudioControls instance = AudioControls.Instance;
				AudioEvent val2 = ((instance != null) ? instance.computerKeyboardKey : null);
				if (val != null && !((SoCustomComparison)(object)val2 == (SoCustomComparison)null) && !((Object)(object)AudioController.Instance == (Object)null))
				{
					AudioController.Instance.PlayWorldOneShot(val2, (Actor)(object)Player.Instance, val.node, val.wPos, (Interactable)null, (List<FMODParam>)null, 1f, (List<NewNode>)null, false, (SoundMaterialOverride)null, false);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(18, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Key sound failed: ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val3);
				}
			}
		}

		internal void Type(string value, bool physical = false)
		{
			if (physical && value.Length > 0)
			{
				KeySound();
			}
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				switch (c)
				{
				case '\b':
					Backspace();
					continue;
				case '\n':
				case '\r':
					Submit();
					continue;
				}
				if (!char.IsControl(c) && buffer.Length < 120)
				{
					buffer += c;
				}
			}
			Render();
		}

		internal void Backspace()
		{
			if (buffer.Length > 0)
			{
				string text = buffer;
				buffer = text.Substring(0, text.Length - 1);
			}
			Render();
		}

		private void History(int delta)
		{
			if (history.Count != 0)
			{
				if (historyIndex == history.Count)
				{
					draft = buffer;
				}
				historyIndex = Math.Clamp(historyIndex + delta, 0, history.Count);
				buffer = ((historyIndex == history.Count) ? draft : history[historyIndex]);
				Render();
			}
		}

		private void Submit()
		{
			string text = buffer.Trim();
			buffer = "";
			scroll = 0;
			if (text.Length == 0)
			{
				return;
			}
			Write("GOV> " + text);
			history.Add(text);
			if (history.Count > 100)
			{
				history.RemoveAt(0);
			}
			historyIndex = history.Count;
			draft = "";
			TerminalCommand terminalCommand = TerminalCommands.Parse(text);
			if (terminalCommand.Verb != "next" && terminalCommand.Verb != "prev")
			{
				siftHelpPage = null;
			}
			switch (terminalCommand.Verb)
			{
			case "help":
			{
				string[] help = TerminalCommands.Help;
				foreach (string message in help)
				{
					Write(message);
				}
				break;
			}
			case "sift":
				Sift(terminalCommand.Argument);
				break;
			case "sifthelp":
				SiftUsage();
				break;
			case "search":
				Search(terminalCommand.Argument);
				break;
			case "show":
			case "open":
				Open(terminalCommand.Argument);
				break;
			case "print":
				Print();
				break;
			case "next":
				if (siftHelpPage.HasValue)
				{
					SiftUsage(siftHelpPage.Value + 1);
					break;
				}
				page = Math.Min(TerminalCommands.PageCount(matches.Count) - 1, page + 1);
				Results();
				break;
			case "prev":
				if (siftHelpPage.HasValue)
				{
					SiftUsage(siftHelpPage.Value - 1);
					break;
				}
				page = Math.Max(0, page - 1);
				Results();
				break;
			case "clear":
				output.Clear();
				break;
			case "olddb":
				Restore(app);
				return;
			case "showall":
				ShowAll();
				break;
			case "quit":
				Close();
				return;
			default:
				Write("UNKNOWN COMMAND. Type HELP.");
				break;
			}
			Write("");
		}

		private void Search(string query)
		{
			if (string.IsNullOrWhiteSpace(query))
			{
				Write("USAGE: SEARCH <name>");
				return;
			}
			matches.Clear();
			selected = null;
			page = 0;
			app.selectedHuman = null;
			if ((Object)(object)app.list != (Object)null)
			{
				app.list.allOptions = new List<OSMultiOption>();
			}
			app.searchString = query.ToUpperInvariant();
			app.UpdateSearch();
			ComputerOSMultiSelect list = app.list;
			List<OSMultiOption> val = ((list != null) ? list.allOptions : null);
			if (val != null)
			{
				for (int i = 0; i < val.Count; i++)
				{
					if ((Object)(object)val[i].human != (Object)null && !((Actor)val[i].human).isPlayer && ((Actor)val[i].human).evidenceEntry != null && !matches.Contains(val[i].human))
					{
						matches.Add(val[i].human);
					}
				}
			}
			foreach (var original in originals)
			{
				if ((Object)(object)original.Object != (Object)null)
				{
					original.Object.SetActive(false);
				}
			}
			int num = TerminalCommands.AutoOpenIndex(matches.Select((Human human) => human.GetCitizenName()).ToArray(), query);
			if (num >= 0)
			{
				Open((num + 1).ToString());
				return;
			}
			Results();
			if (matches.Count > 1)
			{
				Write("Multiple matches. Use OPEN <number> or SEARCH <full name>.");
			}
		}

		private void ShowAll()
		{
			CityData instance = CityData.Instance;
			List<Citizen> val = ((instance != null) ? instance.citizenDirectory : null);
			if (val == null)
			{
				Write("Citizen directory unavailable.");
				return;
			}
			matches.Clear();
			selected = null;
			page = 0;
			app.selectedHuman = null;
			for (int i = 0; i < val.Count; i++)
			{
				Human val2 = (Human)(object)val[i];
				if ((Object)(object)val2 != (Object)null && !((Actor)val2).isPlayer && ((Actor)val2).evidenceEntry != null && !matches.Contains(val2))
				{
					matches.Add(val2);
				}
			}
			matches.Sort((Human a, Human b) => string.Compare(a.GetCitizenName(), b.GetCitizenName(), StringComparison.OrdinalIgnoreCase));
			Write("SHOWALL");
			Results();
			if (matches.Count > 0)
			{
				Write("Use OPEN <number> to view a record. NEXT/PREV to page.");
			}
		}

		private void SiftUsage(int requestedPage = 0)
		{
			if (TerminalCommands.SiftHelp.Length <= listingLines)
			{
				siftHelpPage = null;
				string[] siftHelp = TerminalCommands.SiftHelp;
				foreach (string message in siftHelp)
				{
					Write(message);
				}
				return;
			}
			int num = Math.Max(1, listingLines - 1);
			int num2 = TerminalCommands.ListingPages(TerminalCommands.SiftHelp.Length, num);
			int num3 = Math.Clamp(requestedPage, 0, num2 - 1);
			siftHelpPage = num3;
			Write($"SIFT HELP {num3 + 1}/{num2} (NEXT/PREV)");
			foreach (string item in TerminalCommands.SiftHelp.Skip(num3 * num).Take(num))
			{
				Write(item);
			}
		}

		private void Sift(string argument)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			string error;
			List<(string, string)> list = TerminalCommands.ParseFilters(argument, out error);
			if (list == null)
			{
				Write(error);
				SiftUsage();
				return;
			}
			CityData instance = CityData.Instance;
			List<Citizen> val = ((instance != null) ? instance.citizenDirectory : null);
			if (val == null)
			{
				Write("Citizen directory unavailable.");
				return;
			}
			matches.Clear();
			selected = null;
			page = 0;
			app.selectedHuman = null;
			bool flag = default(bool);
			for (int i = 0; i < val.Count; i++)
			{
				Human val2 = (Human)(object)val[i];
				try
				{
					if (!((Object)(object)val2 == (Object)null) && !((Actor)val2).isPlayer && ((Actor)val2).evidenceEntry != null)
					{
						if (list.Any<(string, string)>(((string Field, string Value) f) => f.Field == "blood") && (int)val2.bloodType == 0)
						{
							val2.GenerateBloodType();
						}
						Dictionary<string, string> profile = Profile(val2);
						if (list.All<(string, string)>(((string Field, string Value) f) => TerminalCommands.Matches(profile, f.Field, f.Value)))
						{
							matches.Add(val2);
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger = Plugin.Logger;
					if (logger != null)
					{
						BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(26, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Filter skipped a citizen: ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
						}
						logger.LogWarning(val3);
					}
				}
			}
			matches.Sort((Human a, Human b) => string.Compare(a.GetCitizenName(), b.GetCitizenName(), StringComparison.OrdinalIgnoreCase));
			Write("SIFT: " + string.Join(", ", list.Select<(string, string), string>(((string Field, string Value) f) => f.Field.ToUpperInvariant() + "=" + f.Value)));
			Results();
			if (matches.Count > 0)
			{
				Write("Use OPEN <number> to view a record. NEXT/PREV to page.");
			}
		}

		private static Dictionary<string, string> Profile(Human h)
		{
			Dictionary<string, string> p = new Dictionary<string, string>();
			Descriptors d = h.descriptors;
			Add("name", () => h.GetCitizenName());
			Add("first", () => h.firstName);
			Add("last", () => h.surName);
			Add("gender", () => TerminalCommands.Label(((object)h.gender/*cast due to .constrained prefix*/).ToString()));
			Add("eyes", () => TerminalCommands.Label(((object)d.eyeColour/*cast due to .constrained prefix*/).ToString(), "Eyes"));
			Add("height", () => Math.Round(d.heightCM).ToString(CultureInfo.InvariantCulture));
			Add("heightclass", () => TerminalCommands.Label(((object)d.height/*cast due to .constrained prefix*/).ToString()));
			Add("shoe", () => Convert.ToDouble(d.shoeSize).ToString(CultureInfo.InvariantCulture));
			Add("hair", () => TerminalCommands.Label(((object)d.hairColourCategory/*cast due to .constrained prefix*/).ToString()));
			Add("hairstyle", () => TerminalCommands.Label(((object)d.hairType/*cast due to .constrained prefix*/).ToString()));
			Add("glasses", () => (!HasFeature((FacialFeature)5)) ? "no" : "yes");
			Add("facialhair", () => (!HasFeature((FacialFeature)1) && !HasFeature((FacialFeature)2)) ? "no" : "yes");
			Add("salary", () => (h.job == null) ? "0" : Math.Round(h.job.salary).ToString(CultureInfo.InvariantCulture));
			Add("build", () => TerminalCommands.Label(((object)d.build/*cast due to .constrained prefix*/).ToString()));
			Add("age", () => h.GetAge().ToString(CultureInfo.InvariantCulture));
			Add("blood", () => ((int)h.bloodType != 0) ? TerminalCommands.BloodType(((object)h.bloodType/*cast due to .constrained prefix*/).ToString()) : "");
			Add("job", () => (h.job == null) ? "Unemployed" : h.job.name);
			Add("address", () => (!((Object)(object)h.home != (Object)null)) ? "" : ((Object)h.home).name);
			Add("fingerprint", () => (h.fingerprintLoop < 0) ? "" : TerminalCommands.PrintIdentifier(h.fingerprintLoop));
			return p;
			void Add(string key, Func<string> get)
			{
				try
				{
					p[key] = get() ?? "";
				}
				catch
				{
					p[key] = "";
				}
			}
			bool HasFeature(FacialFeature f)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				List<FacialFeaturesSetting> facialFeatures = d.facialFeatures;
				if (facialFeatures == null)
				{
					return false;
				}
				for (int i = 0; i < facialFeatures.Count; i++)
				{
					if (facialFeatures[i].feature == f)
					{
						return true;
					}
				}
				return false;
			}
		}

		private void Results()
		{
			Write($"{matches.Count} RECORD(S) / PAGE {page + 1} OF {TerminalCommands.PageCount(matches.Count)}");
			int num = page * 10;
			for (int i = num; i < Math.Min(num + 10, matches.Count); i++)
			{
				Write($"{i + 1,4}  {matches[i].GetCitizenName()}");
			}
			if (matches.Count == 0)
			{
				Write("No records returned by database.");
			}
		}

		private void Open(string argument)
		{
			if (!int.TryParse(argument, out var result) || result < 1 || result > matches.Count)
			{
				Write("USAGE: OPEN <number from the last search>");
				return;
			}
			selected = matches[result - 1];
			app.selectedHuman = selected;
			Write("RECORD / " + selected.GetCitizenName());
			Reveal(selected);
			Descriptors d = selected.descriptors;
			int fingerprintLoop = selected.fingerprintLoop;
			Write("Name:          " + Safe(() => selected.GetCitizenName()));
			Write("Date of Birth: " + Safe(() => selected.birthday));
			Write("Gender:        " + Safe(() => TerminalCommands.Label(((object)selected.gender/*cast due to .constrained prefix*/).ToString())));
			Write("Eyes:          " + Safe(() => TerminalCommands.Label(((object)d.eyeColour/*cast due to .constrained prefix*/).ToString(), "Eyes")));
			Write("Height:        " + Safe(() => $"{Math.Round(d.heightCM)} cm ({TerminalCommands.Label(((object)d.height/*cast due to .constrained prefix*/).ToString())})"));
			Write("Fingerprint:   " + TerminalCommands.PrintIdentifier(fingerprintLoop));
			Write("Shoe Size:     " + Safe(() => d.shoeSize.ToString()));
			Write("Blood Type:    " + Safe(() => TerminalCommands.BloodType(((object)selected.bloodType/*cast due to .constrained prefix*/).ToString())));
			Write("Address:       " + Safe(() => (!((Object)(object)selected.home != (Object)null)) ? "No fixed address" : ((Object)selected.home).name));
			Write("Job Title:     " + Safe(() => (selected.job == null) ? "Unemployed" : selected.job.name));
			static string Safe(Func<string> get)
			{
				try
				{
					string text = get();
					return string.IsNullOrWhiteSpace(text) ? "Unknown" : text;
				}
				catch
				{
					return "Unknown";
				}
			}
		}

		private static void Reveal(Human human)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			bool flag = default(bool);
			try
			{
				if ((int)human.bloodType == 0)
				{
					try
					{
						human.GenerateBloodType();
					}
					catch (Exception ex)
					{
						ManualLogSource logger = Plugin.Logger;
						if (logger != null)
						{
							BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(30, 1, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Blood type generation failed: ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
							}
							logger.LogWarning(val);
						}
					}
				}
				if (human.fingerprintLoop < 0 && (Object)(object)GameplayController.Instance != (Object)null)
				{
					int num = (human.fingerprintLoop = Math.Max(0, GameplayController.Instance.printsLetterLoop));
					GameplayController.Instance.printsLetterLoop = num + 1;
				}
				EvidenceWitness evidenceEntry = ((Actor)human).evidenceEntry;
				if (evidenceEntry != null)
				{
					DataKey[] revealedKeys = RevealedKeys;
					foreach (DataKey val2 in revealedKeys)
					{
						((Evidence)evidenceEntry).MergeDataKeys((DataKey)0, val2);
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(22, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Record reveal failed: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex2.Message);
					}
					logger.LogWarning(val);
				}
			}
		}

		private void Print()
		{
			if ((Object)(object)selected == (Object)null)
			{
				Write("No selected record. Use SEARCH, then OPEN <number>.");
				return;
			}
			if ((Object)(object)((CruncherAppContent)app).controller == (Object)null || ((CruncherAppContent)app).controller.printTimer > 0f || ((CruncherAppContent)app).controller.printedDocument != null)
			{
				Write("PRINTER BUSY. Collect the previous document first.");
				return;
			}
			Reveal(selected);
			app.selectedHuman = selected;
			app.OnPrintEntry();
			Write("Print request sent.");
		}

		private void Write(string message)
		{
			output.AddRange(TerminalCommands.Wrap(message, 46));
			if (output.Count > 500)
			{
				output.RemoveRange(0, output.Count - 500);
			}
		}

		private void Render()
		{
			if (!((Object)(object)this.text == (Object)null))
			{
				int num = Math.Max(0, output.Count - scroll);
				int num2 = Math.Max(0, num - listingLines);
				string obj = string.Join("\n", output.Skip(num2).Take(num - num2));
				string obj2;
				if (buffer.Length <= 39)
				{
					obj2 = buffer;
				}
				else
				{
					string obj3 = buffer;
					int length = obj3.Length;
					int num3 = length - 39;
					obj2 = obj3.Substring(num3, length - num3);
				}
				string text = obj2;
				string text2 = (obj + "\n\nGOV> " + text + (cursor ? "_" : " ")).Replace("<", "‹").Replace(">", "›");
				((TMP_Text)this.text).text = "<mspace=0.65em>" + text2 + "</mspace>";
			}
		}

		internal static void Restore(DatabaseApp app)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			TerminalWindow terminalWindow = Find(app);
			if (terminalWindow == null)
			{
				return;
			}
			Windows.Remove(((Object)app).GetInstanceID());
			foreach (var original in terminalWindow.originals)
			{
				if ((Object)(object)original.Object != (Object)null)
				{
					original.Object.SetActive(original.Active);
				}
			}
			if ((Object)(object)terminalWindow.panel != (Object)null)
			{
				Object.Destroy((Object)(object)terminalWindow.panel);
			}
			app.searchString = "";
			app.selectedHuman = null;
			try
			{
				app.UpdateSearch();
				app.UpdateSelected();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Original database refresh failed: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
			}
		}

		internal static void Forget(DatabaseApp app)
		{
			TerminalWindow terminalWindow = Find(app);
			if (terminalWindow != null)
			{
				Windows.Remove(((Object)app).GetInstanceID());
				if ((Object)(object)terminalWindow.panel != (Object)null)
				{
					Object.Destroy((Object)(object)terminalWindow.panel);
				}
				terminalWindow.panel = null;
				terminalWindow.text = null;
			}
		}

		internal static void RestoreAll()
		{
			TerminalWindow[] array = Windows.Values.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				Restore(array[i].app);
			}
		}

		static TerminalWindow()
		{
			DataKey[] array = new DataKey[9];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			RevealedKeys = (DataKey[])(object)array;
		}
	}
}