Decompiled source of ChatPlus v1.0.0

ChatPlus.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ChatPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ChatPlus")]
[assembly: AssemblyTitle("ChatPlus")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ChatPlus;

internal static class ChatCommands
{
	public const string CommandApiGuid = "com.on-together-mods.commandapi";

	private static readonly string[] Names = new string[2] { "chatplus", "chp" };

	public static bool TryParse(string text, out string[] args)
	{
		args = null;
		if (string.IsNullOrEmpty(text) || text[0] != '/')
		{
			return false;
		}
		string[] array = text.Substring(1).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 0 || Array.IndexOf(Names, array[0].ToLowerInvariant()) < 0)
		{
			return false;
		}
		args = array.Skip(1).ToArray();
		return true;
	}

	public static void Execute(string[] args)
	{
		//IL_03af: Unknown result type (might be due to invalid IL or missing references)
		Plugin instance = Plugin.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return;
		}
		Lang current = Lang.Current;
		if (args.Length == 0)
		{
			instance.ToggleWindow();
			return;
		}
		switch (args[0].ToLowerInvariant())
		{
		case "ui":
		case "window":
		case "settings":
			instance.ToggleWindow();
			break;
		case "time":
		case "timestamps":
			Timestamps(instance, current, args);
			break;
		case "size":
			Size(instance, current, args);
			break;
		case "font":
		case "text":
			TextSize(instance, current, args);
			break;
		case "lines":
		case "limit":
		case "history":
			Lines(instance, current, args);
			break;
		case "clear":
			GameAccess.Notify(string.Format(current.ChatCleared, ChatLines.Clear()));
			break;
		case "clearhistory":
			instance.History.Clear();
			GameAccess.Notify(current.HistoryCleared);
			break;
		case "folder":
		case "open":
			instance.OpenHistoryFolder();
			break;
		case "reset":
			instance.ResetWindow();
			GameAccess.Notify(current.WindowReset);
			break;
		case "kw":
		case "keywords":
		case "keyword":
			Keyword(instance, current, args);
			break;
		case "status":
			GameAccess.Notify(instance.DescribeStatus(current));
			break;
		default:
			GameAccess.Notify(string.Format(current.Help, instance.WindowKey.Value));
			break;
		}
	}

	private static void Timestamps(Plugin plugin, Lang lang, string[] args)
	{
		string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "toggle");
		switch (text)
		{
		case "toggle":
		case "on":
		case "off":
			plugin.Timestamps.Value = ((text == "toggle") ? (!plugin.Timestamps.Value) : (text == "on"));
			GameAccess.Notify(plugin.Timestamps.Value ? lang.TimeOn : lang.TimeOff);
			return;
		case "format":
			if (args.Length >= 3)
			{
				string text2 = string.Join(" ", args, 2, args.Length - 2);
				plugin.TimeFormat.Value = text2;
				plugin.Timestamps.Value = true;
				GameAccess.Notify(string.Format(lang.TimeFormatSet, text2, ChatFormat.FormatTime(DateTime.Now, text2)));
				return;
			}
			break;
		}
		GameAccess.Notify(lang.UsageTime);
	}

	private static void Size(Plugin plugin, Lang lang, string[] args)
	{
		if (args.Length > 1 && args[1].Equals("reset", StringComparison.OrdinalIgnoreCase))
		{
			ChatWindow.ResetSize();
		}
		else
		{
			if (args.Length <= 1 || !TryParsePercent(args[1], out var percent))
			{
				GameAccess.Notify(lang.UsageSize);
				return;
			}
			int percent2 = plugin.Height.Value;
			if (args.Length > 2 && !TryParsePercent(args[2], out percent2))
			{
				GameAccess.Notify(lang.UsageSize);
				return;
			}
			plugin.Width.Value = percent;
			plugin.Height.Value = percent2;
		}
		GameAccess.Notify(string.Format(lang.SizeSet, plugin.Width.Value, plugin.Height.Value));
	}

	private static void TextSize(Plugin plugin, Lang lang, string[] args)
	{
		if (args.Length < 2 || !TryParsePercent(args[1], out var percent))
		{
			GameAccess.Notify(lang.UsageText);
			return;
		}
		plugin.TextSize.Value = percent;
		GameAccess.Notify(string.Format(lang.TextSizeSet, plugin.TextSize.Value));
	}

	private static void Lines(Plugin plugin, Lang lang, string[] args)
	{
		if (args.Length < 2 || !int.TryParse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			GameAccess.Notify(lang.UsageLimit);
			return;
		}
		int result2 = plugin.LocalLimit.Value;
		if (args.Length > 2 && !int.TryParse(args[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
		{
			GameAccess.Notify(lang.UsageLimit);
			return;
		}
		plugin.GlobalLimit.Value = result;
		plugin.LocalLimit.Value = result2;
		GameAccess.Notify(string.Format(lang.LimitsSet, plugin.GlobalLimit.Value, plugin.LocalLimit.Value));
	}

	private static void Keyword(Plugin plugin, Lang lang, string[] args)
	{
		string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "list");
		List<string> list = TextUtil.ParseKeywords((args.Length > 2) ? string.Join(" ", args, 2, args.Length - 2) : null);
		List<string> list2 = TextUtil.ParseKeywords(plugin.Keywords.Value);
		switch (text)
		{
		case "add":
			if (list.Count == 0)
			{
				break;
			}
			foreach (string word2 in list)
			{
				if (!list2.Exists((string w) => string.Equals(w, word2, StringComparison.OrdinalIgnoreCase)))
				{
					list2.Add(word2);
				}
			}
			plugin.Keywords.Value = TextUtil.JoinKeywords(list2);
			GameAccess.Notify(string.Format(lang.KeywordAdded, TextUtil.JoinKeywords(list)));
			return;
		case "remove":
		case "delete":
		{
			if (list.Count == 0)
			{
				break;
			}
			int num = 0;
			foreach (string word in list)
			{
				num += list2.RemoveAll((string w) => string.Equals(w, word, StringComparison.OrdinalIgnoreCase));
			}
			if (num == 0)
			{
				GameAccess.Notify(string.Format(lang.KeywordMissing, TextUtil.JoinKeywords(list)));
				return;
			}
			plugin.Keywords.Value = TextUtil.JoinKeywords(list2);
			GameAccess.Notify(string.Format(lang.KeywordRemoved, TextUtil.JoinKeywords(list)));
			return;
		}
		case "clear":
			plugin.Keywords.Value = string.Empty;
			GameAccess.Notify(lang.KeywordsCleared);
			return;
		case "list":
			GameAccess.Notify(string.Format(lang.KeywordsList, (list2.Count > 0) ? TextUtil.JoinKeywords(list2) : lang.None));
			return;
		}
		GameAccess.Notify(lang.UsageKeyword);
	}

	private static bool TryParsePercent(string text, out int percent)
	{
		return int.TryParse((text ?? string.Empty).TrimEnd('%'), NumberStyles.Integer, CultureInfo.InvariantCulture, out percent);
	}

	public static void RegisterWithCommandApi()
	{
		if (!Chainloader.PluginInfos.TryGetValue("com.on-together-mods.commandapi", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			return;
		}
		try
		{
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			Type type = assembly.GetType("CommandAPI.CommandRegistry");
			Type type2 = assembly.GetType("CommandAPI.Parameter");
			Type type3 = assembly.GetType("CommandAPI.ParameterType");
			if (type == null || type2 == null || type3 == null)
			{
				return;
			}
			MethodInfo method = type.GetMethod("Register", new Type[5]
			{
				typeof(string),
				typeof(string),
				typeof(Action<string[]>),
				typeof(string),
				type2.MakeArrayType()
			});
			if (!(method == null))
			{
				object obj = Enum.Parse(type3, "String");
				Array array = Array.CreateInstance(type2, 2);
				array.SetValue(Activator.CreateInstance(type2, "action", obj, true, null, null), 0);
				array.SetValue(Activator.CreateInstance(type2, "value", obj, true, null, null), 1);
				Action<string[]> action = Execute;
				string[] names = Names;
				foreach (string text in names)
				{
					method.Invoke(null, new object[5] { text, "ChatPlus", action, "Chat Plus: settings window, message time, chat size, text size, lines kept, clear, history folder", array });
				}
				Plugin.Log.LogInfo((object)"Registered /chatplus with CommandAPI.");
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not register with CommandAPI (the command still works): " + ex.Message));
		}
	}
}
internal enum ChatKind
{
	Player,
	Own,
	Notification
}
internal sealed class ChatEntry
{
	public DateTime Time;

	public bool IsLocal;

	public ChatKind Kind;

	public string SteamId = string.Empty;

	public string Name = string.Empty;

	public string Message = string.Empty;

	public string Line = string.Empty;
}
internal sealed class LineStyle
{
	public bool Timestamps = true;

	public string TimeFormat = "HH:mm";

	public string TimeColor;

	public int TimeSize = 100;

	public bool TimeOnNotifications = true;

	public string DateFormat = "dd.MM";

	public bool Highlight;

	public string HighlightColor;

	public string MyName = string.Empty;

	public IList<string> Keywords = new List<string>();
}
internal static class ChatFormat
{
	public const string DefaultTimeFormat = "HH:mm";

	private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

	public static string Compose(ChatEntry entry, LineStyle style, DateTime now)
	{
		string text = entry.Line ?? string.Empty;
		if (style == null)
		{
			return text;
		}
		if (style.Highlight && style.HighlightColor != null && entry.Kind == ChatKind.Player && TextUtil.IsMention(entry.Message, style.MyName, style.Keywords))
		{
			text = "<mark=" + style.HighlightColor + ">" + text + "</mark>";
		}
		if (style.Timestamps && (entry.Kind != ChatKind.Notification || style.TimeOnNotifications))
		{
			text = Stamp(entry.Time, now, style) + text;
		}
		return text;
	}

	public static string Stamp(DateTime time, DateTime now, LineStyle style)
	{
		string text = FormatTime(time, style.TimeFormat);
		if (time.Date != now.Date)
		{
			text = FormatDate(time, style.DateFormat) + " " + text;
		}
		bool num = !string.IsNullOrEmpty(style.TimeColor);
		bool flag = style.TimeSize > 0 && style.TimeSize != 100;
		bool flag2 = text.IndexOf('<') >= 0 || text.IndexOf('>') >= 0;
		StringBuilder stringBuilder = new StringBuilder(text.Length + 48);
		if (num)
		{
			stringBuilder.Append("<color=").Append(style.TimeColor).Append('>');
		}
		if (flag)
		{
			stringBuilder.Append("<size=").Append(style.TimeSize.ToString(Invariant)).Append("%>");
		}
		if (flag2)
		{
			stringBuilder.Append("<noparse>");
		}
		stringBuilder.Append(text);
		if (flag2)
		{
			stringBuilder.Append("</noparse>");
		}
		if (flag)
		{
			stringBuilder.Append("</size>");
		}
		if (num)
		{
			stringBuilder.Append("</color>");
		}
		return stringBuilder.Append(' ').ToString();
	}

	public static string FormatTime(DateTime time, string format)
	{
		if (string.IsNullOrEmpty(format))
		{
			format = "HH:mm";
		}
		try
		{
			return time.ToString(format, Invariant);
		}
		catch (FormatException)
		{
			return time.ToString("HH:mm", Invariant);
		}
	}

	private static string FormatDate(DateTime time, string format)
	{
		try
		{
			return time.ToString(string.IsNullOrEmpty(format) ? "dd.MM" : format, Invariant);
		}
		catch (FormatException)
		{
			return time.ToString("dd.MM", Invariant);
		}
	}

	public static string CopyText(ChatEntry entry)
	{
		return TextUtil.StripTags(entry.Message).Trim();
	}

	public static bool TryParseColor(string value, out string normalized)
	{
		normalized = null;
		if (string.IsNullOrEmpty(value))
		{
			return false;
		}
		string text = value.Trim();
		if (text.StartsWith("#", StringComparison.Ordinal))
		{
			text = text.Substring(1);
		}
		if (text.Length != 3 && text.Length != 4 && text.Length != 6 && text.Length != 8)
		{
			return false;
		}
		string text2 = text;
		for (int i = 0; i < text2.Length; i++)
		{
			if (!Uri.IsHexDigit(text2[i]))
			{
				return false;
			}
		}
		if (text.Length <= 4)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length * 2);
			text2 = text;
			foreach (char value2 in text2)
			{
				stringBuilder.Append(value2).Append(value2);
			}
			text = stringBuilder.ToString();
		}
		normalized = "#" + text.ToUpperInvariant();
		return true;
	}

	public static string WithAlpha(string normalized, byte alpha)
	{
		if (string.IsNullOrEmpty(normalized) || normalized.Length < 7)
		{
			return normalized;
		}
		return normalized.Substring(0, 7) + alpha.ToString("X2", Invariant);
	}
}
internal sealed class ChatHistory
{
	public const string FileName = "chat-history.tsv";

	public const string PreviousFileName = "chat-history.previous.tsv";

	public const string LogFolderName = "logs";

	private const string NewLine = "\r\n";

	private static readonly Encoding Utf8Bom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);

	private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

	private readonly List<ChatEntry> _entries = new List<ChatEntry>();

	private readonly int _capacity;

	private readonly Action<string> _warn;

	private bool _saving;

	private bool _fileBehind;

	private string _lastError;

	public string Folder { get; }

	public string FilePath => Path.Combine(Folder, "chat-history.tsv");

	public string LogFolder => Path.Combine(Folder, "logs");

	public int Count => _entries.Count;

	public bool Saving => _saving;

	public bool DailyLogs { get; set; }

	public ChatHistory(string folder, int capacity, Action<string> warn)
	{
		Folder = folder;
		_capacity = Math.Max(1, capacity);
		_warn = warn;
	}

	public void Open(bool keepPrevious, bool saving)
	{
		_saving = saving;
		if (keepPrevious)
		{
			Load();
		}
		else
		{
			ArchivePrevious();
		}
	}

	public void Add(ChatEntry entry)
	{
		if (entry == null)
		{
			return;
		}
		_entries.Add(entry);
		if (_entries.Count > _capacity)
		{
			_entries.RemoveRange(0, _entries.Count - _capacity);
		}
		if (_saving)
		{
			if (_fileBehind)
			{
				Rewrite();
			}
			else
			{
				Append(entry);
			}
		}
		if (DailyLogs)
		{
			AppendDailyLog(entry);
		}
	}

	public List<ChatEntry> Snapshot()
	{
		return new List<ChatEntry>(_entries);
	}

	public void SetSaving(bool saving)
	{
		if (saving != _saving)
		{
			_saving = saving;
			if (saving)
			{
				Rewrite();
			}
		}
	}

	public void Clear()
	{
		_entries.Clear();
		if (_saving || File.Exists(FilePath))
		{
			Rewrite();
		}
	}

	private void Load()
	{
		string filePath = FilePath;
		if (!File.Exists(filePath))
		{
			return;
		}
		string[] array;
		try
		{
			array = File.ReadAllLines(filePath, Utf8);
		}
		catch (Exception ex)
		{
			Warn("Could not read the chat history: " + ex.Message);
			return;
		}
		int num = 0;
		string[] array2 = array;
		foreach (string text in array2)
		{
			if (text.Length != 0 && !text.StartsWith("time\t", StringComparison.Ordinal))
			{
				num++;
				if (HistoryFormat.TryParse(text, out var entry))
				{
					_entries.Add(entry);
				}
			}
		}
		if (_entries.Count > _capacity)
		{
			_entries.RemoveRange(0, _entries.Count - _capacity);
		}
		if (_saving && num != _entries.Count)
		{
			Rewrite();
		}
	}

	private void ArchivePrevious()
	{
		string filePath = FilePath;
		try
		{
			if (File.Exists(filePath))
			{
				string text = Path.Combine(Folder, "chat-history.previous.tsv");
				if (File.Exists(text))
				{
					File.Delete(text);
				}
				File.Move(filePath, text);
			}
		}
		catch (Exception ex)
		{
			Warn("Could not move the old chat history aside: " + ex.Message);
			Rewrite();
		}
	}

	private void Append(ChatEntry entry)
	{
		try
		{
			string filePath = FilePath;
			if (!File.Exists(filePath))
			{
				Directory.CreateDirectory(Folder);
				File.WriteAllText(filePath, "time\tchannel\ttype\tsteam_id\tname\tmessage\tline\r\n", Utf8Bom);
			}
			File.AppendAllText(filePath, HistoryFormat.ToRecord(entry) + "\r\n", Utf8);
			_lastError = null;
		}
		catch (Exception ex)
		{
			_fileBehind = true;
			Warn("Could not write the chat history, will try again with the next message: " + ex.Message);
		}
	}

	private void Rewrite()
	{
		string filePath = FilePath;
		string text = filePath + ".tmp";
		try
		{
			Directory.CreateDirectory(Folder);
			StringBuilder stringBuilder = new StringBuilder("time\tchannel\ttype\tsteam_id\tname\tmessage\tline".Length + 2 + _entries.Count * 96);
			stringBuilder.Append("time\tchannel\ttype\tsteam_id\tname\tmessage\tline").Append("\r\n");
			foreach (ChatEntry entry in _entries)
			{
				stringBuilder.Append(HistoryFormat.ToRecord(entry)).Append("\r\n");
			}
			File.WriteAllText(text, stringBuilder.ToString(), Utf8Bom);
			if (File.Exists(filePath))
			{
				File.Delete(filePath);
			}
			File.Move(text, filePath);
			_fileBehind = false;
			_lastError = null;
		}
		catch (Exception ex)
		{
			_fileBehind = true;
			Warn("Could not write the chat history, will try again with the next message: " + ex.Message);
			try
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
			catch (Exception)
			{
			}
		}
	}

	private void AppendDailyLog(ChatEntry entry)
	{
		try
		{
			string logFolder = LogFolder;
			string path = Path.Combine(logFolder, entry.Time.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
			if (!File.Exists(path))
			{
				Directory.CreateDirectory(logFolder);
				File.WriteAllText(path, string.Empty, Utf8Bom);
			}
			File.AppendAllText(path, HistoryFormat.ToReadable(entry) + "\r\n", Utf8);
		}
		catch (Exception ex)
		{
			Warn("Could not write the chat log: " + ex.Message);
		}
	}

	private void Warn(string message)
	{
		if (!(message == _lastError))
		{
			_lastError = message;
			_warn?.Invoke(message);
		}
	}
}
internal sealed class ChatLine : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
{
	internal ChatEntry Entry;

	private TMP_Text _text;

	internal static ChatLine Attach(TMP_Text text, ChatEntry entry)
	{
		ChatLine chatLine = ((Component)text).GetComponent<ChatLine>();
		if ((Object)(object)chatLine == (Object)null)
		{
			chatLine = ((Component)text).gameObject.AddComponent<ChatLine>();
		}
		chatLine._text = text;
		chatLine.Entry = entry;
		chatLine.Render(ChatLines.Style, DateTime.Now);
		LineSizer.Apply(text);
		return chatLine;
	}

	internal void Render(LineStyle style, DateTime now)
	{
		if (!((Object)(object)_text == (Object)null) && Entry != null)
		{
			string text = ChatFormat.Compose(Entry, style, now);
			if (!string.Equals(_text.text, text, StringComparison.Ordinal))
			{
				_text.text = text;
			}
		}
	}

	public void OnPointerClick(PointerEventData eventData)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		if (eventData == null || (int)eventData.button != 1 || Entry == null)
		{
			return;
		}
		Plugin instance = Plugin.Instance;
		if (!((Object)(object)instance == (Object)null) && instance.RightClickCopy.Value)
		{
			string text = ChatFormat.CopyText(Entry);
			if (text.Length != 0)
			{
				GUIUtility.systemCopyBuffer = text;
				Toast.Show(Lang.Current.Copied);
			}
		}
	}
}
internal static class ChatLines
{
	internal static bool Suppress;

	internal static int StyleVersion;

	private static LineStyle _style;

	private static int _builtVersion = -1;

	private static string _builtName;

	private static Lang _builtLang;

	internal static LineStyle Style
	{
		get
		{
			string localPlayerName = GameAccess.LocalPlayerName;
			Lang current = Lang.Current;
			if (_style == null || _builtVersion != StyleVersion || localPlayerName != _builtName || current != _builtLang)
			{
				_style = Plugin.Instance.BuildStyle(localPlayerName, current);
				_builtVersion = StyleVersion;
				_builtName = localPlayerName;
				_builtLang = current;
			}
			return _style;
		}
	}

	internal static void ApplyLimits()
	{
		Plugin instance = Plugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			GameAccess.SetLineLimits(instance.GlobalLimit.Value, instance.LocalLimit.Value);
		}
	}

	internal static void Trim(List<GameObject> lines, int limit)
	{
		if (lines == null)
		{
			return;
		}
		limit = Math.Max(1, limit);
		while (lines.Count > limit)
		{
			GameObject val = lines[0];
			lines.RemoveAt(0);
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
		}
	}

	private static int Limit(bool isLocal)
	{
		Plugin instance = Plugin.Instance;
		if (!isLocal)
		{
			return instance.GlobalLimit.Value;
		}
		return instance.LocalLimit.Value;
	}

	internal static void OnMessageAdded(TextChannelManager chat, string userName, string text, bool isLocal, int senderIndex, bool own, LineMark mark)
	{
		List<GameObject> lines = GameAccess.Lines(chat, isLocal);
		TMP_Text val = FindNewLine(lines, mark);
		if (!((Object)(object)val == (Object)null))
		{
			ChatEntry entry = new ChatEntry
			{
				Time = DateTime.Now,
				IsLocal = isLocal,
				Kind = (own ? ChatKind.Own : ChatKind.Player),
				SteamId = (own ? GameAccess.OwnSteamId(chat) : GameAccess.SteamIdAt(senderIndex)),
				Name = (userName ?? string.Empty),
				Message = (text ?? string.Empty),
				Line = (val.text ?? string.Empty)
			};
			ChatLine.Attach(val, entry);
			Plugin.Instance.History.Add(entry);
			Trim(lines, Limit(isLocal));
		}
	}

	internal static void OnNotificationAdded(TextChannelManager chat, string text, LineMark mark)
	{
		List<GameObject> lines = GameAccess.Lines(chat, isLocal: false);
		TMP_Text val = FindNewLine(lines, mark);
		if (!((Object)(object)val == (Object)null))
		{
			ChatEntry entry = new ChatEntry
			{
				Time = DateTime.Now,
				IsLocal = false,
				Kind = ChatKind.Notification,
				Message = (text ?? string.Empty),
				Line = (val.text ?? string.Empty)
			};
			ChatLine.Attach(val, entry);
			if (!Suppress)
			{
				Plugin.Instance.History.Add(entry);
			}
			Trim(lines, Limit(isLocal: false));
		}
	}

	private static TMP_Text FindNewLine(List<GameObject> lines, LineMark mark)
	{
		if (lines == null || lines.Count == 0)
		{
			return null;
		}
		int num = ((mark != null) ? Math.Max(0, lines.Count - 8) : (lines.Count - 1));
		for (int num2 = lines.Count - 1; num2 >= num; num2--)
		{
			GameObject val = lines[num2];
			if (mark != null && val == mark.Newest)
			{
				return null;
			}
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val.GetComponent<ChatLine>() != (Object)null))
			{
				return val.GetComponent<TMP_Text>();
			}
		}
		return null;
	}

	internal static void OnChatStarted(TextChannelManager chat)
	{
		GameAccess.SetChat(chat);
		ApplyLimits();
		TMP_Text val = GameAccess.TextPrefab(chat);
		if ((Object)(object)val != (Object)null && val.fontSize > 1f)
		{
			LineSizer.BaseSize = val.fontSize;
		}
		Plugin instance = Plugin.Instance;
		if (!instance.RestoreHistory.Value)
		{
			return;
		}
		List<ChatEntry> list = instance.History.Snapshot();
		if (list.Count != 0)
		{
			UIManager ui = GameAccess.FindUI();
			int num = Restore(chat, ui, val, list, isLocal: false, instance.RestoreNotifications.Value) + Restore(chat, ui, val, list, isLocal: true, notifications: false);
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)$"Restored {num} chat lines from earlier in this session.");
			}
		}
	}

	private static int Restore(TextChannelManager chat, UIManager ui, TMP_Text prefab, List<ChatEntry> entries, bool isLocal, bool notifications)
	{
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Expected O, but got Unknown
		Transform val = GameAccess.Content(ui, isLocal);
		List<GameObject> list = GameAccess.Lines(chat, isLocal);
		if ((Object)(object)val == (Object)null || list == null || (Object)(object)prefab == (Object)null)
		{
			return 0;
		}
		HashSet<ChatEntry> hashSet = new HashSet<ChatEntry>();
		foreach (GameObject item in list)
		{
			ChatLine chatLine = (((Object)(object)item != (Object)null) ? item.GetComponent<ChatLine>() : null);
			if ((Object)(object)chatLine != (Object)null && chatLine.Entry != null)
			{
				hashSet.Add(chatLine.Entry);
			}
		}
		int num = Limit(isLocal) - 1;
		List<ChatEntry> list2 = new List<ChatEntry>();
		int num2 = entries.Count - 1;
		while (num2 >= 0 && list2.Count < num)
		{
			ChatEntry chatEntry = entries[num2];
			if (chatEntry.IsLocal == isLocal && !hashSet.Contains(chatEntry) && (chatEntry.Kind != ChatKind.Notification || notifications) && (chatEntry.Kind != ChatKind.Player || !GameAccess.IsBlocked(chatEntry.SteamId)))
			{
				list2.Add(chatEntry);
			}
			num2--;
		}
		if (list2.Count == 0)
		{
			return 0;
		}
		list2.Reverse();
		int num3 = 0;
		foreach (ChatEntry item2 in list2)
		{
			TMP_Text val2 = CreateLine(prefab, val, item2.Line);
			Button component = ((Component)val2).GetComponent<Button>();
			if ((Object)(object)component != (Object)null)
			{
				if (item2.Kind == ChatKind.Notification)
				{
					((Selectable)component).interactable = false;
				}
				else if (item2.Kind == ChatKind.Player && !string.IsNullOrEmpty(item2.SteamId))
				{
					string name = item2.Name;
					string steamId = item2.SteamId;
					((UnityEvent)component.onClick).AddListener((UnityAction)delegate
					{
						GameAccess.OpenIdCard(name, steamId);
					});
				}
			}
			ChatLine.Attach(val2, item2);
			val2.transform.SetSiblingIndex(num3);
			list.Insert(num3, ((Component)val2).gameObject);
			num3++;
		}
		TMP_Text val3 = CreateLine(prefab, val, Lang.Current.EarlierMessages);
		Button component2 = ((Component)val3).GetComponent<Button>();
		if ((Object)(object)component2 != (Object)null)
		{
			((Selectable)component2).interactable = false;
		}
		LineSizer.Apply(val3);
		val3.transform.SetSiblingIndex(num3);
		list.Insert(num3, ((Component)val3).gameObject);
		Trim(list, Limit(isLocal));
		return list2.Count;
	}

	private static TMP_Text CreateLine(TMP_Text prefab, Transform parent, string text)
	{
		TMP_Text obj = Object.Instantiate<TMP_Text>(prefab, parent);
		obj.text = text ?? string.Empty;
		return obj;
	}

	internal static void RenderAll()
	{
		UIManager uI = GameAccess.UI;
		if ((Object)(object)uI == (Object)null)
		{
			return;
		}
		LineStyle style = Style;
		DateTime now = DateTime.Now;
		for (int i = 0; i < 2; i++)
		{
			Transform val = GameAccess.Content(uI, i == 1);
			if (!((Object)(object)val == (Object)null))
			{
				ChatLine[] componentsInChildren = ((Component)val).GetComponentsInChildren<ChatLine>(true);
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					componentsInChildren[j].Render(style, now);
				}
			}
		}
	}

	internal static int Clear()
	{
		TextChannelManager chat = GameAccess.Chat;
		int num = 0;
		for (int i = 0; i < 2; i++)
		{
			List<GameObject> list = GameAccess.Lines(chat, i == 1);
			if (list == null)
			{
				continue;
			}
			foreach (GameObject item in list)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
					num++;
				}
			}
			list.Clear();
		}
		return num;
	}
}
internal static class ChatWindow
{
	private sealed class Viewport
	{
		public Mask Mask;

		public Image MaskImage;

		public RectMask2D RectMask;
	}

	private const string FrameName = "Panel_Bg";

	private const string InputBackgroundName = "ImageInputFieldBg";

	private const string MoveHandleName = "Image_Drag";

	private static readonly Vector2 FrameSpriteSize = new Vector2(1644f, 1876f);

	private static readonly Vector4 FrameBorder = new Vector4(108f, 366f, 114f, 120f);

	private const float DefaultLineWidth = 697.1f;

	private const float TopMargin = 25f;

	private const float GameRightLimit = 120f;

	private const float MinWidthFactor = 0.7f;

	private const float MinHeightFactor = 0.5f;

	private const float GripSize = 46f;

	private static readonly Vector2 GripOffset = new Vector2(5f, 6f);

	private const float ReflowDelay = 0.3f;

	private static UIManager _ui;

	private static RectTransform _root;

	private static GameObject _grip;

	private static Vector2 _defaultSize;

	private static Vector2 _defaultCorner;

	private static float _rightMargin;

	private static bool _resizing;

	private static float _reflowAt = -1f;

	private static readonly List<VerticalLayoutGroup> Layouts = new List<VerticalLayoutGroup>();

	private static readonly List<Viewport> Viewports = new List<Viewport>();

	private static Texture2D _gripTexture;

	private static Sprite _gripSprite;

	private static Sprite _slicedFrame;

	private static Sprite _slicedFrameSource;

	internal static bool Ready
	{
		get
		{
			if ((Object)(object)_ui != (Object)null)
			{
				return (Object)(object)_root != (Object)null;
			}
			return false;
		}
	}

	internal static Vector2 Size
	{
		get
		{
			//IL_0018: 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)
			if (!((Object)(object)_root != (Object)null))
			{
				return Vector2.zero;
			}
			return _root.sizeDelta;
		}
	}

	private static Vector2 Corner => _root.anchoredPosition - Vector2.Scale(_root.pivot, _root.sizeDelta);

	private static Vector2 Area
	{
		get
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			Transform parent = ((Transform)_root).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if (!((Object)(object)val != (Object)null))
			{
				return Vector2.zero;
			}
			Rect rect = val.rect;
			return ((Rect)(ref rect)).size;
		}
	}

	internal static void Prepare(UIManager ui)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: 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_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0206: Unknown result type (might be due to invalid IL or missing references)
		//IL_0285: Unknown result type (might be due to invalid IL or missing references)
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_028c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Unknown result type (might be due to invalid IL or missing references)
		GameAccess.SetUI(ui);
		RectTransform val = GameAccess.PanelRoot(ui);
		if ((Object)(object)val == (Object)null || ((Object)(object)ui == (Object)(object)_ui && (Object)(object)val == (Object)(object)_root))
		{
			return;
		}
		_ui = null;
		_root = null;
		_grip = null;
		_resizing = false;
		_reflowAt = -1f;
		Layouts.Clear();
		Viewports.Clear();
		Vector2 sizeDelta = val.sizeDelta;
		RectTransform val2 = GameAccess.CornerBottomLeft(ui);
		if ((Object)(object)val2 != (Object)null)
		{
			Reanchor(val2, Vector2.zero, Vector2.zero);
		}
		GameObject val3 = GameAccess.PanelBackground(ui);
		RectTransform val4 = (RectTransform)(((Object)(object)val3 != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val4 != (Object)null)
		{
			Reanchor(val4, Vector2.zero, Vector2.one);
			Transform obj = ((Transform)val4).Find("Panel_Bg");
			RectTransform val5 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if ((Object)(object)val5 != (Object)null)
			{
				SliceFrame(((Component)val5).GetComponent<Image>());
				Reanchor(val5, Vector2.zero, Vector2.one);
			}
			Transform obj2 = ((Transform)val4).Find("ImageInputFieldBg");
			RectTransform val6 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null);
			if ((Object)(object)val6 != (Object)null)
			{
				Reanchor(val6, Vector2.zero, new Vector2(1f, 0f));
			}
			Transform obj3 = ((Transform)val4).Find("Image_Drag");
			RectTransform val7 = (RectTransform)(object)((obj3 is RectTransform) ? obj3 : null);
			if ((Object)(object)val7 != (Object)null)
			{
				Reanchor(val7, new Vector2(1f, 0f), new Vector2(1f, 0f));
			}
			CreateGrip(ui, val4);
		}
		Transform obj4 = GameAccess.GlobalScroll(ui);
		Transform val8 = GameAccess.LocalScroll(ui);
		RectTransform val9 = (RectTransform)(object)((obj4 is RectTransform) ? obj4 : null);
		if (val9 != null)
		{
			Reanchor(val9, Vector2.zero, Vector2.one);
		}
		RectTransform val10 = (RectTransform)(object)((val8 is RectTransform) ? val8 : null);
		if (val10 != null)
		{
			Reanchor(val10, Vector2.zero, Vector2.one);
		}
		TMP_InputField messageInput = ui.MessageInput;
		if ((Object)(object)messageInput != (Object)null)
		{
			Transform transform = ((Component)messageInput).transform;
			RectTransform val11 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (val11 != null)
			{
				Reanchor(val11, Vector2.zero, new Vector2(1f, 0f));
			}
		}
		TMP_Text val12 = GameAccess.TextPrefab(GameAccess.FindChat());
		float lineWidth = (((Object)(object)val12 != (Object)null && val12.rectTransform.sizeDelta.x > 1f) ? val12.rectTransform.sizeDelta.x : 697.1f);
		PrepareContent(GameAccess.Content(ui, isLocal: false), lineWidth);
		PrepareContent(GameAccess.Content(ui, isLocal: true), lineWidth);
		PrepareViewport(obj4);
		PrepareViewport(val8);
		ApplyCulling();
		_defaultSize = sizeDelta;
		_defaultCorner = val.anchoredPosition - Vector2.Scale(val.pivot, sizeDelta);
		_rightMargin = 120f - (1f - val.pivot.x) * sizeDelta.x;
		_ui = ui;
		_root = val;
		Plugin instance = Plugin.Instance;
		Vector2? corner = null;
		if (instance.RememberPosition.Value && TryParsePosition(instance.Position.Value, out var corner2))
		{
			corner = corner2;
		}
		Apply(corner);
	}

	internal static void Apply()
	{
		Apply(null);
	}

	private static void Apply(Vector2? corner)
	{
		//IL_0022: 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_0061: 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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		if (Ready)
		{
			Plugin instance = Plugin.Instance;
			? corner2 = ((??)corner) ?? Corner;
			Vector2 val = ClampSize(new Vector2(_defaultSize.x * (float)instance.Width.Value / 100f, _defaultSize.y * (float)instance.Height.Value / 100f));
			if (Mathf.Abs(val.x - _root.sizeDelta.x) > 0.01f)
			{
				HoldReflow();
			}
			_root.sizeDelta = val;
			SetCorner((Vector2)corner2);
			if ((Object)(object)_grip != (Object)null && _grip.activeSelf != instance.ResizeHandle.Value)
			{
				_grip.SetActive(instance.ResizeHandle.Value);
			}
		}
	}

	internal static void ApplyPosition()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: 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_0049: Unknown result type (might be due to invalid IL or missing references)
		Plugin instance = Plugin.Instance;
		if (Ready && instance.RememberPosition.Value && TryParsePosition(instance.Position.Value, out var corner))
		{
			Vector2 val = corner - Corner;
			if (((Vector2)(ref val)).sqrMagnitude > 0.25f)
			{
				SetCorner(corner);
			}
		}
	}

	internal static void Tick()
	{
		if (_reflowAt < 0f || _resizing || Time.unscaledTime < _reflowAt)
		{
			return;
		}
		_reflowAt = -1f;
		foreach (VerticalLayoutGroup layout in Layouts)
		{
			if ((Object)(object)layout != (Object)null)
			{
				((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = true;
			}
		}
	}

	private static void HoldReflow()
	{
		foreach (VerticalLayoutGroup layout in Layouts)
		{
			if ((Object)(object)layout != (Object)null && ((HorizontalOrVerticalLayoutGroup)layout).childControlWidth)
			{
				((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = false;
			}
		}
		_reflowAt = Time.unscaledTime + 0.3f;
	}

	internal static void OnBeginMove(UIManager ui)
	{
		if (Ready && (Object)(object)ui == (Object)(object)_ui)
		{
			UpdateDragLimits(includeRight: true);
		}
	}

	internal static void OnEndMove(UIManager ui)
	{
		if (Ready && (Object)(object)ui == (Object)(object)_ui)
		{
			SavePosition();
		}
	}

	internal static void BeginResize()
	{
		if (Ready)
		{
			_resizing = true;
			GameAccess.SetPanelHeld(_ui, held: true);
		}
	}

	internal static void ResizeTo(Vector2 size)
	{
		//IL_0008: 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_001b: 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)
		if (Ready)
		{
			size = ClampSize(size);
			Plugin instance = Plugin.Instance;
			instance.Width.Value = Mathf.RoundToInt(size.x / _defaultSize.x * 100f);
			instance.Height.Value = Mathf.RoundToInt(size.y / _defaultSize.y * 100f);
		}
	}

	internal static void EndResize()
	{
		if (_resizing)
		{
			_resizing = false;
			if (_reflowAt >= 0f)
			{
				_reflowAt = Time.unscaledTime;
			}
			if (Ready)
			{
				GameAccess.SetPanelHeld(_ui, held: false);
				SavePosition();
			}
		}
	}

	internal static void ResetSize()
	{
		Plugin instance = Plugin.Instance;
		instance.Width.Value = 100;
		instance.Height.Value = 100;
	}

	internal static void ResetPosition()
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		Plugin.Instance.Position.Value = string.Empty;
		if (Ready)
		{
			SetCorner(_defaultCorner);
		}
	}

	internal static void ApplyCulling()
	{
		bool value = Plugin.Instance.CullHiddenLines.Value;
		foreach (Viewport viewport in Viewports)
		{
			if ((Object)(object)viewport.Mask == (Object)null || (Object)(object)viewport.RectMask == (Object)null)
			{
				continue;
			}
			if (value)
			{
				((Behaviour)viewport.Mask).enabled = false;
				if ((Object)(object)viewport.MaskImage != (Object)null && !viewport.Mask.showMaskGraphic)
				{
					((Behaviour)viewport.MaskImage).enabled = false;
				}
				((Behaviour)viewport.RectMask).enabled = true;
			}
			else
			{
				((Behaviour)viewport.RectMask).enabled = false;
				if ((Object)(object)viewport.MaskImage != (Object)null)
				{
					((Behaviour)viewport.MaskImage).enabled = true;
				}
				((Behaviour)viewport.Mask).enabled = true;
			}
		}
	}

	internal static bool TryPointer(Vector2 screenPosition, Camera camera, out Vector2 point)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		point = Vector2.zero;
		if (Ready)
		{
			Transform parent = ((Transform)_root).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if (val != null)
			{
				if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(val, screenPosition, camera, ref point))
				{
					return false;
				}
				Vector3 localScale = ((Transform)_root).localScale;
				if (Mathf.Abs(localScale.x) > 0.0001f && Mathf.Abs(localScale.y) > 0.0001f)
				{
					point = new Vector2(point.x / localScale.x, point.y / localScale.y);
				}
				return true;
			}
		}
		return false;
	}

	private static void SetCorner(Vector2 corner)
	{
		//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_0010: 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_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		Vector2 sizeDelta = _root.sizeDelta;
		_root.anchoredPosition = ClampCorner(corner, sizeDelta) + Vector2.Scale(_root.pivot, sizeDelta);
		UpdateDragLimits(includeRight: false);
	}

	private static Vector2 ClampCorner(Vector2 corner, Vector2 size)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		Vector2 area = Area;
		if (area.x < 100f || area.y < 100f)
		{
			return corner;
		}
		float num = area.x - size.x - _rightMargin;
		float num2 = area.y - 25f - size.y - _root.pivot.y * size.y;
		corner.x = Mathf.Max(_defaultCorner.x, Mathf.Min(corner.x, num));
		corner.y = Mathf.Max(_defaultCorner.y, Mathf.Min(corner.y, num2));
		return corner;
	}

	private static Vector2 ClampSize(Vector2 size)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		float num = _defaultSize.x * 0.7f;
		float num2 = _defaultSize.y * 0.5f;
		Vector2 area = Area;
		float num3 = ((area.x > 100f) ? (area.x - _defaultCorner.x - _rightMargin) : float.MaxValue);
		float num4 = ((area.y > 100f) ? ((area.y - 25f - _defaultCorner.y) / (1f + _root.pivot.y)) : float.MaxValue);
		size.x = Mathf.Clamp(size.x, num, Mathf.Max(num, num3));
		size.y = Mathf.Clamp(size.y, num2, Mathf.Max(num2, num4));
		return size;
	}

	private static void UpdateDragLimits(bool includeRight)
	{
		//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_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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)
		//IL_003f: 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_0055: 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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		Vector2 sizeDelta = _root.sizeDelta;
		Vector2 pivot = _root.pivot;
		float num = _defaultCorner.x + pivot.x * sizeDelta.x;
		float minY = _defaultCorner.y + pivot.y * sizeDelta.y;
		float? maxX = null;
		Vector2 area = Area;
		if (includeRight && area.x > 100f)
		{
			maxX = Mathf.Max(num, area.x - (1f - pivot.x) * sizeDelta.x - _rightMargin);
		}
		GameAccess.SetDragLimits(_ui, num, minY, maxX);
	}

	private static void SavePosition()
	{
		//IL_0014: 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)
		Plugin instance = Plugin.Instance;
		if (instance.RememberPosition.Value)
		{
			Vector2 corner = Corner;
			instance.Position.Value = corner.x.ToString("0.#", CultureInfo.InvariantCulture) + ";" + corner.y.ToString("0.#", CultureInfo.InvariantCulture);
		}
	}

	internal static bool TryParsePosition(string value, out Vector2 corner)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		corner = Vector2.zero;
		if (string.IsNullOrEmpty(value))
		{
			return false;
		}
		string[] array = value.Split(';');
		if (array.Length != 2 || !float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
		{
			return false;
		}
		corner = new Vector2(result, result2);
		return true;
	}

	private static void Reanchor(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax)
	{
		//IL_0011: 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_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: 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)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: 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)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		Transform parent = ((Transform)rect).parent;
		RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
		if (val != null)
		{
			Rect rect2 = val.rect;
			Vector2 size = ((Rect)(ref rect2)).size;
			Vector2 val2 = Vector2.Scale(rect.anchorMin, size) + rect.offsetMin;
			Vector2 val3 = Vector2.Scale(rect.anchorMax, size) + rect.offsetMax;
			rect.anchorMin = anchorMin;
			rect.anchorMax = anchorMax;
			rect.offsetMin = val2 - Vector2.Scale(anchorMin, size);
			rect.offsetMax = val3 - Vector2.Scale(anchorMax, size);
		}
	}

	private static void PrepareContent(Transform content, float lineWidth)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		if ((Object)(object)content == (Object)null)
		{
			return;
		}
		VerticalLayoutGroup component = ((Component)content).GetComponent<VerticalLayoutGroup>();
		if ((Object)(object)component != (Object)null && !((HorizontalOrVerticalLayoutGroup)component).childControlWidth)
		{
			RectTransform val = (RectTransform)(object)((content is RectTransform) ? content : null);
			if (val != null)
			{
				Rect rect = val.rect;
				if (((Rect)(ref rect)).width > lineWidth * 0.5f)
				{
					RectOffset padding = ((LayoutGroup)component).padding;
					rect = val.rect;
					int num = Mathf.RoundToInt(((Rect)(ref rect)).width - (float)padding.left - lineWidth);
					((LayoutGroup)component).padding = new RectOffset(padding.left, num, padding.top, padding.bottom);
					((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true;
					((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true;
					Layouts.Add(component);
				}
			}
		}
		if ((Object)(object)((Component)content).GetComponent<LineSizer>() == (Object)null)
		{
			((Component)content).gameObject.AddComponent<LineSizer>();
		}
	}

	private static void PrepareViewport(Transform scroll)
	{
		ScrollRect val = (((Object)(object)scroll != (Object)null) ? ((Component)scroll).GetComponent<ScrollRect>() : null);
		RectTransform val2 = (((Object)(object)val != (Object)null) ? val.viewport : null);
		Mask val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Mask>() : null);
		if (!((Object)(object)val3 == (Object)null))
		{
			RectMask2D val4 = ((Component)val2).GetComponent<RectMask2D>();
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = ((Component)val2).gameObject.AddComponent<RectMask2D>();
				((Behaviour)val4).enabled = false;
			}
			Viewports.Add(new Viewport
			{
				Mask = val3,
				MaskImage = ((Component)val2).GetComponent<Image>(),
				RectMask = val4
			});
		}
	}

	private static void SliceFrame(Image image)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)image == (Object)null || (int)image.type != 0)
		{
			return;
		}
		Sprite sprite = image.sprite;
		if ((Object)(object)sprite == (Object)null || sprite.packed || sprite.border != Vector4.zero)
		{
			return;
		}
		Rect rect = sprite.rect;
		if (Mathf.Abs(((Rect)(ref rect)).width - FrameSpriteSize.x) > 0.5f || Mathf.Abs(((Rect)(ref rect)).height - FrameSpriteSize.y) > 0.5f)
		{
			return;
		}
		Rect rect2 = ((Graphic)image).rectTransform.rect;
		float width = ((Rect)(ref rect2)).width;
		float pixelsPerUnit = image.pixelsPerUnit;
		if (!(width <= 1f) && !(pixelsPerUnit <= 0f))
		{
			if ((Object)(object)_slicedFrame == (Object)null || (Object)(object)_slicedFrameSource != (Object)(object)sprite)
			{
				_slicedFrame = Sprite.Create(sprite.texture, rect, new Vector2(sprite.pivot.x / ((Rect)(ref rect)).width, sprite.pivot.y / ((Rect)(ref rect)).height), sprite.pixelsPerUnit, 0u, (SpriteMeshType)0, FrameBorder);
				((Object)_slicedFrame).name = ((Object)sprite).name + " (ChatPlus 9-slice)";
				_slicedFrameSource = sprite;
			}
			image.sprite = _slicedFrame;
			image.type = (Type)1;
			image.fillCenter = true;
			image.pixelsPerUnitMultiplier = ((Rect)(ref rect)).width / (width * pixelsPerUnit);
		}
	}

	private static void CreateGrip(UIManager ui, RectTransform background)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: 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_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Expected O, but got Unknown
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: 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_00db: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("ChatPlus.ResizeHandle", new Type[1] { typeof(RectTransform) });
		RectTransform val2 = (RectTransform)val.transform;
		((Transform)val2).SetParent((Transform)(object)background, false);
		val2.anchorMin = Vector2.one;
		val2.anchorMax = Vector2.one;
		val2.pivot = new Vector2(0.5f, 0.5f);
		val2.sizeDelta = new Vector2(46f, 46f);
		val2.anchoredPosition = GripOffset;
		((Transform)val2).SetAsLastSibling();
		Image val3 = val.AddComponent<Image>();
		val3.sprite = GripSprite();
		((Graphic)val3).raycastTarget = true;
		List<Image> list = GameAccess.PanelImages(ui);
		if (list != null)
		{
			if (list.Count > 0 && (Object)(object)list[0] != (Object)null)
			{
				Color color = ((Graphic)val3).color;
				color.a = ((Graphic)list[0]).color.a;
				((Graphic)val3).color = color;
			}
			list.Add(val3);
		}
		val.AddComponent<ResizeGrip>();
		val.SetActive(Plugin.Instance.ResizeHandle.Value);
		_grip = val;
	}

	private static Sprite GripSprite()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_gripSprite != (Object)null)
		{
			return _gripSprite;
		}
		_gripTexture = GripArt.Draw();
		_gripSprite = Sprite.Create(_gripTexture, new Rect(0f, 0f, (float)((Texture)_gripTexture).width, (float)((Texture)_gripTexture).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0);
		((Object)_gripSprite).name = "ChatPlus.ResizeHandle";
		return _gripSprite;
	}

	internal static void Unload()
	{
		if ((Object)(object)_grip != (Object)null)
		{
			Object.Destroy((Object)(object)_grip);
		}
		_grip = null;
		if ((Object)(object)_gripSprite != (Object)null)
		{
			Object.Destroy((Object)(object)_gripSprite);
		}
		if ((Object)(object)_gripTexture != (Object)null)
		{
			Object.Destroy((Object)(object)_gripTexture);
		}
		_gripSprite = null;
		_gripTexture = null;
	}
}
internal static class GameAccess
{
	private const float LookupInterval = 1f;

	private static TextChannelManager _chat;

	private static UIManager _ui;

	private static float _nextChatLookup;

	private static float _nextUiLookup;

	private static readonly List<string> MissingFields = new List<string>();

	private static readonly FieldRef<TextChannelManager, TMP_Text> TextPrefabRef = Field<TextChannelManager, TMP_Text>("_textPrefab");

	private static readonly FieldRef<TextChannelManager, List<GameObject>> GlobalLinesRef = Field<TextChannelManager, List<GameObject>>("_messageObjectsGlobal");

	private static readonly FieldRef<TextChannelManager, List<GameObject>> LocalLinesRef = Field<TextChannelManager, List<GameObject>>("_messageObjectsLocal");

	private static readonly FieldRef<TextChannelManager, string> OwnIdRef = Field<TextChannelManager, string>("_playerId");

	private static readonly FieldRef<UIManager, RectTransform> PanelRootRef = Field<UIManager, RectTransform>("_messagePanelParent");

	private static readonly FieldRef<UIManager, GameObject> PanelBackgroundRef = Field<UIManager, GameObject>("_messagePanel");

	private static readonly FieldRef<UIManager, RectTransform> CornerBottomLeftRef = Field<UIManager, RectTransform>("_messageCornerTransform1");

	private static readonly FieldRef<UIManager, Transform> GlobalScrollRef = Field<UIManager, Transform>("_globalMessagePanel");

	private static readonly FieldRef<UIManager, Transform> LocalScrollRef = Field<UIManager, Transform>("_localMessagePanel");

	private static readonly FieldRef<UIManager, List<Image>> PanelImagesRef = Field<UIManager, List<Image>>("_messagePanelImages");

	private static readonly FieldRef<UIManager, float> LimitMinXRef = Field<UIManager, float>("_messageLimitMinX");

	private static readonly FieldRef<UIManager, float> LimitMinYRef = Field<UIManager, float>("_messageLimitMinY");

	private static readonly FieldRef<UIManager, float> LimitMaxXRef = Field<UIManager, float>("_messageLimitMaxX");

	private static readonly FieldRef<UIManager, bool> MessageDragRef = Field<UIManager, bool>("_isMessageDrag");

	private static readonly FieldRef<UIManager, TextMeshProUGUI> FontTextRef = Field<UIManager, TextMeshProUGUI>("_messageTextForFont");

	private static readonly FieldRef<GameSettings, int> GlobalLimitRef = Field<GameSettings, int>("_globalMessageLimitCount");

	private static readonly FieldRef<GameSettings, int> LocalLimitRef = Field<GameSettings, int>("_localMessageLimitCount");

	public static TextChannelManager Chat => Lookup<TextChannelManager>(ref _chat, ref _nextChatLookup, (Func<TextChannelManager>)(() => NetworkSingleton<TextChannelManager>.I));

	public static UIManager UI => Lookup<UIManager>(ref _ui, ref _nextUiLookup, (Func<UIManager>)(() => MonoSingleton<UIManager>.I));

	public static string LocalPlayerName
	{
		get
		{
			TextChannelManager chat = Chat;
			if (!((Object)(object)chat != (Object)null))
			{
				return string.Empty;
			}
			return chat.UserName ?? string.Empty;
		}
	}

	public static bool HasDragLimits
	{
		get
		{
			if (LimitMinXRef != null && LimitMinYRef != null)
			{
				return LimitMaxXRef != null;
			}
			return false;
		}
	}

	public static TMP_InputField MessageInput
	{
		get
		{
			UIManager uI = UI;
			if (!((Object)(object)uI != (Object)null))
			{
				return null;
			}
			return uI.MessageInput;
		}
	}

	private static T Lookup<T>(ref T cached, ref float nextLookup, Func<T> find) where T : Object
	{
		if ((Object)(object)cached == (Object)null && Time.unscaledTime >= nextLookup)
		{
			nextLookup = Time.unscaledTime + 1f;
			try
			{
				cached = find();
			}
			catch (Exception)
			{
				cached = default(T);
			}
		}
		return cached;
	}

	public static UIManager FindUI()
	{
		try
		{
			UIManager i = MonoSingleton<UIManager>.I;
			if ((Object)(object)i != (Object)null)
			{
				_ui = i;
			}
			return i;
		}
		catch (Exception)
		{
			return null;
		}
	}

	public static TextChannelManager FindChat()
	{
		try
		{
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			if ((Object)(object)i != (Object)null)
			{
				_chat = i;
			}
			return i;
		}
		catch (Exception)
		{
			return null;
		}
	}

	public static void SetChat(TextChannelManager chat)
	{
		if ((Object)(object)chat != (Object)null)
		{
			_chat = chat;
		}
	}

	public static void SetUI(UIManager ui)
	{
		if ((Object)(object)ui != (Object)null)
		{
			_ui = ui;
		}
	}

	public static void LogMissingFields()
	{
		if (MissingFields.Count > 0)
		{
			Plugin.Log.LogWarning((object)("Game fields not found (the game was probably updated), some features are off: " + string.Join(", ", MissingFields)));
		}
	}

	public static TMP_Text TextPrefab(TextChannelManager chat)
	{
		if (TextPrefabRef == null || !((Object)(object)chat != (Object)null))
		{
			return null;
		}
		return TextPrefabRef.Invoke(chat);
	}

	public static List<GameObject> Lines(TextChannelManager chat, bool isLocal)
	{
		FieldRef<TextChannelManager, List<GameObject>> val = (isLocal ? LocalLinesRef : GlobalLinesRef);
		if (val == null || !((Object)(object)chat != (Object)null))
		{
			return null;
		}
		return val.Invoke(chat);
	}

	public static string OwnSteamId(TextChannelManager chat)
	{
		if (OwnIdRef == null || !((Object)(object)chat != (Object)null))
		{
			return string.Empty;
		}
		return OwnIdRef.Invoke(chat) ?? string.Empty;
	}

	public static RectTransform PanelRoot(UIManager ui)
	{
		return Get<RectTransform>(PanelRootRef, ui);
	}

	public static GameObject PanelBackground(UIManager ui)
	{
		return Get<GameObject>(PanelBackgroundRef, ui);
	}

	public static RectTransform CornerBottomLeft(UIManager ui)
	{
		return Get<RectTransform>(CornerBottomLeftRef, ui);
	}

	public static Transform GlobalScroll(UIManager ui)
	{
		return Get<Transform>(GlobalScrollRef, ui);
	}

	public static Transform LocalScroll(UIManager ui)
	{
		return Get<Transform>(LocalScrollRef, ui);
	}

	public static List<Image> PanelImages(UIManager ui)
	{
		return Get<List<Image>>(PanelImagesRef, ui);
	}

	public static void SetDragLimits(UIManager ui, float minX, float minY, float? maxX)
	{
		if (!((Object)(object)ui == (Object)null) && HasDragLimits)
		{
			LimitMinXRef.Invoke(ui) = minX;
			LimitMinYRef.Invoke(ui) = minY;
			if (maxX.HasValue)
			{
				LimitMaxXRef.Invoke(ui) = maxX.Value;
			}
		}
	}

	public static void SetPanelHeld(UIManager ui, bool held)
	{
		if ((Object)(object)ui != (Object)null && MessageDragRef != null)
		{
			MessageDragRef.Invoke(ui) = held;
		}
	}

	public static Transform Content(UIManager ui, bool isLocal)
	{
		if ((Object)(object)ui == (Object)null)
		{
			return null;
		}
		try
		{
			return isLocal ? ui.TextContentLocalTransform : ui.TextContentGlobalTransform;
		}
		catch (Exception)
		{
			return null;
		}
	}

	public static bool TryGetFontOutline(out bool outline)
	{
		outline = false;
		UIManager uI = UI;
		TextMeshProUGUI val = ((FontTextRef != null && (Object)(object)uI != (Object)null) ? FontTextRef.Invoke(uI) : null);
		Material val2 = (((Object)(object)val != (Object)null) ? ((TMP_Text)val).fontSharedMaterial : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return false;
		}
		outline = val2.IsKeywordEnabled("UNDERLAY_ON");
		return true;
	}

	public static void SetLineLimits(int global, int local)
	{
		if (GlobalLimitRef == null || LocalLimitRef == null)
		{
			return;
		}
		GameSettings i;
		try
		{
			i = ScriptableSingleton<GameSettings>.I;
		}
		catch (Exception)
		{
			return;
		}
		if (!((Object)(object)i == (Object)null))
		{
			if (GlobalLimitRef.Invoke(i) != global)
			{
				GlobalLimitRef.Invoke(i) = global;
			}
			if (LocalLimitRef.Invoke(i) != local)
			{
				LocalLimitRef.Invoke(i) = local;
			}
		}
	}

	public static string SteamIdAt(int index)
	{
		try
		{
			PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
			List<string> list = (((Object)(object)i != (Object)null) ? i.PlayerSteamIDs : null);
			return (list != null && index >= 0 && index < list.Count) ? (list[index] ?? string.Empty) : string.Empty;
		}
		catch (Exception)
		{
			return string.Empty;
		}
	}

	public static PlayerController PlayerBySteamId(string steamId)
	{
		if (string.IsNullOrEmpty(steamId))
		{
			return null;
		}
		PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
		if ((Object)(object)i == (Object)null || i.PlayerSteamIDs == null || i.PlayerControllers == null)
		{
			return null;
		}
		int num = i.PlayerSteamIDs.IndexOf(steamId);
		if (num < 0 || num >= i.PlayerControllers.Count)
		{
			return null;
		}
		return i.PlayerControllers[num];
	}

	public static bool IsBlocked(string steamId)
	{
		if (string.IsNullOrEmpty(steamId))
		{
			return false;
		}
		try
		{
			DataManager i = MonoSingleton<DataManager>.I;
			BanData val = (((Object)(object)i != (Object)null) ? i.BanData : null);
			if (val == null)
			{
				return false;
			}
			return (val.IgnorePlayers != null && val.IgnorePlayers.Contains(steamId)) || (val.MutedPlayers != null && val.MutedPlayers.Contains(steamId));
		}
		catch (Exception)
		{
			return false;
		}
	}

	public static void OpenIdCard(string name, string steamId)
	{
		try
		{
			TemporaryPhoto i = MonoSingleton<TemporaryPhoto>.I;
			if (!((Object)(object)i == (Object)null))
			{
				PlayerController val = PlayerBySteamId(steamId);
				if ((Object)(object)val != (Object)null)
				{
					i.CapturePhoto(val);
				}
				else
				{
					i.ButtonOpenIdCheck(name, steamId);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not open the ID card: " + ex.Message));
		}
	}

	public static bool IsAnyTextFieldFocused()
	{
		EventSystem current = EventSystem.current;
		if ((Object)(object)current == (Object)null)
		{
			return false;
		}
		GameObject currentSelectedGameObject = current.currentSelectedGameObject;
		if ((Object)(object)currentSelectedGameObject == (Object)null)
		{
			return false;
		}
		TMP_InputField component = currentSelectedGameObject.GetComponent<TMP_InputField>();
		if ((Object)(object)component != (Object)null && component.isFocused)
		{
			return true;
		}
		InputField component2 = currentSelectedGameObject.GetComponent<InputField>();
		if ((Object)(object)component2 != (Object)null)
		{
			return component2.isFocused;
		}
		return false;
	}

	public static void Notify(string message)
	{
		TextChannelManager chat = Chat;
		if ((Object)(object)chat == (Object)null)
		{
			return;
		}
		string text = "<color=#9FD1FF>[ChatPlus]</color> <noparse>" + message + "</noparse>";
		try
		{
			if (chat.Islocal && TryAddLocalLine(chat, text))
			{
				return;
			}
			ChatLines.Suppress = true;
			try
			{
				chat.AddNotification(text);
			}
			finally
			{
				ChatLines.Suppress = false;
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not show a chat notification: " + ex.Message));
		}
	}

	private static bool TryAddLocalLine(TextChannelManager chat, string text)
	{
		Transform val = Content(UI, isLocal: true);
		TMP_Text val2 = TextPrefab(chat);
		List<GameObject> list = Lines(chat, isLocal: true);
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || list == null)
		{
			return false;
		}
		TMP_Text val3 = Object.Instantiate<TMP_Text>(val2, val);
		Button component = ((Component)val3).GetComponent<Button>();
		if ((Object)(object)component != (Object)null)
		{
			((Selectable)component).interactable = false;
		}
		val3.text = text;
		ChatLine.Attach(val3, new ChatEntry
		{
			Time = DateTime.Now,
			IsLocal = true,
			Kind = ChatKind.Notification,
			Message = text,
			Line = text
		});
		list.Add(((Component)val3).gameObject);
		ChatLines.Trim(list, Plugin.Instance.LocalLimit.Value);
		return true;
	}

	private static T Get<T>(FieldRef<UIManager, T> field, UIManager ui) where T : class
	{
		if (field == null || !((Object)(object)ui != (Object)null))
		{
			return null;
		}
		return field.Invoke(ui);
	}

	private static FieldRef<TOwner, TField> Field<TOwner, TField>(string name)
	{
		try
		{
			return AccessTools.FieldRefAccess<TOwner, TField>(name);
		}
		catch (Exception)
		{
			MissingFields.Add(typeof(TOwner).Name + "." + name);
			return null;
		}
	}
}
internal static class HistoryFormat
{
	public const string Header = "time\tchannel\ttype\tsteam_id\tname\tmessage\tline";

	private const string RecordTime = "yyyy-MM-dd'T'HH:mm:ss";

	private const string ReadableTime = "yyyy-MM-dd HH:mm:ss";

	private const int Columns = 7;

	private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

	public static string ToRecord(ChatEntry entry)
	{
		StringBuilder stringBuilder = new StringBuilder(64 + 2 * ((entry.Line ?? string.Empty).Length + (entry.Message ?? string.Empty).Length));
		stringBuilder.Append(entry.Time.ToString("yyyy-MM-dd'T'HH:mm:ss", Invariant)).Append('\t').Append(entry.IsLocal ? 'L' : 'G')
			.Append('\t')
			.Append(KindCode(entry.Kind))
			.Append('\t');
		AppendEscaped(stringBuilder, entry.SteamId).Append('\t');
		AppendEscaped(stringBuilder, entry.Name).Append('\t');
		AppendEscaped(stringBuilder, entry.Message).Append('\t');
		AppendEscaped(stringBuilder, entry.Line);
		return stringBuilder.ToString();
	}

	public static bool TryParse(string record, out ChatEntry entry)
	{
		entry = null;
		if (string.IsNullOrEmpty(record))
		{
			return false;
		}
		string[] array = record.Split('\t');
		if (array.Length < 7)
		{
			return false;
		}
		if (!DateTime.TryParseExact(array[0], "yyyy-MM-dd'T'HH:mm:ss", Invariant, DateTimeStyles.None, out var result))
		{
			return false;
		}
		bool isLocal;
		if (array[1] == "L")
		{
			isLocal = true;
		}
		else
		{
			if (!(array[1] == "G"))
			{
				return false;
			}
			isLocal = false;
		}
		if (!TryParseKind(array[2], out var kind))
		{
			return false;
		}
		entry = new ChatEntry
		{
			Time = result,
			IsLocal = isLocal,
			Kind = kind,
			SteamId = Unescape(array[3]),
			Name = Unescape(array[4]),
			Message = Unescape(array[5]),
			Line = Unescape(array[6])
		};
		return true;
	}

	public static string ToReadable(ChatEntry entry)
	{
		StringBuilder stringBuilder = new StringBuilder(64 + (entry.Message ?? string.Empty).Length);
		stringBuilder.Append('[').Append(entry.Time.ToString("yyyy-MM-dd HH:mm:ss", Invariant)).Append("] [")
			.Append((entry.Kind == ChatKind.Notification) ? "Info" : (entry.IsLocal ? "Local" : "Global"))
			.Append("] ");
		if (entry.Kind != ChatKind.Notification)
		{
			stringBuilder.Append(TextUtil.Plain(entry.Name).Trim()).Append(": ");
		}
		stringBuilder.Append(TextUtil.Plain(entry.Message));
		return stringBuilder.ToString();
	}

	public static string Escape(string value)
	{
		if (!string.IsNullOrEmpty(value))
		{
			return AppendEscaped(new StringBuilder(value.Length + 8), value).ToString();
		}
		return string.Empty;
	}

	private static StringBuilder AppendEscaped(StringBuilder target, string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return target;
		}
		foreach (char c in value)
		{
			switch (c)
			{
			case '\\':
				target.Append("\\\\");
				break;
			case '\t':
				target.Append("\\t");
				break;
			case '\n':
				target.Append("\\n");
				break;
			case '\r':
				target.Append("\\r");
				break;
			default:
				target.Append(c);
				break;
			}
		}
		return target;
	}

	public static string Unescape(string value)
	{
		if (string.IsNullOrEmpty(value) || value.IndexOf('\\') < 0)
		{
			return value ?? string.Empty;
		}
		StringBuilder stringBuilder = new StringBuilder(value.Length);
		for (int i = 0; i < value.Length; i++)
		{
			char c = value[i];
			if (c != '\\' || i == value.Length - 1)
			{
				stringBuilder.Append(c);
				continue;
			}
			char c2 = value[++i];
			switch (c2)
			{
			case '\\':
				stringBuilder.Append('\\');
				break;
			case 't':
				stringBuilder.Append('\t');
				break;
			case 'n':
				stringBuilder.Append('\n');
				break;
			case 'r':
				stringBuilder.Append('\r');
				break;
			default:
				stringBuilder.Append('\\').Append(c2);
				break;
			}
		}
		return stringBuilder.ToString();
	}

	private static char KindCode(ChatKind kind)
	{
		return kind switch
		{
			ChatKind.Own => 'O', 
			ChatKind.Notification => 'N', 
			_ => 'P', 
		};
	}

	private static bool TryParseKind(string code, out ChatKind kind)
	{
		switch (code)
		{
		case "P":
			kind = ChatKind.Player;
			return true;
		case "O":
			kind = ChatKind.Own;
			return true;
		case "N":
			kind = ChatKind.Notification;
			return true;
		default:
			kind = ChatKind.Player;
			return false;
		}
	}
}
public enum UiLanguage
{
	Auto,
	English,
	Russian
}
internal sealed class Lang
{
	public string Title;

	public string SectionTime;

	public string SectionHistory;

	public string SectionWindow;

	public string SectionExtras;

	public string ShowTime;

	public string TimeFormat;

	public string TimeColor;

	public string TimeSize;

	public string TimeOnNotifications;

	public string GlobalLimit;

	public string LocalLimit;

	public string LimitsHint;

	public string RestoreHistory;

	public string RestoreNotifications;

	public string SaveHistory;

	public string KeepAfterRestart;

	public string DailyLogs;

	public string HistoryCount;

	public string OpenFolder;

	public string ClearChat;

	public string ClearHistory;

	public string ConfirmClear;

	public string Width;

	public string Height;

	public string TextSize;

	public string ResizeHandle;

	public string ResizeHint;

	public string RememberPosition;

	public string CullHiddenLines;

	public string ResetWindow;

	public string HighlightMentions;

	public string HighlightColor;

	public string Keywords;

	public string KeywordsHint;

	public string RecallSent;

	public string RightClickCopy;

	public string Close;

	public string WindowHint;

	public string None;

	public string Custom;

	public string[] ColorNames;

	public string EarlierMessages;

	public string Copied;

	public string DateFormat;

	public string On;

	public string Off;

	public string TimeOn;

	public string TimeOff;

	public string TimeFormatSet;

	public string SizeSet;

	public string TextSizeSet;

	public string LimitsSet;

	public string ChatCleared;

	public string HistoryCleared;

	public string WindowReset;

	public string Status;

	public string KeywordAdded;

	public string KeywordRemoved;

	public string KeywordMissing;

	public string KeywordsList;

	public string KeywordsCleared;

	public string UsageTime;

	public string UsageSize;

	public string UsageText;

	public string UsageLimit;

	public string UsageKeyword;

	public string Help;

	public static readonly Lang English = new Lang
	{
		Title = "Chat Plus",
		SectionTime = "Message time",
		SectionHistory = "History",
		SectionWindow = "Chat window",
		SectionExtras = "Extras",
		ShowTime = "Show when each message was sent",
		TimeFormat = "Format",
		TimeColor = "Color",
		TimeSize = "Size",
		TimeOnNotifications = "Also for game notifications",
		GlobalLimit = "Lines in Global",
		LocalLimit = "Lines in Local",
		LimitsHint = "The game itself keeps 50 and 25 lines.",
		RestoreHistory = "Bring earlier messages back after a lobby change",
		RestoreNotifications = "Including game notifications",
		SaveHistory = "Save the history to a file",
		KeepAfterRestart = "Keep the history after a game restart",
		DailyLogs = "Also write readable logs, one file per day",
		HistoryCount = "This session's history: {0} lines",
		OpenFolder = "Open folder",
		ClearChat = "Clear chat",
		ClearHistory = "Clear history",
		ConfirmClear = "Click again to clear",
		Width = "Width",
		Height = "Height",
		TextSize = "Text size",
		ResizeHandle = "Resize handle on the chat",
		ResizeHint = "Drag the handle at the chat's top-right corner; double-click it for the normal size.",
		RememberPosition = "Remember where the chat was moved",
		CullHiddenLines = "Don't draw lines scrolled out of view (faster)",
		ResetWindow = "Reset size and position",
		HighlightMentions = "Highlight messages that mention you",
		HighlightColor = "Highlight",
		Keywords = "Keywords",
		KeywordsHint = "Your name always counts. More words: /chatplus keyword add <word>",
		RecallSent = "Up / Down in the input field: your previous messages",
		RightClickCopy = "Right-click a message to copy it",
		Close = "Close",
		WindowHint = "{0}: this window · /chatplus help: commands",
		None = "none",
		Custom = "Custom",
		ColorNames = new string[7] { "Soft", "Gray", "Gold", "Sky", "Mint", "Rose", "White" },
		EarlierMessages = "<align=center><size=80%><color=#F5EDE18C>— earlier messages —</color></size></align>",
		Copied = "Copied",
		DateFormat = "MM/dd",
		On = "on",
		Off = "off",
		TimeOn = "Message time: on",
		TimeOff = "Message time: off",
		TimeFormatSet = "Time format: {0} (looks like {1})",
		SizeSet = "Chat size: {0}% × {1}%",
		TextSizeSet = "Text size: {0}%",
		LimitsSet = "Lines kept: Global {0}, Local {1}",
		ChatCleared = "Chat cleared ({0} lines). The history still has them.",
		HistoryCleared = "History cleared",
		WindowReset = "Chat size and position reset",
		Status = "Time {0} ({1}), lines Global {2} / Local {3}, size {4}% × {5}%, text {6}%, history {7} lines, file {8}",
		KeywordAdded = "Keyword added: {0}",
		KeywordRemoved = "Keyword removed: {0}",
		KeywordMissing = "No such keyword: {0}",
		KeywordsList = "Keywords: {0}",
		KeywordsCleared = "Keywords cleared",
		UsageTime = "Usage: /chatplus time on | off | format <format>, e.g. HH:mm:ss or h:mm tt",
		UsageSize = "Usage: /chatplus size <width %> [height %] | reset",
		UsageText = "Usage: /chatplus text <size %>",
		UsageLimit = "Usage: /chatplus lines <Global> [Local]",
		UsageKeyword = "Usage: /chatplus keyword add | remove <word>, /chatplus keyword list | clear",
		Help = "Chat Plus, commands:\n/chatplus - settings window ({0})\n/chatplus time on | off | format <format>\n/chatplus size <width %> [height %] | reset\n/chatplus text <size %>\n/chatplus lines <Global> [Local] - lines kept in the chat\n/chatplus clear - clear the chat, /chatplus clearhistory - forget the history\n/chatplus folder - open the history folder\n/chatplus keyword add | remove <word>, keyword list | clear\n/chatplus status"
	};

	public static readonly Lang Russian = new Lang
	{
		Title = "Улучшенный чат",
		SectionTime = "Время сообщений",
		SectionHistory = "История",
		SectionWindow = "Окно чата",
		SectionExtras = "Дополнительно",
		ShowTime = "Показывать время каждого сообщения",
		TimeFormat = "Формат",
		TimeColor = "Цвет",
		TimeSize = "Размер",
		TimeOnNotifications = "И у уведомлений игры",
		GlobalLimit = "Строк в «Глобальном»",
		LocalLimit = "Строк в «Локальном»",
		LimitsHint = "Сама игра хранит 50 и 25 строк.",
		RestoreHistory = "Возвращать прошлые сообщения после смены лобби",
		RestoreNotifications = "Вместе с уведомлениями игры",
		SaveHistory = "Сохранять историю в файл",
		KeepAfterRestart = "Не очищать историю при перезапуске игры",
		DailyLogs = "Ещё и текстовые логи, по файлу на день",
		HistoryCount = "В истории этой сессии: {0} строк",
		OpenFolder = "Открыть папку",
		ClearChat = "Очистить чат",
		ClearHistory = "Очистить историю",
		ConfirmClear = "Нажмите ещё раз",
		Width = "Ширина",
		Height = "Высота",
		TextSize = "Размер текста",
		ResizeHandle = "Уголок для изменения размера",
		ResizeHint = "Тяните уголок в правом верхнем углу чата; двойной щелчок по нему — обычный размер.",
		RememberPosition = "Запоминать, куда перетащен чат",
		CullHiddenLines = "Не рисовать прокрученные строки (быстрее)",
		ResetWindow = "Сбросить размер и положение",
		HighlightMentions = "Подсвечивать сообщения, где упомянуты вы",
		HighlightColor = "Подсветка",
		Keywords = "Слова",
		KeywordsHint = "Ваше имя учитывается всегда. Ещё слова: /chatplus keyword add <слово>",
		RecallSent = "Вверх / вниз в поле ввода: ваши прошлые сообщения",
		RightClickCopy = "Правый щелчок по сообщению копирует его",
		Close = "Закрыть",
		WindowHint = "{0}: это окно · /chatplus help: команды",
		None = "нет",
		Custom = "Свой",
		ColorNames = new string[7] { "Мягкий", "Серый", "Золотой", "Голубой", "Мятный", "Розовый", "Белый" },
		EarlierMessages = "<align=center><size=80%><color=#F5EDE18C>— прошлые сообщения —</color></size></align>",
		Copied = "Скопировано",
		DateFormat = "dd.MM",
		On = "вкл",
		Off = "выкл",
		TimeOn = "Время сообщений: вкл",
		TimeOff = "Время сообщений: выкл",
		TimeFormatSet = "Формат времени: {0} (выглядит так: {1})",
		SizeSet = "Размер чата: {0}% × {1}%",
		TextSizeSet = "Размер текста: {0}%",
		LimitsSet = "Хранится строк: «Глобальный» {0}, «Локальный» {1}",
		ChatCleared = "Чат очищен ({0} строк). В истории они остались.",
		HistoryCleared = "История очищена",
		WindowReset = "Размер и положение чата сброшены",
		Status = "Время {0} ({1}), строк «Глобальный» {2} / «Локальный» {3}, размер {4}% × {5}%, текст {6}%, в истории {7} строк, файл {8}",
		KeywordAdded = "Добавлено: {0}",
		KeywordRemoved = "Удалено: {0}",
		KeywordMissing = "Такого слова нет: {0}",
		KeywordsList = "Слова: {0}",
		KeywordsCleared = "Слова удалены",
		UsageTime = "Использование: /chatplus time on | off | format <формат>, например HH:mm:ss или h:mm tt",
		UsageSize = "Использование: /chatplus size <ширина %> [высота %] | reset",
		UsageText = "Использование: /chatplus text <размер %>",
		UsageLimit = "Использование: /chatplus lines <Глобальный> [Локальный]",
		UsageKeyword = "Использование: /chatplus keyword add | remove <слово>, /chatplus keyword list | clear",
		Help = "Улучшенный чат, команды:\n/chatplus - окно настроек ({0})\n/chatplus time on | off | format <формат> - время сообщений\n/chatplus size <ширина %> [высота %] | reset - размер окна чата\n/chatplus text <размер %> - размер текста\n/chatplus lines <Глобальный> [Локальный] - сколько строк хранить в чате\n/chatplus clear - очистить чат, /chatplus clearhistory - стереть историю\n/chatplus folder - открыть папку с историей\n/chatplus keyword add | remove <слово>, keyword list | clear - слова для подсветки\n/chatplus status - текущие настройки"
	};

	private static Lang _auto = English;

	private static float _nextAutoCheck;

	public static Lang Current
	{
		get
		{
			Plugin instance = Plugin.Instance;
			switch (((Object)(object)instance != (Object)null) ? instance.Language.Value : UiLanguage.Auto)
			{
			case UiLanguage.English:
				return English;
			case UiLanguage.Russian:
				return Russian;
			default:
				if (Time.unscaledTime >= _nextAutoCheck)
				{
					_nextAutoCheck = Time.unscaledTime + 10f;
					_auto = (DetectRussian() ? Russian : English);
				}
				return _auto;
			}
		}
	}

	public static void Invalidate()
	{
		_nextAutoCheck = 0f;
	}

	public string ColorName(int index)
	{
		if (index < 0 || index >= ColorNames.Length)
		{
			return Custom;
		}
		return ColorNames[index];
	}

	private static bool DetectRussian()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Invalid comparison between Unknown and I4
		try
		{
			string text = GameLocaleCode();
			if (!string.IsNullOrEmpty(text))
			{
				return text.StartsWith("ru", StringComparison.OrdinalIgnoreCase);
			}
		}
		catch (Exception)
		{
		}
		return (int)Application.systemLanguage == 30;
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static string GameLocaleCode()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		Locale selectedLocale = LocalizationSettings.SelectedLocale;
		if (!((Object)(object)selectedLocale != (Object)null))
		{
			return null;
		}
		LocaleIdentifier identifier = selectedLocale.Identifier;
		return ((LocaleIdentifier)(ref identifier)).Code;
	}
}
internal sealed class LineSizer : MonoBehaviour
{
	private const float DefaultBaseSize = 46.8f;

	private const int RecentLines = 24;

	internal static float BaseSize = 46.8f;

	internal static int Version;

	private int _count = -1;

	private int _newestId;

	private int _version = -1;

	private static int Percent
	{
		get
		{
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return 100;
			}
			return instance.TextSize.Value;
		}
	}

	private static float TargetSize => BaseSize * (float)Percent / 100f;

	internal static void Apply(TMP_Text text)
	{
		if (!((Object)(object)text == (Object)null) && Percent != 100)
		{
			float targetSize = TargetSize;
			if (Math.Abs(text.fontSize - targetSize) > 0.01f)
			{
				text.fontSize = targetSize;
			}
		}
	}

	private void LateUpdate()
	{
		Transform transform = ((Component)this).transform;
		int childCount = transform.childCount;
		int num = ((childCount > 0) ? ((Object)transform.GetChild(childCount - 1)).GetInstanceID() : 0);
		bool flag = _version != Version;
		if (!flag && childCount == _count && num == _newestId)
		{
			return;
		}
		_count = childCount;
		_newestId = num;
		_version = Version;
		if (!flag && Percent == 100)
		{
			return;
		}
		float targetSize = TargetSize;
		for (int i = ((!flag) ? Math.Max(0, childCount - 24) : 0); i < childCount; i++)
		{
			TMP_Text component = ((Component)transform.GetChild(i)).GetComponent<TMP_Text>();
			if ((Object)(object)component != (Object)null && Math.Abs(component.fontSize - targetSize) > 0.01f)
			{
				component.fontSize = targetSize;
			}
		}
	}
}
internal static class OutlineResets
{
	private static TextChannelManager _chat;

	private static bool _outline;

	private static int _extra;

	internal static bool ShouldRun(TextChannelManager chat)
	{
		if (!GameAccess.TryGetFontOutline(out var outline))
		{
			return true;
		}
		if (chat != _chat)
		{
			_chat = chat;
			_outline = outline;
			_extra = 1;
			return true;
		}
		if (outline != _outline)
		{
			_outline = outline;
			_extra = 1;
			return true;
		}
		if (_extra > 0)
		{
			_extra--;
			return true;
		}
		return false;
	}
}
internal sealed class LineMark
{
	public GameObject Newest;

	public static LineMark Take(TextChannelManager chat, bool isLocal)
	{
		List<GameObject> list = GameAccess.Lines(chat, isLocal);
		return new LineMark
		{
			Newest = ((list != null && list.Count > 0) ? list[list.Count - 1] : null)
		};
	}
}
[HarmonyPatch(typeof(TextChannelManager), "AddMessageUI")]
internal static class AddMessagePatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(TextChannelManager __instance, bool isLocal, out LineMark __state)
	{
		__state = null;
		try
		{
			ChatLines.ApplyLimits();
			__state = LineMark.Take(__instance, isLocal);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not set the chat line limits: ", e);
		}
	}

	[HarmonyPostfix]
	[HarmonyPriority(800)]
	private static void Postfix(TextChannelManager __instance, string userName, string text, bool isLocal, int senderIndex, bool sendByPlayer, LineMark __state)
	{
		try
		{
			ChatLines.OnMessageAdded(__instance, userName, text, isLocal, senderIndex, sendByPlayer, __state);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not process a chat message: ", e);
		}
	}
}
[HarmonyPatch(typeof(TextChannelManager), "AddNotification")]
internal static class AddNotificationPatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(TextChannelManager __instance, out LineMark __state)
	{
		__state = null;
		try
		{
			ChatLines.ApplyLimits();
			__state = LineMark.Take(__instance, isLocal: false);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not set the chat line limits: ", e);
		}
	}

	[HarmonyPostfix]
	[HarmonyPriority(800)]
	private static void Postfix(TextChannelManager __instance, string text, LineMark __state)
	{
		try
		{
			ChatLines.OnNotificationAdded(__instance, text, __state);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not process a chat notification: ", e);
		}
	}
}
[HarmonyPatch(typeof(TextChannelManager), "Start")]
internal static class ChatStartPatch
{
	[HarmonyPostfix]
	private static void Postfix(TextChannelManager __instance)
	{
		try
		{
			ChatLines.OnChatStarted(__instance);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not restore the chat history: ", e);
		}
	}
}
[HarmonyPatch(typeof(TextChannelManager), "ResetTextMaterials")]
internal static class ResetMaterialsPatch
{
	[HarmonyPrefix]
	private static bool Prefix(TextChannelManager __instance)
	{
		try
		{
			return OutlineResets.ShouldRun(__instance);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not check the chat font outline: ", e);
			return true;
		}
	}
}
[HarmonyPatch(typeof(UIManager), "Start")]
internal static class UiStartPatch
{
	[HarmonyPostfix]
	private static void Postfix(UIManager __instance)
	{
		try
		{
			ChatWindow.Prepare(__instance);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not set up the chat window: ", e);
		}
	}
}
[HarmonyPatch(typeof(UIManager), "BeginDragMessage")]
internal static class BeginMovePatch
{
	[HarmonyPostfix]
	private static void Postfix(UIManager __instance)
	{
		try
		{
			ChatWindow.OnBeginMove(__instance);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not update the chat drag limits: ", e);
		}
	}
}
[HarmonyPatch(typeof(UIManager), "EndDragMessage")]
internal static class EndMovePatch
{
	[HarmonyPostfix]
	private static void Postfix(UIManager __instance)
	{
		try
		{
			ChatWindow.OnEndMove(__instance);
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not save the chat position: ", e);
		}
	}
}
[HarmonyPatch(typeof(TextChannelManager), "OnEnterPressed")]
internal static class EnterPatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	[HarmonyBefore(new string[] { "ontogether.chatsounds", "ontogether.localchatrange", "com.on-together-mods.commandapi", "com.andrewlin.ontogether.alpha" })]
	private static void Prefix()
	{
		string[] args;
		try
		{
			TMP_InputField messageInput = GameAccess.MessageInput;
			if ((Object)(object)messageInput == (Object)null)
			{
				return;
			}
			string text = messageInput.text;
			Plugin.Instance.Sent.Add(text);
			if (!ChatCommands.TryParse(text, out args))
			{
				return;
			}
			messageInput.text = string.Empty;
		}
		catch (Exception e)
		{
			Plugin.LogError("Could not read the chat input: ", e);
			return;
		}
		try
		{
			ChatCommands.Execute(args);
		}
		catch (Exception e2)
		{
			Plugin.LogError("Chat command failed: ", e2);
		}
	}
}
[BepInPlugin("ontogether.chatplus", "ChatPlus", "1.0.0")]
[BepInProcess("OnTogether.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "ontogether.chatplus";

	public const string PluginName = "ChatPlus";

	public const string PluginVersion = "1.0.0";

	internal const int MinWidth = 70;

	internal const int MaxWidth = 300;

	internal const int MinHeight = 50;

	internal const int MaxHeight = 200;

	internal const int MinTextSize = 60;

	internal const int MaxTextSize = 200;

	private const int HistoryCapacity = 5000;

	private const int SentCapacity = 50;

	private const float ConfigPollInterval = 1f;

	private const float ConfigSettleTime = 0.5f;

	private const float SaveDelay = 0.75f;

	private const float RetryDelay = 2f;

	private const float RestyleDelay = 0.2f;

	internal ConfigEntry<UiLanguage> Language;

	internal ConfigEntry<KeyboardShortcut> WindowKey;

	internal ConfigEntry<bool> Timestamps;

	internal ConfigEntry<string> TimeFormat;

	internal ConfigEntry<string> TimeColor;

	internal ConfigEntry<int> TimeSize;

	internal ConfigEntry<bool> TimeOnNotifications;

	internal ConfigEntry<int> GlobalLimit;

	internal ConfigEntry<int> LocalLimit;

	internal ConfigEntry<bool> RestoreHistory;

	internal ConfigEntry<bool> RestoreNotifications;

	internal ConfigEntry<bool> SaveHistory;

	internal ConfigEntry<bool> KeepAfterRestart;

	internal ConfigEntry<bool> DailyLogs;

	internal ConfigEntry<int> Width;

	internal ConfigEntry<int> Height;

	internal ConfigEntry<int> TextSize;

	internal ConfigEntry<bool> ResizeHandle;

	internal ConfigEntry<bool> RememberPosition;

	internal ConfigEntry<string> Position;

	internal ConfigEntry<bool> CullHiddenLines;

	internal ConfigEntry<bool> HighlightMentions;

	internal ConfigEntry<string> HighlightColor;

	internal ConfigEntry<string> Keywords;

	internal ConfigEntry<bool> RecallSent;

	internal ConfigEntry<bool> RightClickCopy;

	private GameObject _host;

	private SettingsWindow _window;

	private Harmony _harmony;

	private List<string> _keywords = new List<string>();

	private TMP_InputField _caretToEnd;

	private readonly Dictionary<ConfigEntryBase, object> _unsaved = new Dictionary<ConfigEntryBase, object>();

	private bool _reloading;

	private float _saveAt = -1f;

	private float _restyleAt = -1f;

	private float _resizeTextAt = -1f;

	private float _nextConfigPoll;

	private float _reloadAt = -1f;

	private DateTime _configStamp;

	private DateTime _pendingStamp;

	private bool _fileErrorLogged;

	private static string _lastError;

	internal static Plugin Instance { get; private set; }

	internal static ManualLogSource Log { get; private set; }

	internal ChatHistory History { get; private set; }

	internal SentHistory Sent { get; } = new SentHistory(50);

	internal IList<string> KeywordList => _keywords;

	private void Awake()
	{
		//IL_0102: 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_0114: Expected O, but got Unknown
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Expected O, but got Unknown
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfig();
		((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
		_keywords = TextUtil.ParseKeywords(Keywords.Value);
		_configStamp = ConfigStamp();
		History = new ChatHistory(Path.Combine(Paths.BepInExRootPath, "ChatPlus"), 5000, delegate(string message)
		{
			Log.LogWarning((object)message);
		});
		History.DailyLogs = DailyLogs.Value;
		History.Open(SaveHistory.Value && KeepAfterRestart.Value, SaveHistory.Value);
		if (History.Count > 0)
		{
			Log.LogInfo((object)$"Loaded {History.Count} chat lines from the previous session.");
		}
		_host = new GameObject("ChatPlus")
		{
			hideFlags = (HideFlags)61
		};
		Object.DontDestroyOnLoad((Object)(object)_host);
		_window = _host.AddComponent<SettingsWindow>();
		((Behaviour)_window).enabled = false;
		_host.AddComponent<Toast>();
		_harmony = new Harmony("ontogether.chatplus");
		Patch(typeof(AddMessagePatch), "new messages get no time and are not saved");
		Patch(typeof(AddNotificationPatch), "notifications get no time and are not saved");
		Patch(typeof(ChatStartPatch), "the history is not brought back after a lobby change");
		Patch(typeof(ResetMaterialsPatch), "a long chat is slower when it fades in and out");
		Patch(typeof(UiStartPatch), "the chat window cannot be resized");
		Patch(typeof(BeginMovePatch), "a resized chat may not be dragged to the right edge");
		Patch(typeof(EndMovePatch), "the chat position is not remembered");
		Patch(typeof(EnterPatch), "the /chatplus co