Decompiled source of WorldClock v1.0.0

plugins/WorldClock.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Hoose")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) Hoose 2026")]
[assembly: AssemblyDescription("Displays the current in-game time in a configurable screen position.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+be6f85042814c449b706f2a9f0cd4f68aa7a9f44")]
[assembly: AssemblyProduct("Valheim WorldClock")]
[assembly: AssemblyTitle("WorldClock")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WorldClock
{
	internal sealed class ClockDisplayCache
	{
		private static readonly IReadOnlyList<(string FontName, string Text, bool IsTime, string Prefix, string Suffix)> _invalidRuns = new(string, string, bool, string, string)[1] { (null, "Invalid clock format", false, string.Empty, string.Empty) };

		private string _template;

		private bool _hasTemplate;

		private bool _requiresDay;

		private bool _hasInput;

		private int _seconds;

		private int _day;

		private string _timeFormat;

		private bool _linearColorSpace;

		private string _renderedTemplate;

		private string _renderedTime;

		private string _renderedTimeFormat;

		private string _renderedPhase;

		private int _renderedDay;

		private bool _renderedLinearColorSpace;

		internal bool RequiresDay => _requiresDay;

		internal string Error { get; private set; }

		internal IReadOnlyList<(string FontName, string Text, bool IsTime, string Prefix, string Suffix)> Runs { get; private set; }

		internal void SetTemplate(string template)
		{
			if (!_hasTemplate || !(_template == template))
			{
				_template = template;
				_hasTemplate = true;
				_requiresDay = ClockTextFormatter.RequiresDay(template);
				_hasInput = false;
			}
		}

		internal void Update(float fraction, string timeFormat, int day, bool linearColorSpace)
		{
			int num = ClockTimeFormatter.SecondsIntoDay(fraction);
			if (_hasInput && _seconds == num && _day == day && _timeFormat == timeFormat && _linearColorSpace == linearColorSpace)
			{
				return;
			}
			_hasInput = true;
			_seconds = num;
			_day = day;
			_timeFormat = timeFormat;
			_linearColorSpace = linearColorSpace;
			try
			{
				var (text, text2) = ClockTimeFormatter.FormatSeconds(num, timeFormat);
				if (Error != null || Runs == null || !(_renderedTemplate == _template) || !(_renderedTime == text) || !(_renderedTimeFormat == timeFormat) || !(_renderedPhase == text2) || _renderedDay != day || _renderedLinearColorSpace != linearColorSpace)
				{
					string formattedText = ClockTextFormatter.Format(_template, text, day, text2, markTime: true);
					formattedText = ClockTextFormatter.PrepareColorsForRendering(formattedText, linearColorSpace);
					Runs = ClockTextFormatter.SplitDisplayRuns(formattedText);
					_renderedTemplate = _template;
					_renderedTime = text;
					_renderedTimeFormat = timeFormat;
					_renderedPhase = text2;
					_renderedDay = day;
					_renderedLinearColorSpace = linearColorSpace;
					Error = null;
				}
			}
			catch (FormatException ex)
			{
				Runs = _invalidRuns;
				Error = ex.Message;
			}
		}
	}
	internal static class ClockLayout
	{
		internal static float HorizontalOffset(float screenWidth, float labelWidth, float position)
		{
			float val = position * screenWidth - labelWidth / 2f;
			return Math.Max(0f, Math.Min(val, screenWidth - labelWidth));
		}

		internal static (float Width, float Height) LabelSize(float screenWidth, float screenHeight, float textWidth, float textHeight)
		{
			return (Width: Math.Min(screenWidth, textWidth + 4f), Height: Math.Min(screenHeight, textHeight + 8f));
		}

		internal static float TimeAdvance(float maximumWidth, float actualWidth)
		{
			return Math.Max(0f, Math.Max(maximumWidth, actualWidth) - 2f);
		}

		internal static (float NameWidth, float SampleOffset, float SampleWidth) FontDemoColumns(float availableWidth, float maximumNameWidth)
		{
			float num = Math.Max(1f, availableWidth);
			float num2 = Math.Min(16f, num / 10f);
			float num3 = Math.Min(Math.Max(0f, maximumNameWidth), (num - num2) / 2f);
			float num4 = num3 + num2;
			return (NameWidth: num3, SampleOffset: num4, SampleWidth: num - num4);
		}

		internal static (float Height, float NameOffset, float SampleOffset) FontDemoRow(float nameHeight, float sampleHeight)
		{
			float num = Math.Max(0f, nameHeight);
			float num2 = Math.Max(0f, sampleHeight);
			float num3 = Math.Max(30f, (float)Math.Ceiling(Math.Max(num, num2)) + 8f);
			return (Height: num3, NameOffset: (num3 - num) / 2f, SampleOffset: (num3 - num2) / 2f);
		}
	}
	internal static class ClockTextFormatter
	{
		internal const string DefaultTemplate = "<b><font size=24 color=#FF8E0080>Day {day} <font size=18 color=#B4B4B480>|</font> {time} <font size=18 color=#B4B4B480>|</font> {phase}</font></b>";

		private static readonly Regex _placeholder = new Regex("\\{([a-z]+)\\}", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _fontTag = new Regex("</?font\\b[^>]*>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _attribute = new Regex("\\G\\s+(size|color|name)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"']+))", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _hexColor = new Regex("^#(?:[0-9a-f]{6}|[0-9a-f]{8})$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _colorTag = new Regex("</?color(?:\\s*=\\s*[^>]*)?>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _hexColorTag = new Regex("(<color=)#([0-9a-f]{6})([0-9a-f]{2})?(>)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex _runTokens = new Regex("<worldclockfont(?:=([^>]*))?>|<worldclocktime(?:=end)?>|</?(?:b|i|size|color)(?:=[^>]*)?>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		internal static bool RequiresDay(string template)
		{
			if (template != null)
			{
				return Regex.IsMatch(template, "\\{day\\}", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			}
			return false;
		}

		internal static string Format(string template, string time, int day, string phase, bool markTime = false)
		{
			if (string.IsNullOrWhiteSpace(template))
			{
				throw new FormatException("The display format cannot be empty.");
			}
			if (template.IndexOf("<worldclockfont", StringComparison.OrdinalIgnoreCase) >= 0 || template.IndexOf("<worldclocktime", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				throw new FormatException("The display format contains a reserved tag.");
			}
			string text = _placeholder.Replace(template, delegate(Match match3)
			{
				switch (match3.Groups[1].Value.ToLowerInvariant())
				{
				case "time":
				{
					int num3 = template.LastIndexOf('<', match3.Index);
					int num4 = template.LastIndexOf('>', match3.Index);
					if (markTime && num3 > num4)
					{
						return time;
					}
					if (markTime)
					{
						return "<worldclocktime>" + time + "<worldclocktime=end>";
					}
					return time;
				}
				case "day":
					return day.ToString(CultureInfo.InvariantCulture);
				case "phase":
					return phase;
				default:
					throw new FormatException("Unknown placeholder '" + match3.Value + "'. Use {time}, {day}, or {phase}.");
				}
			});
			StringBuilder stringBuilder = new StringBuilder();
			Stack<(string, string)> stack = new Stack<(string, string)>();
			string text2 = null;
			int num = 0;
			foreach (Match item2 in _fontTag.Matches(text))
			{
				stringBuilder.Append(text, num, item2.Index - num);
				if (item2.Value.StartsWith("</", StringComparison.Ordinal))
				{
					if (!string.Equals(item2.Value, "</font>", StringComparison.OrdinalIgnoreCase) || stack.Count == 0)
					{
						throw new FormatException("Unexpected closing font tag.");
					}
					(string, string) tuple = stack.Pop();
					stringBuilder.Append(tuple.Item1);
					if (text2 != tuple.Item2)
					{
						text2 = tuple.Item2;
						AppendFontMarker(stringBuilder, text2);
					}
				}
				else
				{
					string text3 = item2.Value.Substring(5, item2.Value.Length - 6);
					string text4 = null;
					string text5 = null;
					string text6 = null;
					int num2 = 0;
					while (num2 < text3.Length && !string.IsNullOrWhiteSpace(text3.Substring(num2)))
					{
						Match match2 = _attribute.Match(text3, num2);
						if (!match2.Success)
						{
							throw new FormatException("Font tags support only name, size and color attributes.");
						}
						string value = match2.Groups[1].Value;
						string text7 = (match2.Groups[2].Success ? match2.Groups[2].Value : (match2.Groups[3].Success ? match2.Groups[3].Value : match2.Groups[4].Value));
						if (string.Equals(value, "size", StringComparison.OrdinalIgnoreCase))
						{
							if (text4 != null || !int.TryParse(text7, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 256)
							{
								throw new FormatException("Font size must be an integer from 1 to 256 and appear only once.");
							}
							text4 = result.ToString(CultureInfo.InvariantCulture);
						}
						else if (string.Equals(value, "color", StringComparison.OrdinalIgnoreCase))
						{
							if (text5 != null || !_hexColor.IsMatch(text7))
							{
								throw new FormatException("Font color must be a single #RRGGBB or #RRGGBBAA value.");
							}
							text5 = text7;
						}
						else
						{
							if (text6 != null || !FontCatalog.IsValidName(text7))
							{
								throw new FormatException("Font name must be nonempty and cannot contain angle brackets, quotes or control characters.");
							}
							text6 = text7;
						}
						num2 = match2.Index + match2.Length;
					}
					if (text4 == null && text5 == null && text6 == null)
					{
						throw new FormatException("Font tags require a name, size or color attribute.");
					}
					string text8 = string.Empty;
					string item = text2;
					if (text6 != null && text6 != text2)
					{
						text2 = text6;
						AppendFontMarker(stringBuilder, text2);
					}
					if (text4 != null)
					{
						stringBuilder.Append("<size=").Append(text4).Append('>');
						text8 = "</size>";
					}
					if (text5 != null)
					{
						stringBuilder.Append("<color=").Append(text5).Append('>');
						text8 = "</color>" + text8;
					}
					stack.Push((text8, item));
				}
				num = item2.Index + item2.Length;
			}
			stringBuilder.Append(text, num, text.Length - num);
			if (stack.Count != 0 || _fontTag.IsMatch(stringBuilder.ToString()) || Regex.IsMatch(stringBuilder.ToString(), "</?font\\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
			{
				throw new FormatException("A font tag is unclosed or malformed.");
			}
			return stringBuilder.ToString();
		}

		internal static string Shadow(string formattedText)
		{
			return _colorTag.Replace(formattedText, string.Empty);
		}

		internal static string PrepareColorsForRendering(string formattedText, bool linearColorSpace)
		{
			if (!linearColorSpace)
			{
				return formattedText;
			}
			return _hexColorTag.Replace(formattedText, delegate(Match match)
			{
				string value = match.Groups[2].Value;
				string text = EncodeColorChannel(value.Substring(0, 2));
				string text2 = EncodeColorChannel(value.Substring(2, 2));
				string text3 = EncodeColorChannel(value.Substring(4, 2));
				return match.Groups[1].Value + "#" + text + text2 + text3 + match.Groups[3].Value + match.Groups[4].Value;
			});
		}

		internal static IReadOnlyList<(string FontName, string Text)> SplitRuns(string formattedText)
		{
			List<(string, string)> list = new List<(string, string)>();
			foreach (var item in SplitDisplayRuns(formattedText))
			{
				list.Add((item.FontName, item.Text));
			}
			return list;
		}

		internal static IReadOnlyList<(string FontName, string Text, bool IsTime, string Prefix, string Suffix)> SplitDisplayRuns(string formattedText)
		{
			List<(string, string, bool, string, string)> list = new List<(string, string, bool, string, string)>();
			List<(string, string)> list2 = new List<(string, string)>();
			StringBuilder stringBuilder = new StringBuilder();
			string item = null;
			int num = 0;
			bool flag = false;
			bool item2 = false;
			string item3 = string.Empty;
			foreach (Match item4 in _runTokens.Matches(formattedText))
			{
				string text = formattedText.Substring(num, item4.Index - num);
				stringBuilder.Append(text);
				flag |= text.Length > 0;
				if (item4.Value.StartsWith("<worldclockfont", StringComparison.OrdinalIgnoreCase) || item4.Value.StartsWith("<worldclocktime", StringComparison.OrdinalIgnoreCase))
				{
					if (flag)
					{
						StringBuilder stringBuilder2 = new StringBuilder();
						for (int num2 = list2.Count - 1; num2 >= 0; num2--)
						{
							stringBuilder2.Append("</").Append(list2[num2].Item1).Append('>');
						}
						stringBuilder.Append(stringBuilder2);
						list.Add((item, stringBuilder.ToString(), item2, item3, stringBuilder2.ToString()));
					}
					stringBuilder.Clear();
					foreach (var item5 in list2)
					{
						stringBuilder.Append(item5.Item2);
					}
					item3 = stringBuilder.ToString();
					flag = false;
					if (item4.Value.StartsWith("<worldclockfont", StringComparison.OrdinalIgnoreCase))
					{
						item = (item4.Groups[1].Success ? item4.Groups[1].Value : null);
					}
					else
					{
						item2 = !item4.Value.Equals("<worldclocktime=end>", StringComparison.OrdinalIgnoreCase);
					}
				}
				else
				{
					stringBuilder.Append(item4.Value);
					bool flag2 = item4.Value.StartsWith("</", StringComparison.Ordinal);
					int num3 = ((!flag2) ? 1 : 2);
					int num4 = item4.Value.IndexOfAny(new char[2] { '=', '>' }, num3);
					string text2 = item4.Value.Substring(num3, num4 - num3).ToLowerInvariant();
					if (flag2)
					{
						if (list2.Count == 0 || list2[list2.Count - 1].Item1 != text2)
						{
							throw new FormatException("Unbalanced rich-text tag: " + item4.Value + ".");
						}
						list2.RemoveAt(list2.Count - 1);
					}
					else
					{
						list2.Add((text2, item4.Value));
					}
				}
				num = item4.Index + item4.Length;
			}
			string text3 = formattedText.Substring(num);
			stringBuilder.Append(text3);
			flag |= text3.Length > 0;
			if (list2.Count != 0)
			{
				throw new FormatException("Rich-text tags must be properly closed.");
			}
			if (flag)
			{
				list.Add((item, stringBuilder.ToString(), item2, item3, string.Empty));
			}
			return list;
		}

		private static string EncodeColorChannel(string hex)
		{
			double num = (double)int.Parse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture) / 255.0;
			return ((int)Math.Round(((num <= 0.0031308) ? (num * 12.92) : (1.055 * Math.Pow(num, 5.0 / 12.0) - 0.055)) * 255.0, MidpointRounding.AwayFromZero)).ToString("X2", CultureInfo.InvariantCulture);
		}

		private static void AppendFontMarker(StringBuilder output, string fontName)
		{
			output.Append("<worldclockfont");
			if (fontName != null)
			{
				output.Append('=').Append(fontName);
			}
			output.Append('>');
		}
	}
	internal static class ClockTimeFormatter
	{
		private const int SecondsPerDay = 86400;

		internal static (string Time, string Phase) Format(float dayFraction, string timeFormat)
		{
			return FormatSeconds(SecondsIntoDay(dayFraction), timeFormat);
		}

		internal static int SecondsIntoDay(float dayFraction)
		{
			if (float.IsNaN(dayFraction) || float.IsInfinity(dayFraction))
			{
				throw new ArgumentOutOfRangeException("dayFraction", "The day fraction must be finite.");
			}
			return (int)Math.Floor(((double)dayFraction - Math.Floor(dayFraction)) * 86400.0);
		}

		internal static (string Time, string Phase) FormatSeconds(int secondsIntoDay, string timeFormat)
		{
			if (string.IsNullOrWhiteSpace(timeFormat))
			{
				throw new FormatException("The time format cannot be empty.");
			}
			int num = secondsIntoDay / 3600;
			string item = DateTime.MinValue.AddSeconds(secondsIntoDay).ToString(timeFormat, CultureInfo.InvariantCulture);
			string text = ((num < 12) ? ((num < 6) ? ((num >= 4) ? "Before dawn" : "Late night") : ((num >= 8) ? "Morning" : "Dawn")) : ((num < 17) ? ((num >= 14) ? "Afternoon" : "Midday") : ((num >= 20) ? "Night" : "Evening")));
			string item2 = text;
			return (Time: item, Phase: item2);
		}
	}
	internal static class FontCatalog
	{
		internal const string DefaultName = "Norsebold";

		internal const int DefaultSize = 22;

		internal const string DemoLabelName = "AveriaSansLibre-Bold";

		private static readonly Regex _validName = new Regex("^[^<>\"'\\x00-\\x1f\\x7f]+$", RegexOptions.CultureInvariant);

		internal static bool IsValidName(string name)
		{
			if (!string.IsNullOrWhiteSpace(name))
			{
				return _validName.IsMatch(name);
			}
			return false;
		}

		internal static string EffectiveName(string name)
		{
			return name ?? "Norsebold";
		}

		internal static string DisplayName(string name)
		{
			if (!string.Equals(name, "Norsebold", StringComparison.OrdinalIgnoreCase))
			{
				return name;
			}
			return name + " (default)";
		}

		internal static IReadOnlyList<string> AvailableNames(IEnumerable<string> names, Predicate<string> isAvailable)
		{
			List<string> list = new List<string>();
			foreach (string name in names)
			{
				if (IsValidName(name) && isAvailable(name))
				{
					list.Add(name);
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return list;
		}
	}
	internal sealed class TimeWidthCandidates
	{
		private readonly HashSet<string> _values = new HashSet<string>(StringComparer.Ordinal);

		internal TimeWidthCandidates(string format)
		{
			for (int i = 0; i < 86400; i++)
			{
				_values.Add(DateTime.MinValue.AddSeconds(i).ToString(format, CultureInfo.InvariantCulture));
			}
		}

		internal float Measure(Func<string, float> measure)
		{
			float num = 0f;
			foreach (string value in _values)
			{
				num = Math.Max(num, measure(value));
			}
			return num;
		}
	}
	[BepInPlugin("hoose.valheim.worldclock", "WorldClock", "1.0.0")]
	[BepInProcess("valheim.exe")]
	public sealed class WorldClockPlugin : BaseUnityPlugin
	{
		public const string PluginName = "WorldClock";

		public const string PluginAuthor = "Hoose";

		public const string PluginGuid = "hoose.valheim.worldclock";

		public const string PluginVersion = "1.0.0";

		private ConfigEntry<float> _horizontalPosition;

		private ConfigEntry<float> _verticalPosition;

		private ConfigEntry<string> _displayFormat;

		private ConfigEntry<string> _timeFormat;

		private ConfigEntry<bool> _showFontDemo;

		private ConfigEntry<string> _fontDemoText;

		private readonly ClockDisplayCache _displayCache = new ClockDisplayCache();

		private IReadOnlyList<(string FontName, string Text, bool IsTime, string Prefix, string Suffix)> _runs;

		private IReadOnlyList<(string FontName, string Text, bool IsTime, string Prefix, string Suffix)> _measuredRuns;

		private List<(GUIStyle Style, GUIStyle ShadowStyle, string Text, string ShadowText, Vector2 Size, float Advance)> _measured;

		private readonly Dictionary<(Font Font, string Prefix, string Suffix), float> _timeWidths = new Dictionary<(Font, string, string), float>();

		private string _widthTimeFormat;

		private TimeWidthCandidates _timeCandidates;

		private float _measuredWidth;

		private float _measuredHeight;

		private bool _formatErrorLogged;

		private bool _demoFontWarningLogged;

		private string _missingFont;

		private float _nextFontRefresh;

		private Vector2 _demoScroll;

		private List<(string Name, string Sample, GUIStyle SampleStyle, float Height, float NameHeight, float SampleHeight, float NameOffset, float SampleOffset)> _demoRows;

		private GUIStyle _demoNameStyle;

		private GUIStyle _demoHintStyle;

		private float _demoContentWidth;

		private float _demoContentHeight;

		private string _demoSample;

		private (float NameWidth, float SampleOffset, float SampleWidth) _demoColumns;

		private readonly Dictionary<string, Font> _fonts = new Dictionary<string, Font>(StringComparer.OrdinalIgnoreCase);

		private GUIStyle _clockStyle;

		private GUIStyle _shadowStyle;

		private void Awake()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			_displayFormat = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "Display format", "<b><font size=24 color=#FF8E0080>Day {day} <font size=18 color=#B4B4B480>|</font> {time} <font size=18 color=#B4B4B480>|</font> {phase}</font></b>", "Whole clock template. Use {time}, {day}, and {phase}. Style with <b> and <font name=\"Norse\" size=24 color=#FFFFFF>.");
			_timeFormat = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "Time format", "HH:mm", "A .NET custom date and time format string.");
			_horizontalPosition = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "Horizontal position", 0.07f, new ConfigDescription("Horizontal center of the clock as a fraction of screen width: 0 = left edge, 0.5 = center, 1 = right edge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_verticalPosition = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "Vertical position", 0f, new ConfigDescription("Top edge of the clock, as a fraction of screen height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_showFontDemo = ((BaseUnityPlugin)this).Config.Bind<bool>("Font demo", "Show font demo", false, "Display all currently available fonts on screen, one per row. Open the game menu to use the mouse wheel over the list.");
			_fontDemoText = ((BaseUnityPlugin)this).Config.Bind<string>("Font demo", "Sample text", "Day 42 | 12:00", "Text shown after each font name in the sampler.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Active color space: {2}.", "WorldClock", "1.0.0", QualitySettings.activeColorSpace));
		}

		private void Update()
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Invalid comparison between Unknown and I4
			_displayCache.SetTemplate(_displayFormat.Value);
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Hud.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null || (_displayCache.RequiresDay && (Object)(object)ZNet.instance == (Object)null))
			{
				_runs = null;
				return;
			}
			int day = (_displayCache.RequiresDay ? EnvMan.instance.GetDay() : 0);
			_displayCache.Update(EnvMan.instance.GetDayFraction(), _timeFormat.Value, day, (int)QualitySettings.activeColorSpace == 1);
			_runs = _displayCache.Runs;
			if (_displayCache.Error == null)
			{
				_formatErrorLogged = false;
			}
			else if (!_formatErrorLogged)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Invalid WorldClock display or time format: " + _displayCache.Error));
				_formatErrorLogged = true;
			}
		}

		private void OnGUI()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0036: 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)
			if (_runs == null && (!_showFontDemo.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Hud.instance == (Object)null))
			{
				return;
			}
			Color color = GUI.color;
			Color contentColor = GUI.contentColor;
			try
			{
				GUI.color = Color.white;
				GUI.contentColor = Color.white;
				DrawOverlay();
			}
			finally
			{
				GUI.color = color;
				GUI.contentColor = contentColor;
			}
		}

		private void DrawOverlay()
		{
			//IL_031a: 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_00c4: Expected O, but got Unknown
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			EnsureStyles();
			if (Time.unscaledTime >= _nextFontRefresh)
			{
				RefreshFonts();
			}
			if (_runs == null)
			{
				DrawFontDemo();
				return;
			}
			if (_measured != null)
			{
				foreach (var item3 in _measured)
				{
					if ((Object)(object)item3.Style.font == (Object)null)
					{
						_measuredRuns = null;
						break;
					}
				}
			}
			if (_measuredRuns != _runs)
			{
				List<(GUIStyle, GUIStyle, string, string, Vector2, float)> list = new List<(GUIStyle, GUIStyle, string, string, Vector2, float)>();
				float num = 0f;
				float num2 = 0f;
				for (int i = 0; i < _runs.Count; i++)
				{
					(string, string, bool, string, string) tuple = _runs[i];
					GUIStyle val = new GUIStyle(_clockStyle);
					string text = FontCatalog.EffectiveName(tuple.Item1);
					if (!_fonts.TryGetValue(text, out var value) || (Object)(object)value == (Object)null)
					{
						if (_missingFont != text)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("WorldClock font '" + text + "' is not currently loaded. Check the font demo or choose another font."));
							_missingFont = text;
						}
						GUI.Label(new Rect(0f, 0f, (float)Screen.width, 30f), "WorldClock font unavailable: " + text, _clockStyle);
						if (_showFontDemo.Value)
						{
							DrawFontDemo();
						}
						return;
					}
					val.font = value;
					Vector2 val2 = val.CalcSize(new GUIContent(tuple.Item2));
					GUIStyle val3 = new GUIStyle(val);
					val3.normal.textColor = _shadowStyle.normal.textColor;
					float num3 = (tuple.Item3 ? TimeWidth(val, value, tuple.Item4, tuple.Item5, val2.x) : val2.x) + ((i == _runs.Count - 1 || !string.Equals(tuple.Item1, _runs[i + 1].FontName, StringComparison.OrdinalIgnoreCase)) ? 4f : 0f);
					list.Add((val, val3, tuple.Item2, ClockTextFormatter.Shadow(tuple.Item2), val2, num3));
					num += num3;
					num2 = Mathf.Max(num2, val2.y);
				}
				_measured = list;
				_measuredRuns = _runs;
				_measuredWidth = num;
				_measuredHeight = num2;
			}
			_missingFont = null;
			(float Width, float Height) tuple2 = ClockLayout.LabelSize(Screen.width, Screen.height, _measuredWidth, _measuredHeight);
			float item = tuple2.Width;
			float item2 = tuple2.Height;
			float num4 = ClockLayout.HorizontalOffset(Screen.width, item, _horizontalPosition.Value);
			float num5 = Mathf.Clamp(_verticalPosition.Value * (float)Screen.height, 0f, Mathf.Max(0f, (float)Screen.height - item2));
			GUI.BeginGroup(new Rect(num4, num5, item, item2));
			float num6 = Mathf.Max(0f, (item - _measuredWidth) / 2f);
			Rect val4 = default(Rect);
			foreach (var item4 in _measured)
			{
				((Rect)(ref val4))..ctor(num6, 0f, item4.Size.x + 4f, item2 - 2f);
				GUI.Label(new Rect(((Rect)(ref val4)).x + 2f, ((Rect)(ref val4)).y + 2f, ((Rect)(ref val4)).width, ((Rect)(ref val4)).height), item4.ShadowText, item4.ShadowStyle);
				GUI.Label(val4, item4.Text, item4.Style);
				num6 += item4.Advance;
			}
			GUI.EndGroup();
			if (_showFontDemo.Value)
			{
				DrawFontDemo();
			}
		}

		private float TimeWidth(GUIStyle style, Font font, string prefix, string suffix, float actualWidth)
		{
			string value = _timeFormat.Value;
			if (_timeCandidates == null || _widthTimeFormat != value)
			{
				_timeCandidates = new TimeWidthCandidates(value);
				_widthTimeFormat = value;
				_timeWidths.Clear();
			}
			(Font, string, string) key = (font, prefix, suffix);
			if (!_timeWidths.TryGetValue(key, out var value2))
			{
				value2 = _timeCandidates.Measure((string text) => style.CalcSize(new GUIContent(prefix + text + suffix)).x);
				value2 = Mathf.Ceil(value2);
				_timeWidths.Add(key, value2);
			}
			return ClockLayout.TimeAdvance(value2, actualWidth);
		}

		private void EnsureStyles()
		{
			//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)
			//IL_0020: 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_002f: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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: Expected O, but got Unknown
			//IL_0060: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (_clockStyle == null)
			{
				_clockStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 22,
					richText = true,
					wordWrap = false,
					fixedWidth = 0f,
					fixedHeight = 0f,
					padding = new RectOffset(0, 0, 0, 0)
				};
				_clockStyle.normal.textColor = Color.white;
				_shadowStyle = new GUIStyle(_clockStyle);
				_shadowStyle.normal.textColor = new Color(0f, 0f, 0f, 0.8f);
			}
		}

		private void RefreshFonts()
		{
			_measuredRuns = null;
			_demoRows = null;
			_fonts.Clear();
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			foreach (Font val in array)
			{
				if ((Object)(object)val != (Object)null && FontCatalog.IsValidName(((Object)val).name) && !_fonts.ContainsKey(((Object)val).name))
				{
					_fonts.Add(((Object)val).name, val);
				}
			}
			_nextFontRefresh = Time.unscaledTime + 5f;
		}

		private void DrawFontDemo()
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Expected O, but got Unknown
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Expected O, but got Unknown
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Expected O, but got Unknown
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Expected O, but got Unknown
			float num = Mathf.Max(1f, (float)Screen.width * 0.9f);
			float num2 = Mathf.Max(1f, (float)Screen.height * 0.65f);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((float)Screen.width - num) / 2f, (float)Screen.height * 0.2f, num, num2);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 4f, ((Rect)(ref val)).y + 44f, Mathf.Max(1f, ((Rect)(ref val)).width - 8f), Mathf.Max(1f, ((Rect)(ref val)).height - 48f));
			float num3 = Mathf.Max(1f, ((Rect)(ref val2)).width - 20f);
			if (_demoRows != null)
			{
				foreach (var demoRow in _demoRows)
				{
					if ((Object)(object)demoRow.SampleStyle.font == (Object)null)
					{
						_demoRows = null;
						break;
					}
				}
			}
			if (_demoRows == null || _demoContentWidth != num3 || _demoSample != _fontDemoText.Value)
			{
				GUIStyle val3 = new GUIStyle(_clockStyle)
				{
					fontSize = 18,
					richText = false,
					wordWrap = true,
					alignment = (TextAnchor)0
				};
				if (_fonts.TryGetValue("AveriaSansLibre-Bold", out var value) && (Object)(object)value != (Object)null)
				{
					val3.font = value;
					_demoFontWarningLogged = false;
				}
				else if (!_demoFontWarningLogged)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"WorldClock font demo label font 'AveriaSansLibre-Bold' is not currently loaded; using the Unity GUI label font.");
					_demoFontWarningLogged = true;
				}
				float num4 = 0f;
				IReadOnlyList<string> readOnlyList = FontCatalog.AvailableNames(_fonts.Keys, (string name) => (Object)(object)_fonts[name] != (Object)null);
				foreach (string item in readOnlyList)
				{
					num4 = Mathf.Max(num4, val3.CalcSize(new GUIContent(FontCatalog.DisplayName(item))).x);
				}
				(float, float, float) demoColumns = ClockLayout.FontDemoColumns(num3, num4);
				List<(string, string, GUIStyle, float, float, float, float, float)> list = new List<(string, string, GUIStyle, float, float, float, float, float)>();
				float num5 = 0f;
				foreach (string item2 in readOnlyList)
				{
					GUIStyle val4 = new GUIStyle(val3);
					val4.font = _fonts[item2];
					string value2 = _fontDemoText.Value;
					string text = FontCatalog.DisplayName(item2);
					float num6 = Mathf.Ceil(Mathf.Max(val3.lineHeight, val3.CalcHeight(new GUIContent(text), Mathf.Max(1f, demoColumns.Item1))));
					float num7 = Mathf.Ceil(Mathf.Max(val4.lineHeight, val4.CalcHeight(new GUIContent(value2), Mathf.Max(1f, demoColumns.Item3))));
					(float, float, float) tuple = ClockLayout.FontDemoRow(num6, num7);
					list.Add((text, value2, val4, tuple.Item1, num6, num7, tuple.Item2, tuple.Item3));
					num5 += tuple.Item1 + 2f;
				}
				_demoNameStyle = val3;
				_demoHintStyle = new GUIStyle(val3)
				{
					fontSize = 14
				};
				_demoColumns = demoColumns;
				_demoRows = list;
				_demoContentHeight = num5;
				_demoContentWidth = num3;
				_demoSample = _fontDemoText.Value;
			}
			GUI.Box(val, string.Empty);
			GUI.Label(new Rect(((Rect)(ref val)).x + 8f, ((Rect)(ref val)).y + 4f, Mathf.Max(1f, ((Rect)(ref val)).width - 16f), 38f), "Font demo: open the game menu (Esc), then use the mouse wheel over the list to scroll.", _demoHintStyle);
			if (_demoRows.Count == 0)
			{
				GUI.Label(val2, "No usable fonts are currently loaded.", _demoNameStyle);
				return;
			}
			_demoScroll = GUI.BeginScrollView(val2, _demoScroll, new Rect(0f, 0f, num3, _demoContentHeight));
			float num8 = 0f;
			foreach (var demoRow2 in _demoRows)
			{
				GUI.BeginGroup(new Rect(0f, num8, num3, demoRow2.Height));
				GUI.Label(new Rect(0f, demoRow2.NameOffset, _demoColumns.NameWidth, demoRow2.NameHeight), demoRow2.Name, _demoNameStyle);
				GUI.Label(new Rect(_demoColumns.SampleOffset, demoRow2.Rest.Item1, _demoColumns.SampleWidth, demoRow2.SampleHeight), demoRow2.Sample, demoRow2.SampleStyle);
				GUI.EndGroup();
				num8 += demoRow2.Height + 2f;
			}
			GUI.EndScrollView();
		}
	}
}