Decompiled source of PerformanceTracker v1.0.3

PerformanceTracker.dll

Decompiled 6 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FPSCounter;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using PerformanceTracker.Util;
using PerformanceTracker.Util.Helpers;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PerformanceTracker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Azumatt")]
[assembly: AssemblyProduct("PerformanceTracker")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("E0E2F92E-557C-4A05-9D89-AA92A0BD75C4")]
[assembly: AssemblyFileVersion("1.0.3")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace PerformanceTracker
{
	[BepInPlugin("Azumatt.PerformanceTracker", "PerformanceTracker", "1.0.3")]
	public class PerformanceTrackerPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		public enum CounterColors
		{
			White,
			Black,
			Outline
		}

		private class ConfigurationManagerAttributes
		{
			[UsedImplicitly]
			public int? Order;

			[UsedImplicitly]
			public bool? Browsable;

			[UsedImplicitly]
			public string? Category;

			[UsedImplicitly]
			public Action<ConfigEntryBase>? CustomDrawer;
		}

		internal const string ModName = "PerformanceTracker";

		internal const string ModVersion = "1.0.3";

		internal const string Author = "Azumatt";

		internal const string ModGUID = "Azumatt.PerformanceTracker";

		private static string ConfigFileName = "Azumatt.PerformanceTracker.cfg";

		private static string ConfigFileFullPath;

		internal static string ConnectionError;

		private readonly Harmony _harmony = new Harmony("Azumatt.PerformanceTracker");

		internal static PerformanceTrackerPlugin ModContext;

		internal static GameObject pluginGO;

		public static readonly ManualLogSource PerformanceTrackerLogger;

		private static ConfigEntry<Toggle> _serverConfigLocked;

		private static ConfigEntry<KeyboardShortcut> _showCounter;

		internal static ConfigEntry<CounterColors> _counterColor;

		internal static ConfigEntry<TextAnchor> _position;

		internal static ConfigEntry<Toggle> _shown;

		internal static ConfigEntry<Toggle> _showPluginStats;

		internal static ConfigEntry<Toggle> _showUnityMethodStats;

		internal static ConfigEntry<Toggle> _measureMemory;

		public void Awake()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			ModContext = this;
			pluginGO = ((Component)this).gameObject;
			_showCounter = config<KeyboardShortcut>("1 - General", "Toggle counter and reset stats", new KeyboardShortcut((KeyCode)117, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Key to enable and disable the plugin.");
			_shown = config("1 - General", "Enable", Toggle.On, "Monitor performance statistics and show them on the screen. When disabled the plugin has no effect on performance.");
			_showPluginStats = config("1 - General", "Enable monitoring plugins", Toggle.On, "Count time each plugin takes every frame to execute. Only detects MonoBehaviour event methods, so results might be lower than expected. Has a small performance penalty.");
			_showUnityMethodStats = config("1 - General", "Show detailed frame stats", Toggle.On, "Show how much time was spent by Unity in each part of the frame, for example how long it took to run all Update methods.");
			try
			{
				MemoryInfo.PROCESS_MEMORY_COUNTERS pROCESS_MEMORY_COUNTERS = MemoryInfo.QueryProcessMemStatus();
				MemoryInfo.MEMORYSTATUSEX mEMORYSTATUSEX = MemoryInfo.QuerySystemMemStatus();
				if (pROCESS_MEMORY_COUNTERS.WorkingSetSize == 0 || mEMORYSTATUSEX.ullTotalPhys == 0)
				{
					throw new IOException("Empty data was returned");
				}
				_measureMemory = config("General", "Show memory and GC stats", Toggle.On, "Show memory usage of the process, free available physical memory and garbage collector statistics (if available).");
			}
			catch (Exception ex)
			{
				PerformanceTrackerLogger.LogWarning((object)("Memory statistics are not available - " + ex.Message));
			}
			_position = config<TextAnchor>("Interface", "Screen position", (TextAnchor)8, "Which corner of the screen to display the statistics in.");
			_counterColor = config("Interface", "Color of the text", CounterColors.White, "Color of the displayed stats. Outline has a performance hit but it always easy to see.");
			_position.SettingChanged += delegate
			{
				UIHelper.UpdateLooks();
			};
			_counterColor.SettingChanged += delegate
			{
				UIHelper.UpdateLooks();
			};
			_shown.SettingChanged += delegate
			{
				UIHelper.UpdateLooks();
				Helpers.SetCapturingEnabled(_shown.Value == Toggle.On);
			};
			_showPluginStats.SettingChanged += delegate
			{
				Helpers.SetCapturingEnabled(_shown.Value == Toggle.On);
			};
			OnEnable();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SetupWatcher();
		}

		private void OnEnable()
		{
			Helpers.OnEnable();
		}

		private void OnDisable()
		{
			Helpers.OnDisable();
		}

		private void Update()
		{
			//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)
			KeyboardShortcut value = _showCounter.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_shown.Value = ((_shown.Value == Toggle.Off) ? Toggle.On : Toggle.Off);
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
			Helpers.SetCapturingEnabled(enableCapturing: false);
		}

		private void SetupWatcher()
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			fileSystemWatcher.Changed += ReadConfigValues;
			fileSystemWatcher.Created += ReadConfigValues;
			fileSystemWatcher.Renamed += ReadConfigValues;
			fileSystemWatcher.IncludeSubdirectories = true;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(ConfigFileFullPath))
			{
				return;
			}
			try
			{
				PerformanceTrackerLogger.LogDebug((object)"ReadConfigValues called");
				((BaseUnityPlugin)this).Config.Reload();
			}
			catch
			{
				PerformanceTrackerLogger.LogError((object)("There was an issue loading your " + ConfigFileName));
				PerformanceTrackerLogger.LogError((object)"Please check your config entries for spelling and format!");
			}
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description)
		{
			return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
		}

		static PerformanceTrackerPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConnectionError = "";
			pluginGO = null;
			PerformanceTrackerLogger = Logger.CreateLogSource("PerformanceTracker");
			_serverConfigLocked = null;
		}
	}
}
namespace PerformanceTracker.Util
{
	internal static class MemoryInfo
	{
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		public class MEMORYSTATUSEX
		{
			public uint dwLength;

			public uint dwMemoryLoad;

			public ulong ullTotalPhys;

			public ulong ullAvailPhys;

			public ulong ullTotalPageFile;

			public ulong ullAvailPageFile;

			public ulong ullTotalVirtual;

			public ulong ullAvailVirtual;

			public ulong ullAvailExtendedVirtual;

			public MEMORYSTATUSEX()
			{
				dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
			}
		}

		[StructLayout(LayoutKind.Sequential, Size = 72)]
		public class PROCESS_MEMORY_COUNTERS
		{
			public uint cb;

			public uint PageFaultCount;

			public ulong PeakWorkingSetSize;

			public ulong WorkingSetSize;

			public ulong QuotaPeakPagedPoolUsage;

			public ulong QuotaPagedPoolUsage;

			public ulong QuotaPeakNonPagedPoolUsage;

			public ulong QuotaNonPagedPoolUsage;

			public ulong PagefileUsage;

			public ulong PeakPagefileUsage;

			public PROCESS_MEMORY_COUNTERS()
			{
				cb = (uint)Marshal.SizeOf(typeof(PROCESS_MEMORY_COUNTERS));
			}
		}

		private static readonly IntPtr _currentProcessHandle = GetCurrentProcess();

		private static readonly MEMORYSTATUSEX _memorystatusex = new MEMORYSTATUSEX();

		private static readonly PROCESS_MEMORY_COUNTERS _memoryCounters = new PROCESS_MEMORY_COUNTERS();

		public static MEMORYSTATUSEX QuerySystemMemStatus()
		{
			if (GlobalMemoryStatusEx(_memorystatusex))
			{
				return _memorystatusex;
			}
			throw new Exception("GlobalMemoryStatusEx returned false. Error Code is " + Marshal.GetLastWin32Error());
		}

		public static PROCESS_MEMORY_COUNTERS QueryProcessMemStatus()
		{
			if (GetProcessMemoryInfo(_currentProcessHandle, _memoryCounters, _memoryCounters.cb))
			{
				return _memoryCounters;
			}
			throw new Exception("GetProcessMemoryInfo returned false. Error Code is " + Marshal.GetLastWin32Error());
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool GlobalMemoryStatusEx([In][Out] MEMORYSTATUSEX lpBuffer);

		[DllImport("kernel32.dll")]
		private static extern IntPtr GetCurrentProcess();

		[DllImport("psapi.dll", SetLastError = true)]
		private static extern bool GetProcessMemoryInfo(IntPtr hProcess, [In][Out] PROCESS_MEMORY_COUNTERS counters, uint size);
	}
	internal class MovingAverage
	{
		private readonly int _windowSize;

		private readonly Queue<long> _samples;

		private long _sampleAccumulator;

		public MovingAverage(int windowSize = 11)
		{
			_windowSize = windowSize;
			_samples = new Queue<long>(_windowSize + 1);
		}

		public long GetAverage()
		{
			return _sampleAccumulator / _samples.Count;
		}

		public float GetAverageFloat()
		{
			return _sampleAccumulator / _samples.Count;
		}

		public void Sample(long newSample)
		{
			_sampleAccumulator += newSample;
			_samples.Enqueue(newSample);
			if (_samples.Count > _windowSize)
			{
				_sampleAccumulator -= _samples.Dequeue();
			}
		}
	}
	internal static class PluginCounter
	{
		private static readonly Dictionary<BepInPlugin, KeyValuePair<MovingAverage, List<Stopwatch>>> _averages = new Dictionary<BepInPlugin, KeyValuePair<MovingAverage, List<Stopwatch>>>();

		private static readonly Dictionary<Type, KeyValuePair<BepInPlugin, Stopwatch>> _timers = new Dictionary<Type, KeyValuePair<BepInPlugin, Stopwatch>>();

		private static List<KeyValuePair<string, long>> _sortedList;

		private static Harmony _harmonyInstance;

		private static bool _running;

		private static Action _stopAction;

		private static readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();

		public static List<KeyValuePair<string, long>> SlowPlugins => _sortedList;

		public static void Start(MonoBehaviour mb, BaseUnityPlugin thisPlugin)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_01e9: Expected O, but got Unknown
			BaseUnityPlugin thisPlugin2 = thisPlugin;
			MonoBehaviour mb2 = mb;
			if (_running)
			{
				return;
			}
			_running = true;
			if (_harmonyInstance == null)
			{
				_harmonyInstance = new Harmony("Azumatt.PerformanceTracker");
			}
			int num = 0;
			Type baseType = typeof(MonoBehaviour);
			string[] array = new string[4] { "FixedUpdate", "Update", "LateUpdate", "OnGUI" };
			foreach (BaseUnityPlugin item in Chainloader.Plugins.Where((BaseUnityPlugin x) => (Object)(object)x != (Object)null && (Object)(object)x != (Object)(object)thisPlugin2))
			{
				Stopwatch stopwatch = new Stopwatch();
				foreach (Type item2 in (from x in SafeGetTypes(((object)item).GetType().Assembly)
					where baseType.IsAssignableFrom(x) && !x.IsAbstract
					select x).ToList())
				{
					string[] array2 = array;
					foreach (string name in array2)
					{
						MethodInfo method = item2.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (method == null)
						{
							continue;
						}
						if (!_timers.ContainsKey(item2))
						{
							BepInPlugin metadata = item.Info.Metadata;
							_timers[item2] = new KeyValuePair<BepInPlugin, Stopwatch>(metadata, stopwatch);
							if (!_averages.TryGetValue(metadata, out KeyValuePair<MovingAverage, List<Stopwatch>> value))
							{
								value = new KeyValuePair<MovingAverage, List<Stopwatch>>(new MovingAverage(60), new List<Stopwatch>());
								_averages.Add(metadata, value);
							}
							value.Value.Add(stopwatch);
						}
						try
						{
							_harmonyInstance.Patch((MethodBase)method, new HarmonyMethod(AccessTools.Method(typeof(PluginCounter), "Pre", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.Method(typeof(PluginCounter), "Post", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							num++;
						}
						catch (Exception ex)
						{
							PerformanceTrackerPlugin.PerformanceTrackerLogger.LogError((object)ex);
						}
					}
				}
			}
			_sortedList = new List<KeyValuePair<string, long>>(_averages.Count);
			Coroutine co = mb2.StartCoroutine(CollectLoop());
			_stopAction = delegate
			{
				mb2.StopCoroutine(co);
			};
			PerformanceTrackerPlugin.PerformanceTrackerLogger.LogDebug((object)$"Attached timers to {num} unity methods in {Chainloader.Plugins.Count} plugins");
		}

		public static void Stop()
		{
			if (!_running)
			{
				return;
			}
			_harmonyInstance.UnpatchSelf();
			_running = false;
			foreach (KeyValuePair<Type, KeyValuePair<BepInPlugin, Stopwatch>> timer in _timers)
			{
				timer.Value.Value.Reset();
			}
			_timers.Clear();
			_averages.Clear();
			_stopAction();
			_sortedList = null;
		}

		private static IEnumerator CollectLoop()
		{
			long num = 100000000 / Stopwatch.Frequency;
			_ = 1f / ((float)num * 1000f);
			long cutoffTicks = num * 100;
			while (true)
			{
				yield return _waitForEndOfFrame;
				_sortedList.Clear();
				foreach (KeyValuePair<BepInPlugin, KeyValuePair<MovingAverage, List<Stopwatch>>> average2 in _averages)
				{
					long num2 = 0L;
					int count = average2.Value.Value.Count;
					for (int i = 0; i < count; i++)
					{
						num2 += average2.Value.Value[i].ElapsedTicks;
					}
					MovingAverage key = average2.Value.Key;
					key.Sample(num2);
					long average = key.GetAverage();
					if (average > cutoffTicks)
					{
						_sortedList.Add(new KeyValuePair<string, long>(average2.Key.Name, average));
					}
				}
				foreach (KeyValuePair<Type, KeyValuePair<BepInPlugin, Stopwatch>> timer in _timers)
				{
					timer.Value.Value.Reset();
				}
			}
		}

		private static void Post(MonoBehaviour __instance, MethodInfo __originalMethod)
		{
			if ((FrameCounterHelper.CanProcessOnGui || !(__originalMethod.Name == "OnGUI")) && _timers.TryGetValue(((object)__instance).GetType(), out KeyValuePair<BepInPlugin, Stopwatch> value))
			{
				value.Value.Stop();
			}
		}

		private static void Pre(MonoBehaviour __instance, MethodInfo __originalMethod)
		{
			if ((FrameCounterHelper.CanProcessOnGui || !(__originalMethod.Name == "OnGUI")) && _timers.TryGetValue(((object)__instance).GetType(), out KeyValuePair<BepInPlugin, Stopwatch> value))
			{
				value.Value.Start();
			}
		}

		private static IEnumerable<Type> SafeGetTypes(Assembly ass)
		{
			try
			{
				return ass.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type x) => x != null);
			}
		}
	}
	internal static class ShadowAndOutline
	{
		public static void DrawOutline(Rect rect, string text, GUIStyle style, Color outColor, Color inColor, float size)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			float num = size * 0.5f;
			Color color = GUI.color;
			style.normal.textColor = outColor;
			GUI.color = outColor;
			((Rect)(ref rect)).x = ((Rect)(ref rect)).x - num;
			GUI.Label(rect, text, style);
			((Rect)(ref rect)).x = ((Rect)(ref rect)).x + size;
			GUI.Label(rect, text, style);
			((Rect)(ref rect)).x = ((Rect)(ref rect)).x - num;
			((Rect)(ref rect)).y = ((Rect)(ref rect)).y - num;
			GUI.Label(rect, text, style);
			((Rect)(ref rect)).y = ((Rect)(ref rect)).y + size;
			GUI.Label(rect, text, style);
			((Rect)(ref rect)).y = ((Rect)(ref rect)).y - num;
			style.normal.textColor = inColor;
			GUI.color = color;
			GUI.Label(rect, text, style);
		}

		public static void DrawShadow(Rect rect, GUIContent content, GUIStyle style, Color txtColor, Color shadowColor, Vector2 direction)
		{
			//IL_0006: 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_002a: 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_0045: 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_0068: 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)
			style.normal.textColor = shadowColor;
			((Rect)(ref rect)).x = ((Rect)(ref rect)).x + direction.x;
			((Rect)(ref rect)).y = ((Rect)(ref rect)).y + direction.y;
			GUI.Label(rect, content, style);
			style.normal.textColor = txtColor;
			((Rect)(ref rect)).x = ((Rect)(ref rect)).x - direction.x;
			((Rect)(ref rect)).y = ((Rect)(ref rect)).y - direction.y;
			GUI.Label(rect, content, style);
		}

		public static void DrawLayoutShadow(GUIContent content, GUIStyle style, Color txtColor, Color shadowColor, Vector2 direction, params GUILayoutOption[] options)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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)
			DrawShadow(GUILayoutUtility.GetRect(content, style, options), content, style, txtColor, shadowColor, direction);
		}

		public static bool DrawButtonWithShadow(Rect r, GUIContent content, GUIStyle style, float shadowAlpha, Vector2 direction)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_002b: 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_0059: 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_005e: 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_0062: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			GUIStyle val = new GUIStyle(style);
			val.normal.background = null;
			val.hover.background = null;
			val.active.background = null;
			bool result = GUI.Button(r, content, style);
			DrawShadow(txtColor: ((Rect)(ref r)).Contains(Event.current.mousePosition) ? val.hover.textColor : val.normal.textColor, rect: r, content: content, style: val, shadowColor: new Color(0f, 0f, 0f, shadowAlpha), direction: direction);
			return result;
		}

		public static bool DrawLayoutButtonWithShadow(GUIContent content, GUIStyle style, float shadowAlpha, Vector2 direction, params GUILayoutOption[] options)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return DrawButtonWithShadow(GUILayoutUtility.GetRect(content, style, options), content, style, shadowAlpha, direction);
		}
	}
}
namespace PerformanceTracker.Util.Helpers
{
	[DefaultExecutionOrder(int.MinValue)]
	internal sealed class FrameCounterHelper : MonoBehaviour
	{
		[DefaultExecutionOrder(int.MaxValue)]
		internal sealed class FrameCounterHelper2 : MonoBehaviour
		{
			private void LateUpdate()
			{
				_lateUpdateTime.Sample(TakeMeasurement());
				_onGuiHit = false;
				CanProcessOnGui = true;
			}
		}

		private static readonly MovingAverage _fixedUpdateTime = new MovingAverage();

		private static readonly MovingAverage _updateTime = new MovingAverage();

		private static readonly MovingAverage _yieldTime = new MovingAverage();

		private static readonly MovingAverage _lateUpdateTime = new MovingAverage();

		private static readonly MovingAverage _renderTime = new MovingAverage();

		private static readonly MovingAverage _onGuiTime = new MovingAverage();

		private static readonly MovingAverage _gcAddedSize = new MovingAverage(60);

		private static readonly MovingAverage _frameTime = new MovingAverage();

		private static Stopwatch _measurementStopwatch;

		internal static bool CanProcessOnGui;

		private static bool _onGuiHit;

		private static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();

		private static readonly KVPluginDataComparer Comparer = new KVPluginDataComparer();

		private static long TakeMeasurement()
		{
			long elapsedTicks = _measurementStopwatch.ElapsedTicks;
			_measurementStopwatch.Reset();
			_measurementStopwatch.Start();
			return elapsedTicks;
		}

		private IEnumerator Start()
		{
			_measurementStopwatch = new Stopwatch();
			Stopwatch totalStopwatch = new Stopwatch();
			float nanosecPerTick = 100000000f / (float)Stopwatch.Frequency;
			float msScale = 1f / (nanosecPerTick * 1000f);
			long gcPreviousAmount = 0L;
			while (true)
			{
				yield return null;
				_updateTime.Sample(TakeMeasurement());
				yield return WaitForEndOfFrame;
				if (!_onGuiHit)
				{
					_renderTime.Sample(TakeMeasurement());
					_onGuiHit = true;
				}
				CanProcessOnGui = false;
				_onGuiTime.Sample(TakeMeasurement());
				_measurementStopwatch.Reset();
				_frameTime.Sample(totalStopwatch.ElapsedTicks);
				totalStopwatch.Reset();
				totalStopwatch.Start();
				long average = _frameTime.GetAverage();
				float float_val = 1000000f / ((float)average / nanosecPerTick);
				UIHelper.fString.Append(float_val, 2u, 2u).Append(" FPS");
				if (PerformanceTrackerPlugin._showUnityMethodStats.Value == PerformanceTrackerPlugin.Toggle.On)
				{
					long average2 = _fixedUpdateTime.GetAverage();
					long average3 = _updateTime.GetAverage();
					long average4 = _yieldTime.GetAverage();
					long average5 = _lateUpdateTime.GetAverage();
					long average6 = _renderTime.GetAverage();
					long average7 = _onGuiTime.GetAverage();
					long num = average2 + average3 + average4 + average5 + average6 + average7;
					long num2 = average - num;
					UIHelper.fString.Append(", ").Append((float)average * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nFixed: ").Append((float)average2 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nUpdate: ").Append((float)average3 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nYield/anim: ").Append((float)average4 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nLate: ").Append((float)average5 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nRender/VSync: ").Append((float)average6 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nOnGUI: ").Append((float)average7 * msScale, 2u, 2u);
					UIHelper.fString.Append("ms\nOther: ").Append((float)num2 * msScale, 2u, 2u).Append("ms");
				}
				if (PerformanceTrackerPlugin._measureMemory != null && PerformanceTrackerPlugin._measureMemory.Value == PerformanceTrackerPlugin.Toggle.On)
				{
					ulong num3 = MemoryInfo.QueryProcessMemStatus().WorkingSetSize / 1024 / 1024;
					ulong num4 = MemoryInfo.QuerySystemMemStatus().ullAvailPhys / 1024 / 1024;
					UIHelper.fString.Append("\nRAM: ").Append((uint)num3).Append("MB used, ");
					UIHelper.fString.Append((uint)num4).Append("MB free");
					long totalMemory = GC.GetTotalMemory(forceFullCollection: false);
					if (totalMemory != 0L)
					{
						long newSample = totalMemory - gcPreviousAmount;
						long num5 = totalMemory / 1024 / 1024;
						_gcAddedSize.Sample(newSample);
						UIHelper.fString.Append("\nGC: ").Append((int)num5).Append("MB (");
						UIHelper.fString.Append(_gcAddedSize.GetAverageFloat() / 1024f, 2u, 4u).Append("KB/s)");
						gcPreviousAmount = totalMemory;
					}
					int maxGeneration = GC.MaxGeneration;
					if (maxGeneration > 0)
					{
						UIHelper.fString.Append("\nGC hits:");
						for (int i = 0; i < maxGeneration; i++)
						{
							int int_val = GC.CollectionCount(i);
							UIHelper.fString.Append(' ').Append(i).Append(':')
								.Append(int_val);
						}
					}
				}
				List<KeyValuePair<string, long>> slowPlugins = PluginCounter.SlowPlugins;
				if (slowPlugins != null)
				{
					if (slowPlugins.Count > 0)
					{
						slowPlugins.Sort(Comparer);
						int count = slowPlugins.Count;
						for (int j = 0; j < count && j < 5; j++)
						{
							KeyValuePair<string, long> keyValuePair = slowPlugins[j];
							int count2 = ((keyValuePair.Key.Length > 20) ? 20 : keyValuePair.Key.Length);
							UIHelper.fString.Append("\n[").Append(keyValuePair.Key, 0, count2).Append(": ")
								.Append((float)keyValuePair.Value * msScale, 1u, 2u)
								.Append("ms]");
						}
					}
					else
					{
						UIHelper.fString.Append("\nNo slow plugins");
					}
				}
				UIHelper._frameOutputText = UIHelper.fString.Finalize();
				_measurementStopwatch.Reset();
			}
		}

		private void FixedUpdate()
		{
			_measurementStopwatch.Start();
		}

		private void Update()
		{
			_fixedUpdateTime.Sample(TakeMeasurement());
		}

		private void LateUpdate()
		{
			_yieldTime.Sample(TakeMeasurement());
		}

		private void OnGUI()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			if (!_onGuiHit)
			{
				_renderTime.Sample(TakeMeasurement());
				_onGuiHit = true;
			}
			if ((int)Event.current.type == 7)
			{
				UIHelper.DrawCounter();
			}
		}
	}
	public class Helpers
	{
		private static readonly MonoBehaviour[] _helpers = (MonoBehaviour[])(object)new MonoBehaviour[2];

		internal static void SetCapturingEnabled(bool enableCapturing)
		{
			if (!enableCapturing)
			{
				PluginCounter.Stop();
				Object.Destroy((Object)(object)_helpers[0]);
				Object.Destroy((Object)(object)_helpers[1]);
				return;
			}
			if ((Object)(object)_helpers[0] == (Object)null)
			{
				_helpers[0] = (MonoBehaviour)(object)PerformanceTrackerPlugin.pluginGO.AddComponent<FrameCounterHelper>();
			}
			if ((Object)(object)_helpers[1] == (Object)null)
			{
				_helpers[1] = (MonoBehaviour)(object)PerformanceTrackerPlugin.pluginGO.AddComponent<FrameCounterHelper.FrameCounterHelper2>();
			}
			if (PerformanceTrackerPlugin._showPluginStats.Value == PerformanceTrackerPlugin.Toggle.On)
			{
				PluginCounter.Start(_helpers[0], (BaseUnityPlugin)(object)PerformanceTrackerPlugin.ModContext);
			}
			else
			{
				PluginCounter.Stop();
			}
		}

		internal static void OnEnable()
		{
			ConfigEntry<PerformanceTrackerPlugin.Toggle> shown = PerformanceTrackerPlugin._shown;
			if (shown != null && shown.Value == PerformanceTrackerPlugin.Toggle.On)
			{
				UIHelper.UpdateLooks();
				SetCapturingEnabled(enableCapturing: true);
			}
		}

		internal static void OnDisable()
		{
			SetCapturingEnabled(enableCapturing: false);
		}
	}
	public class UIHelper
	{
		private const int MAX_STRING_SIZE = 499;

		private static readonly GUIStyle _style = new GUIStyle();

		private static Rect _screenRect;

		private const int ScreenOffset = 10;

		internal static MutableString fString = new MutableString(499, ignoreOverflow: true);

		internal static string _frameOutputText;

		internal static void DrawCounter()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (PerformanceTrackerPlugin._counterColor.Value == PerformanceTrackerPlugin.CounterColors.Outline)
			{
				ShadowAndOutline.DrawOutline(_screenRect, _frameOutputText, _style, Color.black, Color.white, 1.5f);
			}
			else
			{
				GUI.Label(_screenRect, _frameOutputText, _style);
			}
		}

		internal static void UpdateLooks()
		{
			//IL_0016: 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_0075: 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)
			if (PerformanceTrackerPlugin._counterColor.Value == PerformanceTrackerPlugin.CounterColors.White)
			{
				_style.normal.textColor = Color.white;
			}
			if (PerformanceTrackerPlugin._counterColor.Value == PerformanceTrackerPlugin.CounterColors.Black)
			{
				_style.normal.textColor = Color.black;
			}
			int width = Screen.width;
			int height = Screen.height;
			_screenRect = new Rect(10f, 10f, (float)(width - 20), (float)(height - 20));
			_style.alignment = PerformanceTrackerPlugin._position.Value;
			_style.fontSize = height / 65;
		}
	}
}
namespace FPSCounter
{
	internal class KVPluginDataComparer : IComparer<KeyValuePair<string, long>>
	{
		public int Compare(KeyValuePair<string, long> val1, KeyValuePair<string, long> val2)
		{
			return val2.Value.CompareTo(val1.Value);
		}
	}
	public class MutableString
	{
		public enum BaseValue
		{
			Binary = 2,
			Decimal = 10,
			Hex = 16
		}

		private const uint MAX_DECIMALS = 2u;

		private const BaseValue DEFAULT_BASE = BaseValue.Decimal;

		private static readonly char[] DIGITS_LUT = new char[16]
		{
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'A', 'B', 'C', 'D', 'E', 'F'
		};

		private int m_Pos;

		private string m_valueStr;

		private char m_defaultPadChar = '0';

		private readonly bool dontThrow;

		public char DefaultPadChar
		{
			get
			{
				return m_defaultPadChar;
			}
			set
			{
				m_defaultPadChar = value;
			}
		}

		public int Capacity => m_valueStr.Length;

		public int Length
		{
			get
			{
				return m_Pos;
			}
			set
			{
				m_Pos = value;
			}
		}

		public string CurrentRaw => m_valueStr;

		public MutableString(int size)
			: this(size, '\0', ignoreOverflow: false)
		{
		}

		public MutableString(int size, bool ignoreOverflow)
			: this(size, '\0', ignoreOverflow)
		{
		}

		public MutableString(int size, char fillChar)
			: this(size, fillChar, ignoreOverflow: false)
		{
		}

		public MutableString(int size, char fillChar, bool ignoreOverflow)
		{
			if (size < 2)
			{
				throw new ArgumentException("Size cannot be 1 or less");
			}
			m_valueStr = new string(fillChar, size);
			dontThrow = ignoreOverflow;
		}

		public string Finalize()
		{
			if (m_Pos == 0)
			{
				return string.Empty;
			}
			int num = m_valueStr.Length - m_Pos;
			if (num > 0)
			{
				repeatChar('\0', num);
			}
			m_Pos = 0;
			return m_valueStr;
		}

		public MutableString Append(bool value)
		{
			Append(value.ToString());
			return this;
		}

		public MutableString Append(byte value)
		{
			Append((uint)value);
			return this;
		}

		public MutableString Append(sbyte value)
		{
			Append((int)value);
			return this;
		}

		public MutableString Append(short value)
		{
			Append((int)value);
			return this;
		}

		public MutableString Append(ushort value)
		{
			Append((int)value);
			return this;
		}

		public MutableString Append(char[] value, int indx, int count)
		{
			if (value == null)
			{
				return this;
			}
			int num = value.Length;
			if (count < 1 || indx < 0 || num < count + indx)
			{
				return this;
			}
			if (num > 1)
			{
				AppendInternal(value, indx, count);
			}
			else
			{
				Append(value[0]);
			}
			return this;
		}

		public MutableString Append(char[] value)
		{
			if (value == null)
			{
				return this;
			}
			int num = value.Length;
			if (num > 1)
			{
				AppendInternal(value, 0, num);
			}
			else
			{
				Append(value[0]);
			}
			return this;
		}

		public MutableString Append(char value)
		{
			if (m_Pos >= m_valueStr.Length)
			{
				if (dontThrow)
				{
					return this;
				}
				throw new ArgumentException("Not enough free space to accomodate element!");
			}
			singleChar(value);
			m_Pos++;
			return this;
		}

		private void AppendInternal(char[] value, int indx, int count)
		{
			int num = m_valueStr.Length - m_Pos;
			if (count > num)
			{
				if (!dontThrow)
				{
					throw new ArgumentException($"Not enough free space to accomodate {count} elements!");
				}
			}
			else
			{
				charCopy(value, indx, count);
				m_Pos += count;
			}
		}

		private void AppendInternal(string value, int indx, int count)
		{
			int num = m_valueStr.Length - m_Pos;
			if (count > num)
			{
				if (!dontThrow)
				{
					throw new ArgumentOutOfRangeException($"Not enough free space to accomodate {count} elements!");
				}
			}
			else
			{
				stringCopy(value, indx, count);
				m_Pos += count;
			}
		}

		public MutableString Append(string value, int indx, int count)
		{
			if (value == null)
			{
				return this;
			}
			int length = value.Length;
			if (count < 1 || indx < 0 || length < count + indx)
			{
				return this;
			}
			if (length > 1)
			{
				AppendInternal(value, indx, count);
			}
			else
			{
				Append(value[0]);
			}
			return this;
		}

		public MutableString Append(string value)
		{
			if (value == null)
			{
				return this;
			}
			int length = value.Length;
			if (length > 1)
			{
				AppendInternal(value, 0, length);
			}
			else
			{
				Append(value[0]);
			}
			return this;
		}

		private unsafe void AppendUINT32(uint uint_val, uint pad_base, char pad_char, bool negative)
		{
			int num = CountDigits(uint_val);
			int num2 = (int)((pad_base > num) ? (pad_base - num) : 0);
			int num3 = Convert.ToInt32(negative) + num2 + num;
			int num4 = m_Pos + num3;
			if (num4 > m_valueStr.Length)
			{
				if (!dontThrow)
				{
					throw new ArgumentOutOfRangeException($"Not enough free space to accomodate {num3} elements!");
				}
				return;
			}
			fixed (char* ptr = m_valueStr)
			{
				char* ptr2 = ptr + num4;
				do
				{
					uint num5 = uint_val / 10;
					*(--ptr2) = (char)(48 + uint_val - num5 * 10);
					uint_val = num5;
				}
				while (uint_val != 0);
				while (num2 > 0)
				{
					*(--ptr2) = pad_char;
					num2--;
				}
				if (negative)
				{
					*(--ptr2) = '-';
				}
			}
			m_Pos = num4;
		}

		public MutableString Append(uint uint_val)
		{
			AppendUINT32(uint_val, 0u, m_defaultPadChar, negative: false);
			return this;
		}

		public MutableString Append(uint uint_val, uint pad_amount)
		{
			AppendUINT32(uint_val, pad_amount, m_defaultPadChar, negative: false);
			return this;
		}

		public MutableString Append(uint uint_val, uint pad_amount, char pad_char)
		{
			AppendUINT32(uint_val, pad_amount, pad_char, negative: false);
			return this;
		}

		public MutableString Append(int int_val)
		{
			Append(int_val, 0u, m_defaultPadChar);
			return this;
		}

		public MutableString Append(int int_val, uint pad_amount)
		{
			Append(int_val, pad_amount, m_defaultPadChar);
			return this;
		}

		public MutableString Append(int int_val, uint pad_base, char pad_char)
		{
			bool isNegative;
			uint positiveEqv = GetPositiveEqv(int_val, out isNegative);
			AppendUINT32(positiveEqv, pad_base, pad_char, isNegative);
			return this;
		}

		private unsafe void AppendULONG(ulong ulong_val, uint pad_base, char pad_char, bool negative)
		{
			int num = CountDigits(ulong_val);
			int num2 = (int)((pad_base > num) ? (pad_base - num) : 0);
			int num3 = Convert.ToInt32(negative) + num + num2;
			int num4 = m_Pos + num3;
			if (num4 > m_valueStr.Length)
			{
				if (!dontThrow)
				{
					throw new ArgumentOutOfRangeException($"Not enough free space to accomodate {num3} elements!");
				}
				return;
			}
			fixed (char* ptr = m_valueStr)
			{
				char* ptr2 = ptr + num4;
				do
				{
					ulong num5 = ulong_val / 10;
					*(--ptr2) = (char)(48 + ulong_val - num5 * 10);
					ulong_val = num5;
				}
				while (ulong_val != 0L);
				while (num2 > 0)
				{
					*(--ptr2) = pad_char;
					num2--;
				}
				if (negative)
				{
					*(--ptr2) = '-';
				}
			}
			m_Pos = num4;
		}

		public MutableString Append(ulong ulong_val)
		{
			AppendULONG(ulong_val, 0u, m_defaultPadChar, negative: false);
			return this;
		}

		public MutableString Append(ulong ulong_val, uint pad_amount)
		{
			AppendULONG(ulong_val, pad_amount, m_defaultPadChar, negative: false);
			return this;
		}

		public MutableString Append(ulong ulong_val, uint pad_amount, char pad_char)
		{
			AppendULONG(ulong_val, pad_amount, pad_char, negative: false);
			return this;
		}

		public MutableString Append(long long_val)
		{
			Append(long_val, 0u, m_defaultPadChar);
			return this;
		}

		public MutableString Append(long long_val, uint pad_base)
		{
			Append(long_val, pad_base, m_defaultPadChar);
			return this;
		}

		public MutableString Append(long long_val, uint pad_base, char pad_char)
		{
			bool flag = long_val < 0;
			ulong ulong_val = (ulong)(flag ? (-1 - long_val + 1) : long_val);
			AppendULONG(ulong_val, pad_base, pad_char, flag);
			return this;
		}

		public unsafe MutableString Append(float float_val, uint decimal_places, uint pad_base, char pad_char)
		{
			decimal_places = ((decimal_places > 2) ? 2u : decimal_places);
			bool flag = float_val < 0f;
			float num = 5f / (float)Math.Pow(10.0, 1 + decimal_places);
			float num2 = (flag ? (float_val + (0f - num)) : (float_val + num));
			if (!IsFinite(float_val))
			{
				if (float_val != float_val)
				{
					AppendInternal("NaN", 0, 3);
				}
				else
				{
					AppendInternal(flag ? "-∞" : "+∞", 0, 2);
				}
				return this;
			}
			bool isNegative;
			uint positiveEqv = GetPositiveEqv((int)num2, out isNegative);
			if (decimal_places == 0)
			{
				AppendUINT32(positiveEqv, pad_base, pad_char, flag);
				return this;
			}
			int num3 = CountDigits(positiveEqv);
			int num4 = num3 + 1 + (int)decimal_places;
			int num5 = (int)((pad_base > num3) ? (pad_base - num3) : 0);
			int num6 = Convert.ToInt32(flag) + num5 + num4;
			int num7 = m_Pos + num6;
			if (num7 > m_valueStr.Length)
			{
				if (dontThrow)
				{
					return this;
				}
				throw new ArgumentOutOfRangeException($"Not enough free space to accomodate {num6} elements!");
			}
			fixed (char* ptr = m_valueStr)
			{
				char* ptr2 = ptr + num7;
				uint num8 = (uint)(num2 * (float)Math.Pow(10.0, decimal_places));
				do
				{
					uint num9 = num8 / 10;
					*(--ptr2) = (char)(48 + num8 - num9 * 10);
					num8 = num9;
					decimal_places--;
				}
				while (decimal_places != 0);
				*(--ptr2) = '.';
				do
				{
					uint num10 = num8 / 10;
					*(--ptr2) = (char)(48 + num8 - num10 * 10);
					num8 = num10;
					num3--;
				}
				while (num3 != 0);
				while (num5 > 0)
				{
					*(--ptr2) = pad_char;
					num5--;
				}
				if (flag)
				{
					*(--ptr2) = '-';
				}
			}
			m_Pos = num7;
			return this;
		}

		public MutableString Append(float float_val)
		{
			return Append(float_val, 2u, 0u, m_defaultPadChar);
		}

		public MutableString Append(float float_val, uint decimal_places)
		{
			return Append(float_val, decimal_places, 0u, m_defaultPadChar);
		}

		public MutableString Append(float float_val, uint decimal_places, uint pad_amount)
		{
			return Append(float_val, decimal_places, pad_amount, m_defaultPadChar);
		}

		private static uint GetPositiveEqv(int val, out bool isNegative)
		{
			isNegative = val < 0;
			if (!isNegative)
			{
				return (uint)val;
			}
			return (uint)(-1 - val + 1);
		}

		private static int CountDigits(ulong value)
		{
			int num = 1;
			uint num2;
			if (value >= 10000000)
			{
				if (value >= 100000000000000L)
				{
					num2 = (uint)(value / 100000000000000L);
					num += 14;
				}
				else
				{
					num2 = (uint)(value / 10000000);
					num += 7;
				}
			}
			else
			{
				num2 = (uint)value;
			}
			if (num2 >= 10)
			{
				num = ((num2 < 100) ? (num + 1) : ((num2 < 1000) ? (num + 2) : ((num2 < 10000) ? (num + 3) : ((num2 < 100000) ? (num + 4) : ((num2 >= 1000000) ? (num + 6) : (num + 5))))));
			}
			return num;
		}

		private static int CountDigits(uint value)
		{
			int num = 1;
			if (value >= 100000)
			{
				value /= 100000;
				num += 5;
			}
			if (value >= 10)
			{
				num = ((value < 100) ? (num + 1) : ((value < 1000) ? (num + 2) : ((value >= 10000) ? (num + 4) : (num + 3))));
			}
			return num;
		}

		private static int CountDecimalTrailingZeros(uint value, out uint valueWithoutTrailingZeros)
		{
			int num = 0;
			if (value != 0)
			{
				while (true)
				{
					uint num2 = value / 10;
					if (value != num2 * 10)
					{
						break;
					}
					value = num2;
					num++;
				}
			}
			valueWithoutTrailingZeros = value;
			return num;
		}

		private static uint Low32(ulong value)
		{
			return (uint)value;
		}

		private static uint High32(ulong value)
		{
			return (uint)((value & 0xFFFFFFFF00000000uL) >> 32);
		}

		private unsafe static int SingleToInt32Bits(float value)
		{
			return *(int*)(&value);
		}

		private static bool IsFinite(float f)
		{
			return (SingleToInt32Bits(f) & 0x7FFFFFFF) < 2139095040;
		}

		private unsafe void stringCopy(string value, int indx, int charCount)
		{
			fixed (char* ptr = m_valueStr)
			{
				fixed (char* ptr2 = value)
				{
					wstrCpy(ptr + m_Pos, ptr2 + indx, charCount);
				}
			}
		}

		private unsafe void charCopy(char[] value, int indx, int charCount)
		{
			fixed (char* ptr = m_valueStr)
			{
				fixed (char* ptr2 = value)
				{
					wstrCpy(ptr + m_Pos, ptr2 + indx, charCount);
				}
			}
		}

		private unsafe void singleChar(char value)
		{
			fixed (char* ptr = m_valueStr)
			{
				ptr[m_Pos] = value;
			}
		}

		private unsafe void repeatChar(char value, int count)
		{
			int num = m_Pos + count;
			fixed (char* ptr = m_valueStr)
			{
				for (int i = m_Pos; i < num; i++)
				{
					ptr[i] = value;
				}
			}
			m_Pos = num;
		}

		private unsafe void rawCopy(char* dest, char* src, int charCount)
		{
			for (int i = 0; i < charCount; i++)
			{
				dest[i] = src[i];
			}
		}

		private unsafe static void wstrCpy(char* dmem, char* smem, int charCount)
		{
			if (((uint)(int)dmem & 2u) != 0)
			{
				*dmem = *smem;
				dmem++;
				smem++;
				charCount--;
			}
			while (charCount >= 8)
			{
				*(int*)dmem = *(int*)smem;
				*(int*)(dmem + 2) = *(int*)(smem + 2);
				*(int*)(dmem + 4) = *(int*)(smem + 4);
				*(int*)(dmem + 6) = *(int*)(smem + 6);
				dmem += 8;
				smem += 8;
				charCount -= 8;
			}
			if (((uint)charCount & 4u) != 0)
			{
				*(int*)dmem = *(int*)smem;
				*(int*)(dmem + 2) = *(int*)(smem + 2);
				dmem += 4;
				smem += 4;
			}
			if (((uint)charCount & 2u) != 0)
			{
				*(int*)dmem = *(int*)smem;
				dmem += 2;
				smem += 2;
			}
			if (((uint)charCount & (true ? 1u : 0u)) != 0)
			{
				*dmem = *smem;
			}
		}
	}
}