Decompiled source of Ultimate Server Configuration Manager v1.0.3

plugins/Ultimate_SCM.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using UltimateSCM.Core;
using UltimateSCM.Localization;
using UltimateSCM.Net;
using UltimateSCM.Translation;
using UltimateSCM.UI;
using UltimateSCM.Util;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("OdinBet")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("Ultimate Server Configuration Manager")]
[assembly: AssemblyTitle("Ultimate_SCM")]
[assembly: AssemblyVersion("1.0.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UltimateSCM
{
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? IsAdvanced;

		public int? Order;
	}
	[BepInPlugin("Igao.UltimateSCM", "Ultimate SCM", "1.0.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "Igao.UltimateSCM";

		public const string Name = "Ultimate SCM";

		public const string Version = "1.0.3";

		public static ConfigEntry<KeyboardShortcut> PanelHotkey;

		public static ConfigEntry<bool> ShowEscMenuButton;

		public static ConfigEntry<bool> AdminNeedsDevcommands;

		public static ConfigEntry<string> LanguageSetting;

		public static ConfigEntry<float> UiScale;

		public static ConfigEntry<bool> PreApplyCache;

		public static ConfigEntry<bool> AutoReconnect;

		public static ConfigEntry<float> ExtremeValueFactor;

		public static ConfigEntry<string> TranslationProvider;

		public static ConfigEntry<string> TranslationFallback;

		public static ConfigEntry<string> MyMemoryEmail;

		public static ConfigEntry<string> DeepLApiKey;

		public static ConfigEntry<bool> AutoTranslate;

		public static ConfigEntry<float> TranslationDelay;

		public static ConfigEntry<bool> BackgroundTranslation;

		public static ConfigEntry<float> BackgroundInterval;

		public static ConfigEntry<bool> WatchConfigFiles;

		public static ConfigEntry<float> FileCheckInterval;

		public static ConfigEntry<string> ExcludedMods;

		public static ConfigEntry<bool> AuditLog;

		public static ConfigEntry<bool> AutoUnlockPlayerOnly;

		private static HashSet<string> _excluded = new HashSet<string>();

		private ConfigPanel _panel;

		private static readonly Action StepRebind = ApplyAnalyzer.Rebind;

		private static readonly Action StepServer = SyncServer.Update;

		private static readonly Action StepClient = SyncClient.Update;

		private static readonly Action StepBackground = TranslationService.BackgroundTick;

		private static readonly Action StepSaveCache = delegate
		{
			TranslationService.SaveCacheIfNeeded(force: false);
		};

		private Action _stepPanel;

		private ConfigPanel _stepPanelOwner;

		private static float _nextErrorLog;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static string PluginDir { get; private set; }

		public static string DataDir => Path.Combine(Paths.ConfigPath, "Ultimate_SCM");

		private void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Paths.PluginPath;
			Directory.CreateDirectory(DataDir);
			BindConfig();
			UiText.Load();
			EditService.Init();
			Harmony val = new Harmony("Igao.UltimateSCM");
			IEnumerable<Type> enumerable = from t in typeof(Plugin).Assembly.GetTypes()
				where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0
				select t;
			foreach (Type item in enumerable)
			{
				try
				{
					val.CreateClassProcessor(item).Patch();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"[Ultimate SCM] Failed to apply {item.Name}: {arg}");
				}
			}
			if (!IsHeadless())
			{
				_panel = ((Component)this).gameObject.AddComponent<ConfigPanel>();
			}
			Log.LogInfo((object)string.Format("{0} {1} loaded. Panel hotkey: {2}.", "Ultimate SCM", "1.0.3", PanelHotkey.Value));
		}

		private void BindConfig()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Expected O, but got Unknown
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Expected O, but got Unknown
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_031a: Expected O, but got Unknown
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Expected O, but got Unknown
			PanelHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "Panel hotkey", new KeyboardShortcut((KeyCode)282, Array.Empty<KeyCode>()), "Key to open/close the panel (it can also be opened from the ESC menu). If ConfigurationManager also uses F1, change one of the two keys.");
			AdminNeedsDevcommands = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Admin needs devcommands", true, "ON: even admins (and the host / single-player) are treated as normal players until they type 'devcommands' in the console (F5) — hidden configs don't show and locked ones are read-only. OFF: admins always see and edit everything.");
			ShowEscMenuButton = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Show button in ESC menu", true, "Show the panel button in the pause (ESC) menu.");
			LanguageSetting = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "Language", "Auto", "Auto = follow Valheim's language. Or a language name as Valheim calls it: English, Portuguese_Brazilian, Spanish... (the panel uses translations/<name>.json).");
			UiScale = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "UI scale", 1f, new ConfigDescription("Panel size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2f), Array.Empty<object>()));
			PreApplyCache = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Apply server values before world loads", true, "Remember the values each server enforces and apply them BEFORE the world loads on the next join (makes configs that mods only read while loading the world work).");
			AutoReconnect = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Auto reconnect on relog", true, "The \"Relog now\" button leaves and joins the same server again by itself.");
			ExtremeValueFactor = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Extreme value factor", 10f, new ConfigDescription("Ask for confirmation when a server-wide value becomes N times bigger/smaller than the default, is set to zero or has its sign flipped. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			AcceptableValueList<string> val = new AcceptableValueList<string>(new string[3] { "MyMemory", "DeepL", "Off" });
			TranslationProvider = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Translation", "Provider", "MyMemory", new ConfigDescription("MAIN service used to translate mod comments. MyMemory: free public API, no key (~5,000 characters/day per PC). DeepL: official, last resort when MyMemory fails (creating your own free key is recommended, see 'DeepL API key'). Off: cache and community packs only.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			TranslationFallback = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Translation", "Fallback provider", "DeepL", new ConfigDescription("BACKUP service: used right away if the main one fails. A service that fails 3 times in a row is skipped for 10 min. Off: no backup.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
			DeepLApiKey = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Translation", "DeepL API key", "", "Optional. DeepL is the last resort when MyMemory fails. Recommended: create your own FREE key at deepl.com/pro-api (500,000 characters/month) and paste it here. Keys ending in :fx use the free servers automatically. It is only sent to DeepL.");
			MyMemoryEmail = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Translation", "MyMemory email", "", "Optional. With your e-mail, MyMemory's free limit goes from ~5,000 to ~50,000 characters per day. The e-mail is only sent to MyMemory, together with the text to translate.");
			AutoTranslate = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Translation", "Auto translate", false, "Automatically translate the comments when you open a mod (if your language isn't English).");
			TranslationDelay = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Translation", "Delay between requests", 5f, new ConfigDescription("Seconds between online translations (avoids being blocked or running out of the free quota). Default 5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			BackgroundTranslation = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Translation", "Background translation", false, new ConfigDescription("ADVANCED. Slowly translates the comments of ALL mods while you play, one text at a time, so everything is already translated when you open the panel. Uses your translation services; keep a long interval to avoid being blocked.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdvanced = true,
					Order = -10
				}
			}));
			BackgroundInterval = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Translation", "Background interval", 30f, new ConfigDescription("ADVANCED. Seconds between background translations (minimum 10). 30 s = about 120 texts per hour.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdvanced = true,
					Order = -11
				}
			}));
			WatchConfigFiles = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Server", "Watch config files", true, "Server: if a .cfg is edited by hand while the server runs, reload it and send it to everyone.");
			FileCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Server", "File check interval", 60f, new ConfigDescription("Seconds between checks for .cfg files edited BY HAND on the server (panel changes are always instant). Default 60.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			ExcludedMods = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Server", "Excluded mods", "", "Mod GUIDs (comma separated) that Ultimate SCM never enforces — always local for each player.");
			AutoUnlockPlayerOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Server", "Auto unlock player-only settings", false, "Server: OFF (default) = everything starts Hidden until the admin releases it. ON = hotkeys, UI, sound and colors (which only affect the player) start UNLOCKED. Each config is evaluated only once: if the admin locks it later, it stays locked.");
			AuditLog = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Server", "Audit log", true, "Server: log who changed what in BepInEx/config/Ultimate_SCM/changes.log.");
			ParseExcluded();
			ExcludedMods.SettingChanged += delegate
			{
				ParseExcluded();
			};
			LanguageSetting.SettingChanged += delegate
			{
				UiText.Refresh();
			};
		}

		private static void ParseExcluded()
		{
			_excluded = new HashSet<string>(from x in (ExcludedMods.Value ?? "").Split(new char[1] { ',' })
				select x.Trim() into x
				where x.Length > 0
				select x, StringComparer.OrdinalIgnoreCase);
		}

		public static bool IsExcluded(string guid)
		{
			if (guid != null)
			{
				return _excluded.Contains(guid);
			}
			return false;
		}

		private static bool IsHeadless()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			if (!Application.isBatchMode)
			{
				return (int)SystemInfo.graphicsDeviceType == 4;
			}
			return true;
		}

		private void Start()
		{
			ConfigCatalogBootstrap.Run();
		}

		private void Update()
		{
			Safe(StepRebind);
			Safe(StepServer);
			Safe(StepClient);
			if ((Object)(object)_panel != (Object)null)
			{
				if (_stepPanelOwner != _panel)
				{
					_stepPanelOwner = _panel;
					_stepPanel = _panel.Tick;
				}
				Safe(_stepPanel);
			}
			Safe(StepBackground);
			Safe(StepSaveCache);
		}

		private static void Safe(Action a)
		{
			try
			{
				a();
			}
			catch (Exception arg)
			{
				if (!(Time.unscaledTime < _nextErrorLog))
				{
					_nextErrorLog = Time.unscaledTime + 5f;
					Log.LogError((object)$"[Ultimate SCM] {arg}");
				}
			}
		}

		private void OnApplicationQuit()
		{
			SyncClient.RestoreAll();
			TranslationService.SaveCacheIfNeeded(force: true);
			EntryMetaStore.SaveIfDirty();
			_panel?.SaveStateIfDirty();
		}
	}
}
namespace UltimateSCM.Util
{
	public static class MiniJson
	{
		private sealed class Parser
		{
			private readonly string _s;

			private int _i;

			private int _depth;

			public Parser(string s)
			{
				_s = s;
			}

			public void SkipWs()
			{
				while (_i < _s.Length && char.IsWhiteSpace(_s[_i]))
				{
					_i++;
				}
			}

			public object ParseValue()
			{
				SkipWs();
				if (_i >= _s.Length)
				{
					throw new FormatException("Unexpected end of JSON");
				}
				char c = _s[_i];
				if ((c == '{' || c == '[') && ++_depth > 64)
				{
					throw new FormatException("JSON nested too deeply");
				}
				try
				{
					return ParseValueCore(c);
				}
				finally
				{
					if (c == '{' || c == '[')
					{
						_depth--;
					}
				}
			}

			private object ParseValueCore(char c)
			{
				switch (c)
				{
				case '{':
					return ParseObj();
				case '[':
					return ParseArr();
				case '"':
					return ParseStr();
				case 't':
					Expect("true");
					return true;
				case 'f':
					Expect("false");
					return false;
				case 'n':
					Expect("null");
					return null;
				default:
					return ParseNum();
				}
			}

			private void Expect(string word)
			{
				if (string.CompareOrdinal(_s, _i, word, 0, word.Length) != 0)
				{
					throw new FormatException($"Expected '{word}' at position {_i}");
				}
				_i += word.Length;
			}

			private Dictionary<string, object> ParseObj()
			{
				Dictionary<string, object> dictionary = new Dictionary<string, object>();
				_i++;
				SkipWs();
				if (_i < _s.Length && _s[_i] == '}')
				{
					_i++;
					return dictionary;
				}
				while (true)
				{
					SkipWs();
					string key = ParseStr();
					SkipWs();
					if (_s[_i] != ':')
					{
						throw new FormatException($"Expected ':' at position {_i}");
					}
					_i++;
					dictionary[key] = ParseValue();
					SkipWs();
					if (_s[_i] != ',')
					{
						break;
					}
					_i++;
				}
				if (_s[_i] == '}')
				{
					_i++;
					return dictionary;
				}
				throw new FormatException($"Expected ',' or '}}' at position {_i}");
			}

			private List<object> ParseArr()
			{
				List<object> list = new List<object>();
				_i++;
				SkipWs();
				if (_i < _s.Length && _s[_i] == ']')
				{
					_i++;
					return list;
				}
				while (true)
				{
					list.Add(ParseValue());
					SkipWs();
					if (_s[_i] != ',')
					{
						break;
					}
					_i++;
				}
				if (_s[_i] == ']')
				{
					_i++;
					return list;
				}
				throw new FormatException($"Expected ',' or ']' at position {_i}");
			}

			private string ParseStr()
			{
				if (_s[_i] != '"')
				{
					throw new FormatException($"Expected '\"' at position {_i}");
				}
				_i++;
				StringBuilder stringBuilder = new StringBuilder();
				while (_i < _s.Length)
				{
					char c = _s[_i++];
					switch (c)
					{
					case '"':
						return stringBuilder.ToString();
					default:
						stringBuilder.Append(c);
						break;
					case '\\':
					{
						char c2 = _s[_i++];
						switch (c2)
						{
						case '"':
							stringBuilder.Append('"');
							break;
						case '\\':
							stringBuilder.Append('\\');
							break;
						case '/':
							stringBuilder.Append('/');
							break;
						case 'b':
							stringBuilder.Append('\b');
							break;
						case 'f':
							stringBuilder.Append('\f');
							break;
						case 'n':
							stringBuilder.Append('\n');
							break;
						case 'r':
							stringBuilder.Append('\r');
							break;
						case 't':
							stringBuilder.Append('\t');
							break;
						case 'u':
							stringBuilder.Append((char)int.Parse(_s.Substring(_i, 4), NumberStyles.HexNumber));
							_i += 4;
							break;
						default:
							stringBuilder.Append(c2);
							break;
						}
						break;
					}
					}
				}
				throw new FormatException("Unterminated JSON string");
			}

			private double ParseNum()
			{
				int i = _i;
				while (_i < _s.Length && "+-0123456789.eE".IndexOf(_s[_i]) >= 0)
				{
					_i++;
				}
				if (i == _i)
				{
					throw new FormatException($"Unexpected character '{_s[_i]}' at position {_i}");
				}
				return double.Parse(_s.Substring(i, _i - i), CultureInfo.InvariantCulture);
			}
		}

		public static object Parse(string json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return null;
			}
			Parser parser = new Parser(json);
			parser.SkipWs();
			return parser.ParseValue();
		}

		public static Dictionary<string, object> ParseObject(string json)
		{
			return Parse(json) as Dictionary<string, object>;
		}

		public static string Serialize(object value, bool pretty = true)
		{
			StringBuilder stringBuilder = new StringBuilder();
			Write(stringBuilder, value, pretty, 0);
			return stringBuilder.ToString();
		}

		private static void Write(StringBuilder sb, object v, bool pretty, int indent)
		{
			if (v != null)
			{
				if (!(v is string s))
				{
					if (!(v is bool flag))
					{
						if (!(v is double d))
						{
							if (!(v is float f))
							{
								if (!(v is int num))
								{
									if (!(v is long num2))
									{
										if (!(v is IDictionary dictionary))
										{
											if (v is IEnumerable enumerable)
											{
												sb.Append('[');
												bool flag2 = true;
												foreach (object item in enumerable)
												{
													if (!flag2)
													{
														sb.Append(',');
													}
													flag2 = false;
													NewLine(sb, pretty, indent + 1);
													Write(sb, item, pretty, indent + 1);
												}
												if (!flag2)
												{
													NewLine(sb, pretty, indent);
												}
												sb.Append(']');
											}
											else
											{
												WriteStr(sb, Convert.ToString(v, CultureInfo.InvariantCulture));
											}
											return;
										}
										sb.Append('{');
										bool flag3 = true;
										foreach (DictionaryEntry item2 in dictionary)
										{
											if (!flag3)
											{
												sb.Append(',');
											}
											flag3 = false;
											NewLine(sb, pretty, indent + 1);
											WriteStr(sb, item2.Key.ToString());
											sb.Append(pretty ? ": " : ":");
											Write(sb, item2.Value, pretty, indent + 1);
										}
										if (!flag3)
										{
											NewLine(sb, pretty, indent);
										}
										sb.Append('}');
									}
									else
									{
										sb.Append(num2.ToString(CultureInfo.InvariantCulture));
									}
								}
								else
								{
									sb.Append(num.ToString(CultureInfo.InvariantCulture));
								}
							}
							else
							{
								sb.Append((float.IsNaN(f) || float.IsInfinity(f)) ? "0" : f.ToString("R", CultureInfo.InvariantCulture));
							}
						}
						else
						{
							sb.Append((double.IsNaN(d) || double.IsInfinity(d)) ? "0" : d.ToString("R", CultureInfo.InvariantCulture));
						}
					}
					else
					{
						sb.Append(flag ? "true" : "false");
					}
				}
				else
				{
					WriteStr(sb, s);
				}
			}
			else
			{
				sb.Append("null");
			}
		}

		private static void NewLine(StringBuilder sb, bool pretty, int indent)
		{
			if (pretty)
			{
				sb.Append('\n');
				sb.Append(' ', indent * 2);
			}
		}

		private static void WriteStr(StringBuilder sb, string s)
		{
			sb.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					sb.Append("\\\"");
					continue;
				case '\\':
					sb.Append("\\\\");
					continue;
				case '\n':
					sb.Append("\\n");
					continue;
				case '\r':
					sb.Append("\\r");
					continue;
				case '\t':
					sb.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder = sb.Append("\\u");
					int num = c;
					stringBuilder.Append(num.ToString("x4"));
				}
				else
				{
					sb.Append(c);
				}
			}
			sb.Append('"');
		}
	}
	public static class SafeFile
	{
		public static void WriteAllText(string path, string text)
		{
			string text2 = path + ".tmp";
			File.WriteAllText(text2, text);
			try
			{
				if (File.Exists(path))
				{
					File.Replace(text2, path, null);
				}
				else
				{
					File.Move(text2, path);
				}
			}
			catch
			{
				File.WriteAllText(path, text);
				try
				{
					File.Delete(text2);
				}
				catch
				{
				}
			}
		}

		public static void Quarantine(string path)
		{
			try
			{
				string text = path + ".bad";
				if (File.Exists(text))
				{
					File.Delete(text);
				}
				File.Move(path, text);
				Plugin.Log.LogWarning((object)("[Ultimate SCM] Unreadable file kept as " + Path.GetFileName(text) + "."));
			}
			catch
			{
			}
		}
	}
}
namespace UltimateSCM.UI
{
	public sealed class ConfigPanel : MonoBehaviour
	{
		private struct Sig
		{
			public int Data;

			public int Revision;

			public int Mode;

			public int Flags;

			public string View;

			public string Search;

			public AccessPolicy Policy;

			public bool Same(Sig o)
			{
				if (Data == o.Data && Revision == o.Revision && Mode == o.Mode && Flags == o.Flags && string.Equals(View, o.View, StringComparison.Ordinal) && string.Equals(Search, o.Search, StringComparison.Ordinal))
				{
					return Policy == o.Policy;
				}
				return false;
			}
		}

		private sealed class Listing
		{
			public readonly List<ModInfo> Mods = new List<ModInfo>();

			public readonly List<GUIContent> Labels = new List<GUIContent>();

			public readonly List<EntryInfo> Pinned = new List<EntryInfo>();

			public int TotalListed;
		}

		private sealed class SectionView
		{
			public string Key;

			public string Display;

			public string CollapseKey;

			public string ResetKey;

			public string LabelExpanded;

			public string LabelCollapsed;

			public List<EntryInfo> Entries;

			public bool Translatable;

			public bool AnyTranslated;
		}

		private sealed class GroupView
		{
			public string Header;

			public List<EntryInfo> Entries;
		}

		private sealed class MainView
		{
			public string CullKey = "";

			public string Title = "";

			public string Empty = "";

			public List<GroupView> Groups = new List<GroupView>();

			public int Count;

			public ModInfo Mod;

			public List<SectionView> Sections = new List<SectionView>();

			public List<string> Names = new List<string>();

			public string[] TabLabels = new string[0];

			public List<EntryInfo> Shown = new List<EntryInfo>();

			public bool ModTranslatable;

			public bool ModMirrored;

			public bool VisMixed;

			public List<EntryInfo> Suggested = new List<EntryInfo>();

			public List<ConfigFile> Files = new List<ConfigFile>();

			public string FileNames = "";
		}

		private enum Vis
		{
			Hidden,
			View,
			Edit
		}

		private int _dataVersion;

		private Listing _listing;

		private MainView _main;

		private Sig _cacheSig;

		private bool _cacheValid;

		private const float CullEstimate = 150f;

		private readonly Dictionary<string, Rect> _cardRects = new Dictionary<string, Rect>();

		private readonly List<bool> _cullPlan = new List<bool>();

		private string _cullView;

		private int _cullIndex;

		private bool _cullStop;

		private bool _cullOff;

		private float _cullLimit;

		private float _mainViewH = 600f;

		private const int TabsThreshold = 6;

		private const int MaxSearchResults = 300;

		private const int MaxErrorLog = 20;

		private const string ViewHome = "@home";

		private const string ViewPinned = "@pinned";

		private const string ViewChanged = "@changed";

		private const string ViewNews = "@news";

		private UiState _state;

		private bool _stateDirty;

		private float _nextStateSave;

		private string _view = "@home";

		private string _expandedChoice;

		private Vector2 _modScroll;

		private Vector2 _mainScroll;

		private readonly Dictionary<string, Vector2> _scrollByView = new Dictionary<string, Vector2>();

		private Rect _win;

		private bool _dragging;

		private bool _resizing;

		private Vector2 _dragOffset;

		private float _screenW = 1920f;

		private float _screenH = 1080f;

		private string _searchInput = "";

		private string _search = "";

		private bool _onlyModifiedInput;

		private bool _onlyModified;

		private bool _showAdvancedInput;

		private bool _showAdvanced;

		private bool _showHiddenInput = true;

		private bool _showHidden = true;

		private bool _focusSearch;

		private readonly Dictionary<string, string> _buffers = new Dictionary<string, string>();

		private bool _clearFocus;

		private string _confirmKey;

		private float _confirmUntil;

		private string _currentError;

		private readonly Dictionary<string, string> _drafts = new Dictionary<string, string>();

		private bool _showDraftList;

		private string _noteEditId;

		private string _noteBuffer = "";

		private readonly Dictionary<string, EntryInfo> _noteEntries = new Dictionary<string, EntryInfo>();

		private readonly HashSet<string> _noteAutoTried = new HashSet<string>();

		private readonly List<(string text, bool error, float until, bool warn)> _toasts = new List<(string, bool, float, bool)>();

		private readonly List<(DateTime at, string text)> _errorLog = new List<(DateTime, string)>();

		private bool _showErrors;

		private readonly Queue<Action> _deferred = new Queue<Action>();

		private readonly List<EntryInfo> _pendingRelog = new List<EntryInfo>();

		private readonly List<EntryInfo> _pendingRestart = new List<EntryInfo>();

		private List<(string id, string name, ApplyMode mode)> _serverPending = new List<(string, string, ApplyMode)>();

		private readonly Dictionary<string, int> _newsByMod = new Dictionary<string, int>();

		private readonly Dictionary<string, int> _modifiedByMod = new Dictionary<string, int>();

		private int _newsCount;

		private int _changedCount;

		private bool _showServerPending;

		private bool _showClientPending;

		private bool _showModDiff;

		private float _nextPendingCalc;

		private List<string> _modalLines;

		private Action _modalConfirm;

		private const float HeaderControlH = 26f;

		private static bool _nrPlayersLooked;

		private static MethodInfo _nrPlayers;

		private string _captureId;

		private string _expandedColor;

		private readonly Dictionary<string, string[]> _vecParts = new Dictionary<string, string[]>();

		private readonly Dictionary<string, string> _vecSource = new Dictionary<string, string>();

		public static ConfigPanel Instance { get; private set; }

		public static bool IsOpen { get; private set; }

		public static int ClosedFrame { get; private set; } = -10;

		private static bool PlayerView
		{
			get
			{
				if (SyncClient.Mode == SessionMode.ClientSynced)
				{
					return !SyncClient.ActsAsAdmin;
				}
				return false;
			}
		}

		private bool Compact
		{
			get
			{
				if (PlayerView)
				{
					return !_state.ShowDetails;
				}
				return false;
			}
		}

		private void BumpData()
		{
			_dataVersion++;
		}

		private Sig CurrentSig()
		{
			int flags = (int)((_onlyModified ? 1u : 0u) | (uint)(_showAdvanced ? 2 : 0) | (uint)(_showHidden ? 4 : 0) | (uint)(SyncClient.IsAdmin ? 8 : 0) | (uint)(SyncClient.SoloHost ? 16 : 0) | (uint)(SyncClient.CheatsOn ? 32 : 0) | (uint)(Plugin.AdminNeedsDevcommands.Value ? 64 : 0)) | (SyncClient.ActsAsAdmin ? 128 : 0);
			return new Sig
			{
				Data = _dataVersion,
				Revision = ConfigCatalog.Revision,
				Mode = (int)SyncClient.Mode,
				Flags = flags,
				View = _view,
				Search = _search,
				Policy = SyncClient.Policy
			};
		}

		private void EnsureCaches()
		{
			Sig cacheSig = CurrentSig();
			if (!_cacheValid || _listing == null || _main == null || !cacheSig.Same(_cacheSig))
			{
				_cacheValid = false;
				_listing = BuildListing();
				_main = BuildMain();
				_cacheSig = cacheSig;
				_cacheValid = true;
			}
		}

		private Listing BuildListing()
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			Listing listing = new Listing();
			foreach (ModInfo mod in ConfigCatalog.GetMods())
			{
				int num = 0;
				foreach (EntryInfo entry in mod.Entries)
				{
					if (Listed(entry))
					{
						num++;
					}
				}
				if (num != 0)
				{
					listing.TotalListed += num;
					listing.Mods.Add(mod);
					_modifiedByMod.TryGetValue(mod.Guid ?? "", out var value);
					string text = mod.Name + "  (" + num + ")";
					if (value > 0)
					{
						text = text + "\n  " + UiText.T("n_modified", value);
					}
					if (_newsByMod.TryGetValue(mod.Guid ?? "", out var value2) && value2 > 0)
					{
						text = text + "\n  " + UiText.T("n_news", value2);
					}
					if (mod.OnlyOnServer)
					{
						text = text + "\n  " + UiText.T("only_on_server");
					}
					listing.Labels.Add(new GUIContent(text, mod.Guid));
				}
			}
			listing.Pinned.AddRange(PinnedEntries());
			return listing;
		}

		private MainView BuildMain()
		{
			MainView mainView = new MainView();
			mainView.CullKey = ((_search.Length > 0) ? ("search:" + _search) : _view) + "|" + _onlyModified + _showAdvanced + _showHidden;
			MainView mainView2 = mainView;
			if (_search.Length > 0)
			{
				List<EntryInfo> list = new List<EntryInfo>();
				foreach (EntryInfo item in ConfigCatalog.All)
				{
					if (Visible(item) && MatchesSearch(item))
					{
						list.Add(item);
						if (list.Count > 300)
						{
							break;
						}
					}
				}
				mainView2.Title = ((list.Count > 300) ? UiText.T("search_too_many", 300) : UiText.T("search_results", list.Count, _search));
				mainView2.Empty = UiText.T("search_none");
				if (list.Count > 300)
				{
					list.RemoveRange(300, list.Count - 300);
				}
				FillGroups(mainView2, list);
				return mainView2;
			}
			switch (_view)
			{
			case "@home":
				return mainView2;
			case "@pinned":
				mainView2.Title = UiText.T("view_pinned_title");
				mainView2.Empty = UiText.T("view_pinned_empty");
				FillGroups(mainView2, _listing.Pinned);
				return mainView2;
			case "@changed":
				mainView2.Title = UiText.T("view_changed_title");
				mainView2.Empty = UiText.T("view_changed_empty");
				FillGroups(mainView2, ConfigCatalog.All.Where((EntryInfo e) => Listed(e) && !e.IsDefault).ToList());
				return mainView2;
			case "@news":
			{
				List<EntryInfo> list2 = ConfigCatalog.All.Where((EntryInfo e) => Listed(e) && SyncClient.IsNews(e)).ToList();
				mainView2.Title = UiText.T("view_news_title", list2.Count);
				mainView2.Empty = UiText.T("view_news_empty");
				FillGroups(mainView2, list2);
				return mainView2;
			}
			default:
				BuildModView(mainView2);
				return mainView2;
			}
		}

		private static void FillGroups(MainView v, List<EntryInfo> entries)
		{
			v.Count = entries.Count;
			foreach (IGrouping<string, EntryInfo> item in from e in entries
				group e by e.ModName + "  ›  " + Sec(e))
			{
				v.Groups.Add(new GroupView
				{
					Header = item.Key,
					Entries = Sort(item).ToList()
				});
			}
		}

		private void BuildModView(MainView v)
		{
			ModInfo mod = null;
			foreach (ModInfo mod2 in _listing.Mods)
			{
				if (mod2.Guid == _view)
				{
					mod = mod2;
					break;
				}
			}
			v.Mod = mod;
			if (mod == null)
			{
				return;
			}
			IOrderedEnumerable<IGrouping<string, EntryInfo>> orderedEnumerable = (from e in mod.Entries.Where(Visible)
				group e by e.Section).OrderBy((IGrouping<string, EntryInfo> g) => g.Key, Comparer<string>.Create(DescriptionAnalyzer.NaturalCompare));
			foreach (IGrouping<string, EntryInfo> item in orderedEnumerable)
			{
				List<EntryInfo> list = Sort(item).ToList();
				string text = ((list.Count > 0) ? Sec(list[0]) : item.Key);
				int num = 0;
				foreach (EntryInfo item2 in list)
				{
					if (!item2.IsDefault)
					{
						num++;
					}
				}
				string text2 = "   (" + list.Count + ((num > 0) ? (" · " + UiText.T("n_modified", num)) : "") + ")";
				EntryInfo entryInfo = list.FirstOrDefault((EntryInfo e) => !string.IsNullOrWhiteSpace(e.Description));
				bool flag = entryInfo != null && TranslationService.HasAny(entryInfo);
				v.Sections.Add(new SectionView
				{
					Key = item.Key,
					Display = text,
					CollapseKey = mod.Guid + "|" + item.Key,
					ResetKey = "reset_sec_" + mod.Guid + item.Key,
					LabelExpanded = "-  " + text + text2,
					LabelCollapsed = "+  " + text + text2,
					Entries = list,
					Translatable = flag,
					AnyTranslated = (flag && list.Any((EntryInfo e) => TranslationService.GetState(e) == TranslationService.State.Translated))
				});
			}
			v.Names = v.Sections.Select((SectionView s) => s.Key).ToList();
			v.TabLabels = v.Names.Select((string n) => mod.Entries.FirstOrDefault((EntryInfo x) => x.Section == n)?.SectionDisplay ?? n).ToArray();
			v.Shown = mod.Entries.Where(Shown).ToList();
			v.ModTranslatable = TranslationService.HasAny(v.Shown.FirstOrDefault((EntryInfo e) => !string.IsNullOrWhiteSpace(e.Description)) ?? new EntryInfo());
			v.ModMirrored = mod.ExternalSync != null && mod.Entries.Any(SyncClient.IsMirrored);
			if (ShowAccessControls(mod))
			{
				AccessPolicy p = SyncClient.Policy;
				int modState = (p.UnlockedMods.Contains(mod.Guid) ? 2 : (p.ShownMods.Contains(mod.Guid) ? 1 : 0));
				v.VisMixed = mod.Entries.Any((EntryInfo x) => (p.IsUnlocked(x) ? 2 : ((!p.IsHidden(x)) ? 1 : 0)) != modState);
				v.Suggested = v.Shown.Where((EntryInfo e) => e.HintClientSide && SyncClient.IsLockedOnServer(e) && SyncClient.ExternalSyncOf(e) == null && e.Local != null).ToList();
			}
			if (!mod.OnlyOnServer)
			{
				v.Files = ConfigCatalog.FilesOf(mod.Guid).ToList();
				v.FileNames = string.Join(", ", v.Files.Select((ConfigFile f) => Path.GetFileName(f.ConfigFilePath)).ToArray());
			}
		}

		private void BeginCull(string viewKey)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if ((int)Event.current.type == 8)
			{
				if (!string.Equals(viewKey, _cullView, StringComparison.Ordinal))
				{
					_cullView = viewKey;
					_cardRects.Clear();
				}
				_cullPlan.Clear();
				string nameOfFocusedControl = GUI.GetNameOfFocusedControl();
				_cullOff = _noteEditId != null || _captureId != null || _expandedChoice != null || _expandedColor != null || (!string.IsNullOrEmpty(nameOfFocusedControl) && nameOfFocusedControl.StartsWith("uscm_", StringComparison.Ordinal) && nameOfFocusedControl != "uscm_search");
				_cullLimit = _mainScroll.y + _mainViewH + Mathf.Max(400f, _mainViewH * 0.75f);
			}
			_cullIndex = 0;
			_cullStop = false;
		}

		private void EndCull()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type == 7)
			{
				Rect lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).height > 50f)
				{
					_mainViewH = ((Rect)(ref lastRect)).height;
				}
			}
		}

		private void DrawEntryCulled(EntryInfo e, float width)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Invalid comparison between Unknown and I4
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Invalid comparison between Unknown and I4
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			EventType type = Event.current.type;
			bool flag;
			if ((int)type == 8)
			{
				if (!_cullOff && !_cullStop && _cardRects.TryGetValue(e.Id, out var value) && ((Rect)(ref value)).y > _cullLimit)
				{
					_cullStop = true;
				}
				flag = !_cullStop;
				_cullPlan.Add(flag);
			}
			else
			{
				flag = _cullIndex >= _cullPlan.Count || _cullPlan[_cullIndex];
			}
			_cullIndex++;
			if (flag)
			{
				DrawEntry(e, width);
				if ((int)type == 7)
				{
					_cardRects[e.Id] = GUILayoutUtility.GetLastRect();
				}
				return;
			}
			Rect value2;
			float num = ((_cardRects.TryGetValue(e.Id, out value2) && ((Rect)(ref value2)).height > 1f) ? ((Rect)(ref value2)).height : 150f);
			GUILayout.Space(num + 6f);
			if ((int)type == 7)
			{
				Rect lastRect = GUILayoutUtility.GetLastRect();
				_cardRects[e.Id] = new Rect(((Rect)(ref lastRect)).x, ((Rect)(ref lastRect)).y, ((Rect)(ref lastRect)).width, num);
			}
		}

		private void Awake()
		{
			Instance = this;
			_state = UiState.Load();
			_onlyModifiedInput = (_onlyModified = _state.OnlyModified);
			_showAdvancedInput = (_showAdvanced = _state.ShowAdvanced);
			_showHiddenInput = (_showHidden = _state.ShowHidden);
			EditService.Notify += delegate(string key, string arg, bool error)
			{
				Toast(UiText.T(key, arg), error, key);
			};
			TranslationService.Notify += delegate(string key, string arg, bool error)
			{
				bool flag = error && (key == "tr_provider_down" || key == "tr_all_paused");
				Toast(UiText.T(key, arg), error && !flag, key, flag);
			};
			EditService.ConfirmNeeded += delegate(List<string> lines, Action confirm)
			{
				Defer(delegate
				{
					_modalLines = lines;
					_modalConfirm = confirm;
				});
			};
			SyncClient.Changed += BumpData;
			TranslationService.Changed += BumpData;
		}

		public void Open()
		{
			ConfigCatalog.RefreshLocal();
			UiText.Refresh();
			IsOpen = true;
			TranslationService.Warm();
			string text = (string.IsNullOrEmpty(_state.View) ? "@home" : _state.View);
			_view = ((IsSpecial(text) || ModExists(text)) ? text : "@home");
			if (_view == "@news" && SyncClient.CountNews() == 0)
			{
				_view = "@home";
			}
			RecalcCounts();
			BumpData();
			ModInfo modInfo = CurrentMod();
			if (modInfo != null)
			{
				TranslationService.AutoTranslate(modInfo.Entries);
				TranslationService.BackgroundPrioritize(modInfo.Entries);
			}
		}

		public void Close()
		{
			if (IsOpen)
			{
				IsOpen = false;
				ClosedFrame = Time.frameCount;
				_modalLines = null;
				_modalConfirm = null;
				_noteEditId = null;
				_captureId = null;
				_dragging = (_resizing = false);
				_clearFocus = true;
				SyncClient.MarkAllSeen();
				SaveState();
			}
		}

		public void Toggle()
		{
			if (IsOpen)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		private void LateUpdate()
		{
			if (IsOpen)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private static bool HotkeyPressed()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				KeyboardShortcut value = Plugin.PanelHotkey.Value;
				KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey;
				if ((int)mainKey == 0 || !Input.GetKeyDown(mainKey))
				{
					return false;
				}
				foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
				{
					if (!Input.GetKey(modifier))
					{
						return false;
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		public void Tick()
		{
			if (_captureId == null && HotkeyPressed())
			{
				Toggle();
			}
			if (IsOpen && Input.GetKeyDown((KeyCode)27))
			{
				OnEscape();
			}
			if (IsOpen && Time.unscaledTime >= _nextPendingCalc)
			{
				_nextPendingCalc = Time.unscaledTime + 0.5f;
				BumpData();
				try
				{
					SyncClient.GetPending(_pendingRelog, _pendingRestart);
					_serverPending = (List<(string id, string name, ApplyMode mode)>)(EditService.CanManageAccess ? ((IList)SyncClient.ServerPending) : ((IList)new List<(string, string, ApplyMode)>()));
					RecalcCounts();
					PruneDrafts();
					PruneBuffers();
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"[Ultimate SCM] Panel refresh error: {arg}");
				}
			}
			if (_stateDirty && Time.unscaledTime >= _nextStateSave)
			{
				SaveState();
			}
			if (_deferred.Count > 0)
			{
				BumpData();
			}
			while (_deferred.Count > 0)
			{
				try
				{
					_deferred.Dequeue()();
				}
				catch (Exception arg2)
				{
					Plugin.Log.LogError((object)$"[Ultimate SCM] Panel error: {arg2}");
				}
			}
		}

		private void OnEscape()
		{
			if (_modalLines != null)
			{
				CloseModal(confirm: false);
			}
			else if (_captureId != null)
			{
				_captureId = null;
			}
			else if (_noteEditId != null)
			{
				_noteEditId = null;
				_clearFocus = true;
			}
			else if (_searchInput.Length > 0)
			{
				_searchInput = "";
				_clearFocus = true;
			}
			else if (GUIUtility.keyboardControl != 0)
			{
				_clearFocus = true;
			}
			else
			{
				Close();
			}
		}

		private void RecalcCounts()
		{
			_newsByMod.Clear();
			_modifiedByMod.Clear();
			_newsCount = 0;
			_changedCount = 0;
			foreach (EntryInfo item in ConfigCatalog.All)
			{
				if (Listed(item))
				{
					if (!item.IsDefault)
					{
						_changedCount++;
						_modifiedByMod.TryGetValue(item.ModGuid ?? "", out var value);
						_modifiedByMod[item.ModGuid ?? ""] = value + 1;
					}
					if (SyncClient.IsNews(item))
					{
						_newsCount++;
						_newsByMod.TryGetValue(item.ModGuid ?? "", out var value2);
						_newsByMod[item.ModGuid ?? ""] = value2 + 1;
					}
				}
			}
		}

		private void Defer(Action a)
		{
			_deferred.Enqueue(a);
		}

		private void MarkState()
		{
			if (!_stateDirty)
			{
				_nextStateSave = Time.unscaledTime + 2f;
			}
			_stateDirty = true;
		}

		public void SaveStateIfDirty()
		{
			if (_stateDirty)
			{
				SaveState();
			}
		}

		private void SaveState()
		{
			_stateDirty = false;
			_state.View = _view;
			_state.OnlyModified = _onlyModifiedInput;
			_state.ShowAdvanced = _showAdvancedInput;
			_state.ShowHidden = _showHiddenInput;
			_state.Save();
		}

		private void Toast(string text, bool error, string key = null, bool warn = false)
		{
			Defer(delegate
			{
				AddToast(text, error, key, warn);
			});
		}

		private void AddToast(string text, bool error, string key, bool warn = false)
		{
			if (warn && key != null && UiText.Has(key + "_fix"))
			{
				text = text + "\n" + UiText.T(key + "_fix");
			}
			if (error)
			{
				if (key != null && UiText.Has(key + "_fix"))
				{
					text = text + "\n" + UiText.T(key + "_fix");
				}
				_errorLog.Add((DateTime.Now, text.Replace("\n", " — ")));
				if (_errorLog.Count > 20)
				{
					_errorLog.RemoveAt(0);
				}
			}
			float item = Time.unscaledTime + (error ? 9f : (warn ? 7f : 3.5f));
			int num = _toasts.FindIndex(((string text, bool error, float until, bool warn) t) => t.text == text && t.error == error);
			if (num >= 0)
			{
				_toasts[num] = (text, error, item, warn);
				return;
			}
			_toasts.Add((text, error, item, warn));
			if (_toasts.Count > 4)
			{
				_toasts.RemoveAt(0);
			}
		}

		private static bool IsSpecial(string view)
		{
			switch (view)
			{
			default:
				return view == "@news";
			case "@home":
			case "@pinned":
			case "@changed":
				return true;
			}
		}

		private static bool ModExists(string guid)
		{
			return ConfigCatalog.GetMods().Any((ModInfo m) => m.Guid == guid);
		}

		private void Navigate(string view)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			_scrollByView[_view] = _mainScroll;
			_view = view;
			_searchInput = "";
			_mainScroll = (_scrollByView.TryGetValue(view, out var value) ? value : Vector2.zero);
			_expandedChoice = null;
			_captureId = null;
			_expandedColor = null;
			MarkState();
			ModInfo modInfo = CurrentMod();
			if (modInfo != null)
			{
				TranslationService.AutoTranslate(modInfo.Entries);
				TranslationService.BackgroundPrioritize(modInfo.Entries);
			}
		}

		private void OnGUI()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Invalid comparison between Unknown and I4
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Invalid comparison between Unknown and I4
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Invalid comparison between Unknown and I4
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: 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_00f6: Invalid comparison between Unknown and I4
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			if (_clearFocus)
			{
				_clearFocus = false;
				GUI.FocusControl((string)null);
			}
			if (IsOpen)
			{
				float num = Mathf.Clamp((float)Screen.height / 1080f * Plugin.UiScale.Value, 0.6f, 3f);
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				Styles.Ensure(15);
				if ((int)Event.current.type == 8)
				{
					GUI.tooltip = "";
				}
				if ((int)Event.current.type == 8)
				{
					_search = _searchInput.Trim();
					_onlyModified = _onlyModifiedInput;
					_showAdvanced = _showAdvancedInput;
					_showHidden = _showHiddenInput;
				}
				if ((int)Event.current.type == 8 || _listing == null || _main == null)
				{
					EnsureCaches();
				}
				HandleShortcuts();
				if (_focusSearch && (int)Event.current.type == 8)
				{
					_focusSearch = false;
					GUI.FocusControl("uscm_search");
				}
				_screenW = (float)Screen.width / num;
				_screenH = (float)Screen.height / num;
				Rect val = WindowRect(_screenW, _screenH);
				GUI.Box(val, GUIContent.none, Styles.Window);
				bool enabled = GUI.enabled;
				GUI.enabled = _modalLines == null;
				GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 14f, ((Rect)(ref val)).y + 12f, ((Rect)(ref val)).width - 28f, ((Rect)(ref val)).height - 24f));
				DrawHeader();
				GUILayout.Space(6f);
				DrawToolbar();
				GUILayout.Space(8f);
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				DrawSidebar(260f, ((Rect)(ref val)).height - 166f);
				GUILayout.Space(10f);
				DrawMain(((Rect)(ref val)).width - 28f - 270f, ((Rect)(ref val)).height - 166f);
				GUILayout.EndHorizontal();
				GUILayout.FlexibleSpace();
				DrawFooter();
				GUILayout.EndArea();
				GUI.enabled = enabled;
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax - 18f, ((Rect)(ref val)).yMax - 18f, 14f, 14f);
				GUI.Box(val2, new GUIContent("", UiText.T("tip_resize")), Styles.BadgeColored(Styles.Muted));
				HandleWindowMouse(val, val2);
				if (_modalLines != null)
				{
					DrawModal(_screenW, _screenH);
				}
				DrawToasts(val);
				DrawTooltip(_screenW, _screenH);
				if (((Rect)(ref val)).Contains(Event.current.mousePosition) && Event.current.isMouse)
				{
					Event.current.Use();
				}
			}
		}

		private void HandleShortcuts()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			Event current = Event.current;
			if ((int)current.type != 4 || _modalLines != null || _captureId != null || (!current.control && !current.command))
			{
				return;
			}
			KeyCode keyCode = current.keyCode;
			if ((int)keyCode != 102)
			{
				if ((int)keyCode != 115)
				{
					if ((int)keyCode == 122 && GUIUtility.keyboardControl == 0)
					{
						if (ChangeHistory.CanUndo)
						{
							Defer(EditService.Undo);
						}
						else
						{
							Toast(UiText.T("msg_nothing_to_undo"), error: false);
						}
						current.Use();
					}
				}
				else
				{
					if (_drafts.Count > 0)
					{
						Defer(ApplyDrafts);
					}
					current.Use();
				}
			}
			else
			{
				_focusSearch = true;
				current.Use();
			}
		}

		private Rect WindowRect(float w, float h)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Min(900f, w);
			float num2 = Mathf.Min(500f, h);
			Rect win = default(Rect);
			if (_dragging || _resizing)
			{
				win = _win;
			}
			else if (_state.W <= 0.01f || _state.H <= 0.01f)
			{
				float num3 = h * 0.88f;
				float num4 = Mathf.Min(w * 0.88f, num3 * 1.9f);
				((Rect)(ref win))..ctor((w - num4) / 2f, (h - num3) / 2f, num4, num3);
			}
			else
			{
				((Rect)(ref win))..ctor(_state.X * w, _state.Y * h, _state.W * w, _state.H * h);
			}
			((Rect)(ref win)).width = Mathf.Clamp(((Rect)(ref win)).width, num, w);
			((Rect)(ref win)).height = Mathf.Clamp(((Rect)(ref win)).height, num2, h);
			((Rect)(ref win)).x = Mathf.Clamp(((Rect)(ref win)).x, 0f, w - ((Rect)(ref win)).width);
			((Rect)(ref win)).y = Mathf.Clamp(((Rect)(ref win)).y, 0f, h - ((Rect)(ref win)).height);
			_win = win;
			return win;
		}

		private void HandleWindowMouse(Rect win, Rect handle)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Invalid comparison between Unknown and I4
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			Event current = Event.current;
			if ((int)current.rawType == 1 && (_dragging || _resizing))
			{
				_dragging = (_resizing = false);
				_state.X = ((Rect)(ref _win)).x / _screenW;
				_state.Y = ((Rect)(ref _win)).y / _screenH;
				_state.W = ((Rect)(ref _win)).width / _screenW;
				_state.H = ((Rect)(ref _win)).height / _screenH;
				MarkState();
				current.Use();
			}
			else
			{
				if (_modalLines != null)
				{
					return;
				}
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((Rect)(ref win)).x, ((Rect)(ref win)).y, ((Rect)(ref win)).width, 60f);
				if ((int)current.type == 0 && current.button == 0)
				{
					if (((Rect)(ref handle)).Contains(current.mousePosition))
					{
						_resizing = true;
						_win = win;
						current.Use();
					}
					else if (((Rect)(ref val)).Contains(current.mousePosition))
					{
						_dragging = true;
						_win = win;
						_dragOffset = current.mousePosition - ((Rect)(ref win)).position;
						current.Use();
					}
				}
				else if ((int)current.type == 3 && (_dragging || _resizing))
				{
					if (_dragging)
					{
						((Rect)(ref _win)).position = current.mousePosition - _dragOffset;
					}
					else
					{
						((Rect)(ref _win)).width = current.mousePosition.x - ((Rect)(ref _win)).x + 6f;
						((Rect)(ref _win)).height = current.mousePosition.y - ((Rect)(ref _win)).y + 6f;
					}
					current.Use();
				}
			}
		}

		private void ResetWindow()
		{
			_state.X = (_state.Y = (_state.W = (_state.H = 0f)));
			MarkState();
		}

		private void DrawHeader()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Expected O, but got Unknown
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.Label(new GUIContent(UiText.T("title"), UiText.T("tip_move_window")), Styles.Title, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.Label(new GUIContent("Server Configuration Manager  ·  v1.0.3", UiText.T("tip_move_window")), Styles.Meta, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.EndVertical();
			GUILayout.FlexibleSpace();
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			var (text, color, text2) = SessionStatus();
			HeaderBadge(new GUIContent(text, text2), color);
			if (SyncClient.Mode == SessionMode.ClientSynced || (SyncClient.Mode == SessionMode.Host && !SyncClient.SoloHost))
			{
				bool actsAsAdmin = SyncClient.ActsAsAdmin;
				HeaderBadge(new GUIContent(actsAsAdmin ? UiText.T("role_admin") : UiText.T("role_player"), actsAsAdmin ? UiText.T("tip_role_admin") : UiText.T("tip_role_player")), actsAsAdmin ? Styles.Accent : Styles.Blue);
			}
			GUILayout.Space(8f);
			if (_state.W > 0.01f && GUILayout.Button(new GUIContent(UiText.T("reset_window"), UiText.T("tip_reset_window")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.ExpandWidth(false),
				GUILayout.Height(26f)
			}))
			{
				Defer(ResetWindow);
			}
			if (GUILayout.Button(new GUIContent("X", UiText.T("close")), Styles.ButtonDanger, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(34f),
				GUILayout.Height(26f)
			}))
			{
				Defer(Close);
			}
			GUILayout.EndHorizontal();
			GUILayout.FlexibleSpace();
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
		}

		private static void HeaderBadge(GUIContent content, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			GUIStyle val = Styles.BadgeColored(color);
			Vector2 val2 = val.CalcSize(content);
			GUILayout.Label(content, val, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(Mathf.Ceil(val2.x) + 2f),
				GUILayout.Height(Mathf.Max(26f, Mathf.Ceil(val2.y)))
			});
		}

		private static void MiddleLabel(string text, GUIStyle style)
		{
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.FlexibleSpace();
			GUILayout.Label(text, style, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.FlexibleSpace();
			GUILayout.EndVertical();
		}

		private (string, Color, string) SessionStatus()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			switch (SyncClient.Mode)
			{
			case SessionMode.Host:
				if (SyncClient.SoloHost)
				{
					return (UiText.T("status_solo"), Styles.Muted, UiText.T("tip_status_solo"));
				}
				return (UiText.T("status_host"), Styles.Green, UiText.T("tip_status_host"));
			case SessionMode.ClientWaiting:
				return (UiText.T("status_waiting"), Styles.Orange, UiText.T("tip_status_waiting"));
			case SessionMode.ClientSynced:
				return (UiText.T("status_synced", SyncClient.ForcedCount), Styles.Green, UiText.T("tip_status_synced"));
			case SessionMode.ClientNoMod:
				if (SyncClient.ServerProtocolMismatch == 0)
				{
					return (UiText.T("status_no_mod"), Styles.Orange, UiText.T("banner_no_mod_fix"));
				}
				return (UiText.T("status_protocol"), Styles.Red, ProtocolFix());
			default:
				return (UiText.T("status_offline"), Styles.Muted, UiText.T("tip_status_offline"));
			}
		}

		private static string ProtocolFix()
		{
			return UiText.T((SyncClient.ServerProtocolMismatch > 8) ? "banner_protocol_fix_you" : "banner_protocol_fix_server");
		}

		private void DrawToolbar()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Expected O, but got Unknown
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Expected O, but got Unknown
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Expected O, but got Unknown
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Expected O, but got Unknown
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("search"), Styles.Subtitle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUI.SetNextControlName("uscm_search");
			_searchInput = GUILayout.TextField(_searchInput, Styles.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) });
			if (GUILayout.Button(new GUIContent("x", UiText.T("search_clear")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
			{
				Defer(delegate
				{
					_searchInput = "";
					_clearFocus = true;
				});
			}
			GUILayout.Space(10f);
			if (GUILayout.Button(new GUIContent(Check(_onlyModifiedInput) + UiText.T("filter_modified"), UiText.T("tip_filter_modified")), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					_onlyModifiedInput = !_onlyModifiedInput;
					MarkState();
				});
			}
			if (GUILayout.Button(new GUIContent(Check(_showAdvancedInput) + UiText.T("filter_advanced"), UiText.T("tip_filter_advanced")), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					_showAdvancedInput = !_showAdvancedInput;
					MarkState();
				});
			}
			if (EditService.CanManageAccess && GUILayout.Button(new GUIContent(Check(_showHiddenInput) + UiText.T("filter_hidden"), UiText.T("tip_filter_hidden")), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					_showHiddenInput = !_showHiddenInput;
					MarkState();
				});
			}
			bool all = !_onlyModifiedInput && _showAdvancedInput && (_showHiddenInput || !EditService.CanManageAccess);
			if (GUILayout.Button(new GUIContent(Check(all) + UiText.T("filter_all"), UiText.T("tip_filter_all")), all ? Styles.ButtonAccent : Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					if (all)
					{
						_showAdvancedInput = false;
					}
					else
					{
						_onlyModifiedInput = false;
						_showAdvancedInput = true;
						_showHiddenInput = true;
					}
					MarkState();
				});
			}
			if (GUILayout.Button(new GUIContent(Check(_state.DraftMode) + UiText.T("draft_mode"), UiText.T("tip_draft_mode")), _state.DraftMode ? Styles.ButtonAccent : Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					_state.DraftMode = !_state.DraftMode;
					MarkState();
				});
			}
			if (PlayerView && GUILayout.Button(new GUIContent(Check(_state.ShowDetails) + UiText.T("filter_details"), UiText.T("tip_filter_details")), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					_state.ShowDetails = !_state.ShowDetails;
					MarkState();
				});
			}
			GUILayout.FlexibleSpace();
			if (TranslationService.Pending > 0)
			{
				GUILayout.Label(UiText.T("tr_pending", TranslationService.Pending), Styles.Link, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			}
			bool flag = SyncClient.Mode == SessionMode.ClientSynced || (SyncClient.Mode == SessionMode.Host && !SyncClient.SoloHost);
			if (GUILayout.Button(new GUIContent(UiText.T("copy_changes"), UiText.T(flag ? "tip_copy_changes_server" : "tip_copy_changes_local")), Styles.Button, Array.Empty<GUILayoutOption>()))
			{
				Defer(CopyChanges);
			}
			bool enabled = GUI.enabled;
			GUI.enabled = enabled && ChangeHistory.CanUndo;
			if (GUILayout.Button(new GUIContent(UiText.T("undo"), UiText.T("tip_undo")), Styles.Button, Array.Empty<GUILayoutOption>()))
			{
				Defer(EditService.Undo);
			}
			GUI.enabled = enabled;
			GUILayout.EndHorizontal();
		}

		private static string Check(bool on)
		{
			if (!on)
			{
				return "[  ] ";
			}
			return "[x] ";
		}

		private void DrawSidebar(float width, float height)
		{
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical(Styles.Sidebar, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(width),
				GUILayout.Height(height)
			});
			NavButton("@home", UiText.T("nav_home"), UiText.T("tip_nav_home"));
			NavButton("@pinned", UiText.T("nav_pinned", _listing.Pinned.Count), UiText.T("tip_nav_pinned"));
			NavButton("@changed", UiText.T("nav_changed", _changedCount), UiText.T("tip_nav_changed"));
			if (SyncClient.Mode == SessionMode.ClientSynced)
			{
				NavButton("@news", UiText.T("nav_news", _newsCount), UiText.T("tip_nav_news"), _newsCount > 0);
			}
			GUILayout.Space(6f);
			Listing listing = _listing;
			GUILayout.Label(UiText.T("mods_count", listing.Mods.Count), Styles.Subtitle, Array.Empty<GUILayoutOption>());
			_modScroll = GUILayout.BeginScrollView(_modScroll, Array.Empty<GUILayoutOption>());
			for (int i = 0; i < listing.Mods.Count; i++)
			{
				ModInfo modInfo = listing.Mods[i];
				GUIStyle val = ((modInfo.Guid == _view && _search.Length == 0) ? Styles.ModButtonSelected : Styles.ModButton);
				if (GUILayout.Button(listing.Labels[i], val, Array.Empty<GUILayoutOption>()))
				{
					string guid = modInfo.Guid;
					Defer(delegate
					{
						Navigate(guid);
					});
				}
			}
			if (SyncClient.AdminWaitingDevcommands && (SyncClient.Mode == SessionMode.Host || SyncClient.Mode == SessionMode.ClientSynced))
			{
				GUILayout.Label(UiText.T("sidebar_admin_hint"), Styles.Desc, Array.Empty<GUILayoutOption>());
			}
			else if (SyncClient.Mode == SessionMode.ClientSynced && !SyncClient.ActsAsAdmin && listing.Mods.Count == 0)
			{
				GUILayout.Label(UiText.T("sidebar_nothing_released"), Styles.Desc, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
		}

		private void NavButton(string view, string label, string tip, bool highlight = false)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			GUIStyle val = ((_view == view && _search.Length == 0) ? Styles.NavButtonSelected : (highlight ? Styles.ButtonAccent : Styles.NavButton));
			if (GUILayout.Button(new GUIContent(label, tip), val, Array.Empty<GUILayoutOption>()))
			{
				Defer(delegate
				{
					Navigate(view);
				});
			}
		}

		private ModInfo CurrentMod()
		{
			if (!IsSpecial(_view))
			{
				return ConfigCatalog.GetMods().FirstOrDefault((ModInfo m) => m.Guid == _view && m.Entries.Any(Listed));
			}
			return null;
		}

		private static bool Shown(EntryInfo e)
		{
			if (e.Browsable)
			{
				return !SyncClient.HiddenForMe(e);
			}
			return false;
		}

		private bool Listed(EntryInfo e)
		{
			if (Shown(e))
			{
				if (!_showHidden)
				{
					return !SyncClient.IsHidden(e);
				}
				return true;
			}
			return false;
		}

		private bool Visible(EntryInfo e)
		{
			if (!Listed(e))
			{
				return false;
			}
			if (e.Advanced && !_showAdvanced)
			{
				return false;
			}
			if (_onlyModified && e.IsDefault)
			{
				return false;
			}
			return true;
		}

		private bool MatchesSearch(EntryInfo e)
		{
			string q = _search;
			if (!Has(e.DisplayName) && !Has(e.Key) && !Has(Sec(e)) && !Has(e.ModName) && !Has(e.Description) && !Has(TranslationService.CachedTranslation(e)))
			{
				return Has(SyncClient.MetaOf(e)?.Note);
			}
			return true;
			bool Has(string s)
			{
				if (!string.IsNullOrEmpty(s))
				{
					return s.IndexOf(q, StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return false;
			}
		}

		private List<EntryInfo> PinnedEntries()
		{
			List<EntryInfo> list = new List<EntryInfo>();
			foreach (string item in _state.Pinned)
			{
				if (ConfigCatalog.TryGet(item, out var e) && Listed(e))
				{
					list.Add(e);
				}
			}
			return list;
		}

		private bool IsPinned(EntryInfo e)
		{
			return _state.Pinned.Contains(e.Id);
		}

		private void TogglePin(EntryInfo e)
		{
			if (!_state.Pinned.Remove(e.Id))
			{
				_state.Pinned.Add(e.Id);
			}
			MarkState();
			BumpData();
		}

		private void DrawMain(float width, float height)
		{
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(width),
				GUILayout.Height(height)
			});
			DrawConnectionBanner();
			DrawPendingBanners();
			DrawDraftBar();
			if (_showErrors)
			{
				DrawErrorLog();
			}
			if (_search.Length > 0)
			{
				DrawGroupedList(_main, width);
				GUILayout.EndVertical();
				return;
			}
			switch (_view)
			{
			case "@home":
				DrawHome(width);
				break;
			case "@pinned":
			case "@changed":
				DrawGroupedList(_main, width);
				break;
			case "@news":
				DrawNewsView(width);
				break;
			default:
				DrawModView(width);
				break;
			}
			GUILayout.EndVertical();
		}

		private void DrawModView(float width)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			MainView main = _main;
			ModInfo mod = main.Mod;
			if (mod == null)
			{
				GUILayout.Label(UiText.T("no_configs"), Styles.Subtitle, Array.Empty<GUILayoutOption>());
				if ((int)Event.current.type != 7)
				{
					return;
				}
				Defer(delegate
				{
					if (CurrentMod() == null && !IsSpecial(_view))
					{
						Navigate("@home");
					}
				});
				return;
			}
			DrawModHeader(main);
			List<SectionView> sections = main.Sections;
			if (sections.Count == 0)
			{
				GUILayout.Label(_onlyModified ? UiText.T("no_modified") : UiText.T("no_configs"), Styles.Subtitle, Array.Empty<GUILayoutOption>());
				return;
			}
			bool flag = sections.Count > 6;
			List<string> names = main.Names;
			string value;
			string text = (_state.Sections.TryGetValue(mod.Guid, out value) ? value : names[0]);
			if (!names.Contains(text))
			{
				text = names[0];
			}
			if (flag)
			{
				DrawSectionTabs(mod, main, text);
			}
			BeginCull(main.CullKey + (flag ? ("|tab:" + text) : ""));
			_mainScroll = GUILayout.BeginScrollView(_mainScroll, Array.Empty<GUILayoutOption>());
			foreach (SectionView item in sections)
			{
				if (flag && item.Key != text)
				{
					continue;
				}
				if (!flag && sections.Count > 1)
				{
					if (!DrawSectionHeader(mod, item))
					{
						continue;
					}
				}
				else
				{
					DrawSectionActions(mod, item);
				}
				foreach (EntryInfo entry in item.Entries)
				{
					DrawEntryCulled(entry, width - 40f);
				}
				GUILayout.Space(6f);
			}
			GUILayout.EndScrollView();
			EndCull();
		}

		private void DrawGroupedList(MainView view, float width)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label(view.Title, Styles.Subtitle, Array.Empty<GUILayoutOption>());
			if (view.Groups.Count == 0)
			{
				GUILayout.Space(10f);
				GUILayout.Label(view.Empty, Styles.Desc, Array.Empty<GUILayoutOption>());
				return;
			}
			BeginCull(view.CullKey);
			_mainScroll = GUILayout.BeginScrollView(_mainScroll, Array.Empty<GUILayoutOption>());
			foreach (GroupView group in view.Groups)
			{
				GUILayout.Label(group.Header, Styles.Link, Array.Empty<GUILayoutOption>());
				foreach (EntryInfo entry in group.Entries)
				{
					DrawEntryCulled(entry, width - 40f);
				}
				GUILayout.Space(6f);
			}
			GUILayout.EndScrollView();
			EndCull();
		}

		private void DrawNewsView(float width)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			if (_main.Count > 0)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.Label(UiText.T("view_news_hint"), Styles.Meta, Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(new GUIContent(UiText.T("mark_seen"), UiText.T("tip_mark_seen")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						SyncClient.MarkAllSeen();
						RecalcCounts();
					});
				}
				GUILayout.EndHorizontal();
			}
			DrawGroupedList(_main, width);
		}

		private static string Sec(EntryInfo e)
		{
			return e.SectionDisplay ?? e.Section;
		}

		private static IEnumerable<EntryInfo> Sort(IEnumerable<EntryInfo> list)
		{
			return list.OrderByDescending((EntryInfo e) => e.Order).ThenBy<EntryInfo, string>((EntryInfo e) => e.DisplayName, StringComparer.OrdinalIgnoreCase);
		}

		private void DrawHome(float width)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			_mainScroll = GUILayout.BeginScrollView(_mainScroll, Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("home_title"), Styles.Title, Array.Empty<GUILayoutOption>());
			GUILayout.Label(HomeSituation(), Styles.Desc, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(width - 40f) });
			GUILayout.Space(10f);
			List<ModInfo> mods = _listing.Mods;
			if (mods.Count == 0)
			{
				GUILayout.Label(UiText.T((ConfigCatalog.GetMods().Count <= 0 || (SyncClient.Mode != SessionMode.ClientSynced && SyncClient.Mode != SessionMode.Host)) ? "home_no_mods" : (SyncClient.AdminWaitingDevcommands ? "home_nothing_released_admin" : "home_nothing_released")), Styles.Desc, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(width - 40f) });
				GUILayout.EndScrollView();
				return;
			}
			int count = _listing.Pinned.Count;
			int value = _pendingRelog.Count + _pendingRestart.Count;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			Stat(mods.Count, UiText.T("home_stat_mods"), UiText.T("tip_home_stat_mods"), null);
			Stat(_listing.TotalListed, UiText.T("home_stat_configs"), UiText.T("tip_home_stat_configs"), null);
			Stat(_changedCount, UiText.T("home_stat_changed"), UiText.T("tip_nav_changed"), "@changed");
			Stat(count, UiText.T("home_stat_pinned"), UiText.T("tip_nav_pinned"), "@pinned");
			if (SyncClient.Mode == SessionMode.ClientSynced)
			{
				Stat(_newsCount, UiText.T("home_stat_news"), UiText.T("tip_nav_news"), "@news");
			}
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				Stat(value, UiText.T("home_stat_pending"), UiText.T("tip_home_stat_pending"), null);
			}
			if (_drafts.Count > 0)
			{
				Stat(_drafts.Count, UiText.T("home_stat_drafts"), UiText.T("tip_draft_mode"), null);
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			if (_newsCount > 0)
			{
				GUILayout.BeginHorizontal(Styles.BannerInfo, Array.Empty<GUILayoutOption>());
				GUILayout.Label(UiText.T("home_news", _newsCount), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(UiText.T("home_news_open"), Styles.ButtonAccent, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						Navigate("@news");
					});
				}
				GUILayout.EndHorizontal();
				GUILayout.Space(8f);
			}
			List<EntryInfo> pinned = _listing.Pinned;
			if (pinned.Count > 0)
			{
				GUILayout.Label(UiText.T("home_pinned_title"), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
				foreach (EntryInfo item in pinned.Take(8))
				{
					DrawEntry(item, width - 40f);
				}
				if (pinned.Count > 8 && GUILayout.Button(UiText.T("home_pinned_more", pinned.Count - 8), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						Navigate("@pinned");
					});
				}
				GUILayout.Space(8f);
			}
			GUILayout.BeginVertical(Styles.Sidebar, Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("home_tips_title"), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
			string[] array = new string[4] { "home_tip_pick", "home_tip_badges", "home_tip_pin", "home_tip_draft" };
			foreach (string key in array)
			{
				GUILayout.Label("•  " + UiText.T(key), Styles.Desc, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(width - 60f) });
			}
			if (EditService.CanManageAccess)
			{
				string[] array2 = new string[2] { "home_tip_admin_lock", "home_tip_admin_note" };
				foreach (string key2 in array2)
				{
					GUILayout.Label("•  " + UiText.T(key2), Styles.Desc, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(width - 60f) });
				}
			}
			GUILayout.EndVertical();
			GUILayout.Space(8f);
			GUILayout.BeginVertical(Styles.Sidebar, Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("home_shortcuts_title"), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("home_shortcuts", ((object)Plugin.PanelHotkey.Value/*cast due to .constrained prefix*/).ToString()), Styles.Desc, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(width - 60f) });
			GUILayout.EndVertical();
			GUILayout.EndScrollView();
		}

		private string HomeSituation()
		{
			switch (SyncClient.Mode)
			{
			case SessionMode.Host:
				if (!SyncClient.SoloHost)
				{
					return UiText.T("home_mode_host");
				}
				return UiText.T("home_mode_solo");
			case SessionMode.ClientWaiting:
				return UiText.T("home_mode_waiting");
			case SessionMode.ClientSynced:
				if (!SyncClient.ActsAsAdmin)
				{
					return UiText.T("home_mode_player");
				}
				return UiText.T("home_mode_admin");
			case SessionMode.ClientNoMod:
				if (SyncClient.ServerProtocolMismatch == 0)
				{
					return UiText.T("home_mode_no_mod");
				}
				return UiText.T("banner_protocol", SyncClient.ServerProtocolMismatch, 8) + "\n" + ProtocolFix();
			default:
				return UiText.T("home_mode_offline");
			}
		}

		private void Stat(int value, string label, string tip, string view)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			GUIContent val = new GUIContent(value.ToString(CultureInfo.InvariantCulture) + "\n" + label, tip);
			if (GUILayout.Button(val, Styles.StatButton, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(118f),
				GUILayout.MinHeight(64f)
			}) && view != null)
			{
				Defer(delegate
				{
					Navigate(view);
				});
			}
		}

		private void DrawConnectionBanner()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			if (SyncClient.Mode == SessionMode.ClientNoMod && !(_view == "@home"))
			{
				bool flag = SyncClient.ServerProtocolMismatch != 0;
				GUILayout.BeginHorizontal(flag ? Styles.BannerDanger : Styles.BannerInfo, Array.Empty<GUILayoutOption>());
				string text = (flag ? UiText.T("banner_protocol", SyncClient.ServerProtocolMismatch, 8) : UiText.T("banner_no_mod"));
				GUILayout.Label(new GUIContent(text, flag ? ProtocolFix() : UiText.T("banner_no_mod_fix")), Styles.Meta, Array.Empty<GUILayoutOption>());
				GUILayout.EndHorizontal();
				GUILayout.Space(6f);
			}
		}

		private void DrawPendingBanners()
		{
			//IL_063f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Expected O, but got Unknown
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			//IL_0454: Expected O, but got Unknown
			bool flag = (Object)(object)ZNet.instance != (Object)null;
			bool flag2 = SyncClient.Mode == SessionMode.Host && !SyncClient.SoloHost;
			List<(string, string, string)> modVersionMismatch = SyncClient.ModVersionMismatch;
			List<(string, string)> modsMissing = SyncClient.ModsMissing;
			if (SyncClient.Mode == SessionMode.ClientSynced && (modVersionMismatch.Count > 0 || modsMissing.Count > 0))
			{
				GUILayout.BeginVertical((modVersionMismatch.Count > 0) ? Styles.BannerDanger : Styles.Banner, Array.Empty<GUILayoutOption>());
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				List<string> list = new List<string>();
				if (modVersionMismatch.Count > 0)
				{
					list.Add(UiText.T("mods_banner_version", modVersionMismatch.Count));
				}
				if (modsMissing.Count > 0)
				{
					list.Add(UiText.T("mods_banner_missing", modsMissing.Count));
				}
				GUILayout.Label(new GUIContent(string.Join("   ·   ", list), UiText.T("tip_mods_banner")), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(_showModDiff ? UiText.T("hide_list") : UiText.T("show_list"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						_showModDiff = !_showModDiff;
					});
				}
				GUILayout.EndHorizontal();
				if (_showModDiff)
				{
					foreach (var item in modVersionMismatch.Take(40))
					{
						GUILayout.Label("•  " + UiText.T("mod_version_line", item.Item1, item.Item2, item.Item3), Styles.Desc, Array.Empty<GUILayoutOption>());
					}
					foreach (var item2 in modsMissing.Take(40))
					{
						GUILayout.Label("•  " + UiText.T("mod_missing_line", item2.Item1, item2.Item2), Styles.Meta, Array.Empty<GUILayoutOption>());
					}
					if (modVersionMismatch.Count > 0)
					{
						GUILayout.Label(UiText.T("tip_mods_banner"), Styles.Meta, Array.Empty<GUILayoutOption>());
					}
					if (modsMissing.Count > 0)
					{
						GUILayout.Label(UiText.T("tip_mods_missing"), Styles.Meta, Array.Empty<GUILayoutOption>());
					}
				}
				GUILayout.EndVertical();
				GUILayout.Space(6f);
			}
			if (flag && (_pendingRelog.Count > 0 || _pendingRestart.Count > 0))
			{
				GUILayout.BeginVertical(Styles.Banner, Array.Empty<GUILayoutOption>());
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				List<string> list2 = new List<string>();
				if (_pendingRelog.Count > 0)
				{
					list2.Add(UiText.T(flag2 ? "pending_reload_world" : "pending_relog", _pendingRelog.Count));
				}
				if (_pendingRestart.Count > 0)
				{
					list2.Add(UiText.T("pending_restart_game", _pendingRestart.Count));
				}
				GUILayout.Label(string.Join("   ·   ", list2), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button(_showClientPending ? UiText.T("hide_list") : UiText.T("show_list"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						_showClientPending = !_showClientPending;
					});
				}
				if (_pendingRelog.Count > 0 && GUILayout.Button(new GUIContent(flag2 ? UiText.T("leave_world") : UiText.T("relog_now"), flag2 ? UiText.T("tip_leave_world") : ((JoinTracker.CanReconnect && Plugin.AutoReconnect.Value) ? UiText.T("tip_relog_now") : UiText.T("tip_relog_manual"))), Styles.ButtonAccent, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						Close();
						JoinTracker.RelogNow();
					});
				}
				List<EntryInfo> adoptable = _pendingRestart.Where(SyncClient.CanAdopt).ToList();
				if (adoptable.Count > 0 && GUILayout.Button(new GUIContent(UiText.T("adopt_values", adoptable.Count), UiText.T("tip_adopt_values")), Styles.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					Defer(delegate
					{
						Toast(UiText.T("msg_adopted", SyncClient.AdoptServerValues(adoptable)), error: false);
					});
				}
				GUILayout.EndHorizontal();
				if (_showClientPending)
				{
					foreach (EntryInfo item3 in _pendingRelog.Concat(_pendingRestart).Take(30))
					{
						GUILayout.Label("•  " + (SyncClient.HiddenForMe(item3) ? UiText.T("hidden_item") : (item3.ModName + " › " + item3.DisplayName)) + "   (" + ApplyLabel(item3.Apply) + ")", Styles.Meta, Array.Empty<GUILayoutOption>());
					}
				}
				GUILayout.EndVertical();
				GUILayout.Space(6f);
			}
			if (_serverPending.Count <= 0)
			{
				return;
			}
			GUILayout.BeginVertical(Styles.BannerDanger, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(new GUIContent(UiText.T(flag2 ? "server_pending_host" : "server_pending", _serverPending.Count), UiText.T("tip_server_pending")), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(_showServerPending ? UiText.T("hide_list") : UiText.T("show_list"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(delegate
				{
					_showServerPending = !_showServerPending;
				});
			}
			GUILayout.EndHorizontal();
			if (_showServerPending)
			{
				foreach (var item4 in _serverPending.Take(30))
				{
					GUILayout.Label("•  " + item4.name, Styles.Meta, Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.EndVertical();
			GUILayout.Space(6f);
		}

		private void DrawErrorLog()
		{
			GUILayout.BeginVertical(Styles.BannerDanger, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(UiText.T("errors_title"), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(UiText.T("errors_clear"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(delegate
				{
					_errorLog.Clear();
					_showErrors = false;
				});
			}
			if (GUILayout.Button(UiText.T("hide_list"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(delegate
				{
					_showErrors = false;
				});
			}
			GUILayout.EndHorizontal();
			for (int num = _errorLog.Count - 1; num >= 0; num--)
			{
				GUILayout.Label(TimeText.Clock(_errorLog[num].at) + "   " + _errorLog[num].text, Styles.Meta, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Label(UiText.T("errors_log_hint"), Styles.Meta, Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			GUILayout.Space(6f);
		}

		private void CopyChanges()
		{
			ChangeSummary.Result result = ChangeSummary.Build();
			if (result.Count == 0)
			{
				Toast(UiText.T("msg_nothing_changed"), error: false);
				return;
			}
			GUIUtility.systemCopyBuffer = result.Text;
			string text = (result.Truncated ? UiText.T("msg_copied_truncated", result.Count, "BepInEx/config/Ultimate_SCM/changes_summary.txt") : UiText.T("msg_copied", result.Count));
			if (result.HiddenSkipped > 0)
			{
				text = text + " · " + UiText.T("msg_hidden_skipped", result.HiddenSkipped);
			}
			Toast(text, error: false);
		}

		private static string ApplyLabel(ApplyMode m)
		{
			return m switch
			{
				ApplyMode.Live => UiText.T("apply_live"), 
				ApplyMode.Relog => UiText.T("apply_relog"), 
				ApplyMode.RestartGame => UiText.T("apply_restart"), 
				_ => UiText.T("apply_unknown"), 
			};
		}

		private string ValueOf(EntryInfo e)
		{
			if (!_drafts.TryGetValue(e.Id, out var value))
			{
				return LiveValue(e);
			}
			return value;
		}

		private static string LiveValue(EntryInfo e)
		{
			if (!EditService.GoesToServer(e))
			{
				return e.CurrentValue;
			}
			return SyncClient.ServerValueOf(e) ?? e.CurrentValue;
		}

		private static bool SameValue(string a, string b)
		{
			return string.Equals((a ?? "").Trim(), (b ?? "").Trim(), StringComparison.Ordinal);
		}

		private void Change(EntryInfo e, string value)
		{
			if (_state.DraftMode)
			{
				Defer(delegate
				{
					AddDraft(e, value);
				});
				return;
			}
			Defer(delegate
			{
				_drafts.Remove(e.Id);
				EditService.Set(e, value);
			});
		}

		private bool AddDraft(EntryInfo e, string value)
		{
			if (!EditService.CanEdit(e))
			{
				Toast(UiText.T("msg_denied_locked", e.DisplayName), error: true, "msg_denied_locked");
				return false;
			}
			if (!EditService.Validate(e, value, out var normalized, out var message))
			{
				Toast(e.DisplayName + ": " + message, error: true);
				return false;
			}
			if (SameValue(normalized, LiveValue(e)))
			{
				_drafts.Remove(e.Id);
			}
			else
			{
				_drafts[e.Id] = normalized;
			}
			return true;
		}

		private void PruneDrafts()
		{
			if (_drafts.Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (string item in _drafts.Keys.ToList())
			{
				if (!ConfigCatalog.TryGet(item, out var e) || SameValue(LiveValue(e), _drafts[item]))
				{
					_drafts.Remove(item);
				}
				else if (!EditService.CanEdit(e))
				{
					_drafts.Remove(item);
					num++;
				}
			}
			if (num > 0)
			{
				Toast(UiText.T("msg_draft_lost", num), error: true);
			}
		}

		private void PruneBuffers()
		{
			if (_buffers.Count == 0)
			{
				return;
			}
			foreach (string item in _buffers.Keys.ToList())
			{
				if (!ConfigCatalog.TryGet(item, out var e) || SameValue(_buffers[item], ValueOf(e)))
				{
					_buffers.Remove(item);
				}
			}
		}

		private void ApplyDrafts()
		{
			PruneDrafts();
			List<KeyValuePair<EntryInfo, string>> list = new List<KeyValuePair<EntryInfo, string>>();
			foreach (KeyValuePair<string, string> draft in _drafts)
			{
				if (ConfigCatalog.TryGet(draft.Key, out var e))
				{
					list.Add(new KeyValuePair<EntryInfo, string>(e, draft.Value));
				}
			}
			if (list.Count == 0)
			{
				_drafts.Clear();
			}
			else
			{
				if (!EditService.SetMany(list))
				{
					return;
				}
				foreach (KeyValuePair<EntryInfo, string> item in list)
				{
					_drafts.Remove(item.Key.Id);
				}
				_showDraftList = false;
			}
		}

		private void DrawDraftBar()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Expected O, but got Unknown
			if (_drafts.Count == 0)
			{
				return;
			}
			GUILayout.BeginVertical(Styles.BannerDraft, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(new GUIContent(UiText.T("draft_bar", _drafts.Count), UiText.T("tip_draft_mode")), Styles.EntryTitle, Array.Empty<GUILayoutOption>());
			EntryInfo e2;
			int num = _drafts.Keys.Count((string id2) => ConfigCatalog.TryGet(id2, out e2) && EditService.AffectsOthers(e2));
			if (num > 0)
			{
				GUILayout.Label(UiText.T("draft_server_hint", num), Styles.Meta, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			}
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(_showDraftList ? UiText.T("hide_list") : UiText.T("show_list"), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(delegate
				{
					_showDraftList = !_showDraftList;
				});
			}
			if (ConfirmButton("draft_discard", UiText.T("draft_discard"), UiText.T("confirm"), UiText.T("tip_draft_discard")))
			{
				Defer(delegate
				{
					_drafts.Clear();
					_showDraftList = false;
				});
			}
			if (GUILayout.Button(new GUIContent(UiText.T("draft_apply", _drafts.Count), UiText.T("tip_draft_apply")), Styles.ButtonAccent, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(ApplyDrafts);
			}
			GUILayout.EndHorizontal();
			if (_showDraftList)
			{
				foreach (KeyValuePair<string, string> item in _drafts.Take(40).ToList())
				{
					if (!ConfigCatalog.TryGet(item.Key, out var e))
					{
						continue;
					}
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label("•  " + e.ModName + " › " + e.DisplayName + ":  " + Shorten(e.CurrentValue, 30) + "  →  " + Shorten(item.Value, 30), Styles.Meta, Array.Empty<GUILayoutOption>());
					GUILayout.FlexibleSpace();
					string id = item.Key;
					if (GUILayout.Button(new GUIContent("x", UiText.T("tip_draft_remove")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
					{
						Defer(delegate
						{
							_drafts.Remove(id);
						});
					}
					GUILayout.EndHorizontal();
				}
			}
			GUILayout.EndVertical();
			GUILayout.Space(6f);
		}

		private void ResetEntries(List<EntryInfo> list)
		{
			if (!_state.DraftMode)
			{
				EditService.ResetToDefault(list);
				return;
			}
			int num = 0;
			foreach (EntryInfo item in list)
			{
				if (EditService.CanEdit(item) && !SameValue(ValueOf(item), item.DefaultValue) && AddDraft(item, item.DefaultValue))
				{
					num++;
				}
			}
			Toast((num > 0) ? UiText.T("msg_drafted", num) : UiText.T("msg_nothing_to_reset"), error: false);
		}

		private void DrawModHeader(MainView view)
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Expected O, but got Unknown
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Expected O, but got Unknown
			ModInfo mod = view.Mod;
			GUILayout.BeginVertical(Styles.Sidebar, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(mod.Name, Styles.Title, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			GUILayout.Label("v" + mod.Version + "   " + mod.Guid, Styles.Subtitle, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			if (mod.OnlyOnServer)
			{
				GUILayout.Label(UiText.T("mod_only_on_server"), Styles.Meta, Array.Empty<GUILayoutOption>());
			}
			if (mod.ExternalSync != null)
			{
				GUILayout.Label(view.ModMirrored ? UiText.T("mod_mirrored", mod.ExternalSync) : UiText.T("mod_external_sync", mod.ExternalSync), Styles.Meta, Array.Empty<GUILayoutOption>());
			}
			if (mod.Guid == "Igao.UltimateSCM")
			{
				GUILayout.Label(UiText.T("mod_own"), Styles.Meta, Array.Empty<GUILayoutOption>());
			}
			List<EntryInfo> all = view.Shown;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (view.ModTranslatable)
			{
				if (GUILayout.Button(new GUIContent(UiText.T("tr_all"), UiText.T("tip_tr_all", GameLanguage.Native(TranslationService.TargetName))), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
				{
					Defer(delegate
					{
						TranslationService.TranslateMany(all);
					});
				}
				if (GUILayout.Button(UiText.T("tr_show_original_all"), Styles.ButtonSmall, Array.Empty<GUILayoutOption>()))
				{
					Defer(delegate
					{
						TranslationService.ShowOriginalMany(all);
					});
				}
			}
			if (ConfirmButton("reset_mod_" + mod.Guid, UiText.T("reset_mod"), UiText.T("confirm"), UiText.T("tip_reset_mod")))
			{
				Defer(delegate
				{
					ResetEntries(all);
				});
			}
			DrawModFileButtons(view);
			if (mod.Guid == "Igao.UltimateSCM" && GUILayout.Button(new GUIContent(UiText.T("tr_test"), UiText.T("tip_tr_test")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Defer(TranslationService.TestServices);
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			if (ShowAccessControls(mod))
			{
				string guid = mod.Guid;
				bool flag = SyncClient.Policy.UnlockedMods.Contains(guid);
				bool flag2 = SyncClient.Policy.ShownMods.Contains(guid);
				Vis current = (flag ? Vis.Edit : (flag2 ? Vis.View : Vis.Hidden));
				string hint = (view.VisMixed ? UiText.T("vis_mixed") : null);
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				DrawVisibility(guid, isMod: true, current, canHide: true, canView: true, canEdit: true, hint);
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
				List<EntryInfo> suggested = view.Suggested;
				if (suggested.Count > 0 && !flag)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					if (GUILayout.Button(new GUIContent(UiText.T("unlock_suggested", suggested.Count), UiText.T("tip_unlock_suggested")), Styles.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
					{
						Defer(delegate
						{
							EditService.SetVisibility(suggested.Select((EntryInfo e) => (Id: e.Id, false, 2)).ToList());
						});
					}
					GUILayout.FlexibleSpace();
					GUILayout.EndHorizontal();
				}
			}
			GUILayout.EndVertical();
			GUILayout.Space(6f);
		}

		private void DrawVisibility(string target, bool isMod, Vis current, bool canHide, bool canV