Decompiled source of DualSenseHaptics v0.2.3

plugins/DualSenseHaptics/DualSenseHaptics.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DualSenseHaptics.Core;
using DualSenseHaptics.Core.Immersion;
using DualSenseHaptics.Core.Imu;
using DualSenseHaptics.Core.ThirdParty;
using DualSenseHaptics.Device;
using DualSenseHaptics.Gyro;
using DualSenseHaptics.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using Lc.Common;
using LethalCompanyInputUtils.Api;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.DualShock;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.InputSystem.XInput;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("LethalCompanyInputUtils")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("thatumi")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: AssemblyInformationalVersion("0.2.3+efbf0bfb99543d065375f3e2e0c510d9f50420f8")]
[assembly: AssemblyProduct("DualSenseHaptics")]
[assembly: AssemblyTitle("DualSenseHaptics")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Lc.Common
{
	internal static class FontProvider
	{
		private static TMP_FontAsset? _hudFont;

		internal static TMP_FontAsset? Get()
		{
			if ((Object)(object)_hudFont != (Object)null)
			{
				return _hudFont;
			}
			TMP_FontAsset val = ReflectionGuard.Run("FontProvider.HUD", delegate
			{
				HUDManager instance = HUDManager.Instance;
				return (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.chatText != (Object)null)) ? null : ((TMP_Text)instance.chatText).font;
			}, null);
			if ((Object)(object)val != (Object)null)
			{
				_hudFont = val;
				return val;
			}
			TMP_FontAsset val2 = ReflectionGuard.Run("FontProvider.Default", () => TMP_Settings.defaultFontAsset, null);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			TMP_FontAsset? obj = ReflectionGuard.Run("FontProvider.Any", () => Resources.FindObjectsOfTypeAll<TMP_FontAsset>().FirstOrDefault(), null);
			if ((Object)(object)obj == (Object)null)
			{
				Log.WarnOnce("FontProvider.none", "FontProvider: no TMP font available yet; text will use TMP defaults");
			}
			return obj;
		}

		internal static void Invalidate()
		{
			_hudFont = null;
		}
	}
	internal enum LogKind
	{
		Debug,
		Info,
		Warning,
		Error
	}
	internal static class Log
	{
		private static readonly object Gate = new object();

		private static readonly HashSet<string> OnceKeys = new HashSet<string>(StringComparer.Ordinal);

		private static Action<LogKind, string>? _sink;

		internal static bool DebugEnabled { get; set; }

		internal static void SetSink(Action<LogKind, string>? sink)
		{
			_sink = sink;
		}

		internal static void Debug(string message)
		{
			if (DebugEnabled)
			{
				Write(LogKind.Debug, message);
			}
		}

		internal static void Info(string message)
		{
			Write(LogKind.Info, message);
		}

		internal static void Warn(string message)
		{
			Write(LogKind.Warning, message);
		}

		internal static void Error(string message)
		{
			Write(LogKind.Error, message);
		}

		internal static bool WarnOnce(string key, string message)
		{
			if (!MarkOnce(key))
			{
				return false;
			}
			Write(LogKind.Warning, message);
			return true;
		}

		internal static bool ErrorOnce(string key, string message)
		{
			if (!MarkOnce(key))
			{
				return false;
			}
			Write(LogKind.Error, message);
			return true;
		}

		internal static bool MarkOnce(string key)
		{
			lock (Gate)
			{
				return OnceKeys.Add(key);
			}
		}

		internal static void ResetOnce()
		{
			lock (Gate)
			{
				OnceKeys.Clear();
			}
		}

		private static void Write(LogKind kind, string message)
		{
			Action<LogKind, string> sink = _sink;
			try
			{
				if (sink != null)
				{
					sink(kind, message);
				}
				else
				{
					Console.WriteLine("[" + kind.ToString() + "] " + message);
				}
			}
			catch
			{
			}
		}
	}
	internal static class LogBepInEx
	{
		internal static void Init(ManualLogSource source)
		{
			Log.SetSink(delegate(LogKind kind, string message)
			{
				switch (kind)
				{
				case LogKind.Debug:
					source.LogDebug((object)message);
					break;
				case LogKind.Info:
					source.LogInfo((object)message);
					break;
				case LogKind.Warning:
					source.LogWarning((object)message);
					break;
				default:
					source.LogError((object)message);
					break;
				}
			});
		}
	}
	internal static class ReflectionGuard
	{
		internal const BindingFlags AllMembers = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly object Gate = new object();

		private static readonly Dictionary<string, string> DisabledReasons = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, int> FailureCounts = new Dictionary<string, int>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Type?> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal);

		internal static int MaxFailuresBeforeDisable { get; set; } = 25;

		internal static IReadOnlyDictionary<string, string> DisabledFeatures
		{
			get
			{
				lock (Gate)
				{
					return new Dictionary<string, string>(DisabledReasons, StringComparer.Ordinal);
				}
			}
		}

		internal static event Action<string, string>? FeatureDisabled;

		internal static bool IsEnabled(string feature)
		{
			lock (Gate)
			{
				return !DisabledReasons.ContainsKey(feature);
			}
		}

		internal static void Disable(string feature, string reason)
		{
			lock (Gate)
			{
				if (DisabledReasons.ContainsKey(feature))
				{
					return;
				}
				DisabledReasons[feature] = reason;
			}
			Log.Warn("[" + feature + "] disabled: " + reason);
			try
			{
				ReflectionGuard.FeatureDisabled?.Invoke(feature, reason);
			}
			catch
			{
			}
		}

		internal static void Reset()
		{
			lock (Gate)
			{
				DisabledReasons.Clear();
				FailureCounts.Clear();
				TypeCache.Clear();
			}
		}

		internal static Type? FindType(string fullName, string feature, string? assemblyName = null)
		{
			string key = (assemblyName ?? "*") + "|" + fullName;
			lock (Gate)
			{
				if (TypeCache.TryGetValue(key, out Type value) && value != null)
				{
					return value;
				}
			}
			Type type = null;
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if (assemblyName == null || string.Equals(assembly.GetName().Name, assemblyName, StringComparison.Ordinal))
					{
						type = assembly.GetType(fullName, throwOnError: false);
						if (type != null)
						{
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Disable(feature, "type lookup " + fullName + " threw " + ex.GetType().Name + ": " + ex.Message);
				return null;
			}
			if (type != null)
			{
				lock (Gate)
				{
					TypeCache[key] = type;
				}
			}
			else
			{
				Disable(feature, "type " + fullName + ((assemblyName != null) ? (" (in " + assemblyName + ")") : "") + " not found");
			}
			return type;
		}

		internal static FieldInfo? Field(Type? type, string name, string feature)
		{
			if (type == null)
			{
				Disable(feature, "field " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "field " + type.FullName + "." + name + " not found");
			return null;
		}

		internal static MethodInfo? Method(Type? type, string name, Type[]? parameterTypes, string feature)
		{
			if (type == null)
			{
				Disable(feature, "method " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				List<MethodInfo> list = (from m in type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
					where m.Name == name
					select m).ToList();
				if (list.Count != 0)
				{
					if (parameterTypes == null)
					{
						if (list.Count == 1)
						{
							return list[0];
						}
						Disable(feature, "method " + type2.FullName + "." + name + " is ambiguous (" + list.Count + " overloads); pass parameter types");
						return null;
					}
					foreach (MethodInfo item in list)
					{
						if (ParametersMatch(item.GetParameters(), parameterTypes))
						{
							return item;
						}
					}
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "method " + type.FullName + "." + name + Signature(parameterTypes) + " not found");
			return null;
		}

		internal static PropertyInfo? Property(Type? type, string name, string feature)
		{
			if (type == null)
			{
				Disable(feature, "property " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property;
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "property " + type.FullName + "." + name + " not found");
			return null;
		}

		internal static bool TryGet<T>(FieldInfo? field, object? instance, out T value, string feature)
		{
			value = default(T);
			if (field == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				object value2 = field.GetValue(field.IsStatic ? null : instance);
				if (value2 is T val)
				{
					value = val;
					return true;
				}
				if (value2 == null && default(T) == null)
				{
					return true;
				}
				Log.WarnOnce(feature + "|type|" + field.Name, "[" + feature + "] field " + field.Name + " is " + (value2?.GetType().FullName ?? "null") + ", expected " + typeof(T).FullName);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "get " + field.Name, e);
				return false;
			}
		}

		internal static bool TryGet<T>(PropertyInfo? property, object? instance, out T value, string feature)
		{
			value = default(T);
			MethodInfo methodInfo = property?.GetGetMethod(nonPublic: true);
			if (property == null || methodInfo == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				object value2 = property.GetValue(methodInfo.IsStatic ? null : instance, null);
				if (value2 is T val)
				{
					value = val;
					return true;
				}
				if (value2 == null && default(T) == null)
				{
					return true;
				}
				Log.WarnOnce(feature + "|type|" + property.Name, "[" + feature + "] property " + property.Name + " is " + (value2?.GetType().FullName ?? "null") + ", expected " + typeof(T).FullName);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "get " + property.Name, e);
				return false;
			}
		}

		internal static bool TrySet(FieldInfo? field, object? instance, object? value, string feature)
		{
			if (field == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				field.SetValue(field.IsStatic ? null : instance, value);
				return true;
			}
			catch (Exception e)
			{
				CountFailure(feature, "set " + field.Name, e);
				return false;
			}
		}

		internal static bool TryInvoke(MethodInfo? method, object? instance, object?[]? args, out object? result, string feature)
		{
			result = null;
			if (method == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				result = method.Invoke(method.IsStatic ? null : instance, args);
				return true;
			}
			catch (TargetInvocationException ex) when (ex.InnerException != null)
			{
				CountFailure(feature, "invoke " + method.Name, ex.InnerException);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "invoke " + method.Name, e);
				return false;
			}
		}

		internal static void Run(string key, Action body)
		{
			if (!IsEnabled(key))
			{
				return;
			}
			try
			{
				body();
			}
			catch (Exception e)
			{
				CountFailure(key, "threw", e);
			}
		}

		internal static T Run<T>(string key, Func<T> body, T fallback)
		{
			if (!IsEnabled(key))
			{
				return fallback;
			}
			try
			{
				return body();
			}
			catch (Exception e)
			{
				CountFailure(key, "threw", e);
				return fallback;
			}
		}

		internal static bool Prefix(string key, Func<bool> body)
		{
			return Run(key, body, fallback: true);
		}

		internal static int FailureCount(string key)
		{
			lock (Gate)
			{
				int value;
				return FailureCounts.TryGetValue(key, out value) ? value : 0;
			}
		}

		private static void CountFailure(string key, string what, Exception e)
		{
			int value;
			lock (Gate)
			{
				FailureCounts.TryGetValue(key, out value);
				value++;
				FailureCounts[key] = value;
			}
			if (value == 1)
			{
				Log.Error("[" + key + "] " + what + ": " + e);
			}
			if (value >= MaxFailuresBeforeDisable)
			{
				Disable(key, value + " exceptions (first logged above)");
			}
		}

		private static bool ParametersMatch(ParameterInfo[] actual, Type[] expected)
		{
			if (actual.Length != expected.Length)
			{
				return false;
			}
			for (int i = 0; i < actual.Length; i++)
			{
				if (actual[i].ParameterType != expected[i])
				{
					return false;
				}
			}
			return true;
		}

		private static string Signature(Type[]? parameterTypes)
		{
			if (parameterTypes != null)
			{
				return "(" + string.Join(", ", parameterTypes.Select((Type t) => t.Name)) + ")";
			}
			return "";
		}
	}
	internal static class TexGen
	{
		private delegate float CoverageFn(float x, float y);

		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		internal static Sprite RoundedRect(int radius = 12, int size = 0)
		{
			if (size <= 0)
			{
				size = radius * 2 + 4;
			}
			string key = "rr|" + radius + "|" + size;
			return Get(key, delegate
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0093: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(size, size, key);
				Paint(obj, (float x, float y) => TexGenMath.FillCoverage(TexGenMath.RoundedRectDistance(x, y, size, size, radius)));
				return Sprite.Create(obj, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)(radius + 1), (float)(radius + 1), (float)(radius + 1), (float)(radius + 1)));
			});
		}

		internal static Sprite Outline(int radius = 12, int thickness = 2, int size = 0)
		{
			if (size <= 0)
			{
				size = radius * 2 + 4;
			}
			string key = "ol|" + radius + "|" + thickness + "|" + size;
			return Get(key, delegate
			{
				//IL_0068: 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_008b: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(size, size, key);
				Paint(obj, (float x, float y) => TexGenMath.StrokeCoverage(TexGenMath.RoundedRectDistance(x, y, size, size, radius), thickness));
				int num = Mathf.Max(radius, thickness) + 1;
				return Sprite.Create(obj, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)num, (float)num, (float)num, (float)num));
			});
		}

		internal static Sprite Circle(int diameter = 64, int thickness = 0)
		{
			string key = "ci|" + diameter + "|" + thickness;
			return Get(key, delegate
			{
				//IL_0061: 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)
				Texture2D obj = NewTexture(diameter, diameter, key);
				float r = (float)diameter * 0.5f;
				Paint(obj, delegate(float x, float y)
				{
					float distance = TexGenMath.CircleDistance(x, y, r, r, r - 0.5f);
					return (thickness <= 0) ? TexGenMath.FillCoverage(distance) : TexGenMath.StrokeCoverage(distance, thickness);
				});
				return Sprite.Create(obj, new Rect(0f, 0f, (float)diameter, (float)diameter), new Vector2(0.5f, 0.5f), 100f);
			});
		}

		internal static Sprite White()
		{
			return Get("white", delegate
			{
				//IL_000f: 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_0044: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(1, 1, "white");
				obj.SetPixel(0, 0, Color.white);
				obj.Apply(false, false);
				return Sprite.Create(obj, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 100f);
			});
		}

		private static Sprite Get(string key, Func<Sprite> create)
		{
			if (Cache.TryGetValue(key, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = create();
			((Object)value).name = "LcTexGen_" + key;
			((Object)value).hideFlags = (HideFlags)52;
			Cache[key] = value;
			return value;
		}

		private static Texture2D NewTexture(int w, int h, string key)
		{
			//IL_0004: 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_001a: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			return new Texture2D(w, h, (TextureFormat)4, false)
			{
				name = "LcTexGen_" + key,
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)52
			};
		}

		private static void Paint(Texture2D tex, CoverageFn coverage)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			int width = ((Texture)tex).width;
			int height = ((Texture)tex).height;
			Color32[] array = (Color32[])(object)new Color32[width * height];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = coverage((float)j + 0.5f, (float)i + 0.5f);
					array[i * width + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(num * 255f));
				}
			}
			tex.SetPixels32(array);
			tex.Apply(false, false);
		}
	}
	internal static class TexGenMath
	{
		internal static float RoundedRectDistance(float px, float py, float w, float h, float r)
		{
			r = Math.Max(0f, Math.Min(r, Math.Min(w, h) * 0.5f));
			float num = w * 0.5f;
			float num2 = h * 0.5f;
			float val = Math.Abs(px - num) - (num - r);
			float num3 = Math.Abs(py - num2) - (num2 - r);
			float num4 = Math.Max(val, 0f);
			float num5 = Math.Max(num3, 0f);
			float num6 = (float)Math.Sqrt(num4 * num4 + num5 * num5);
			float num7 = Math.Min(Math.Max(val, num3), 0f);
			return num6 + num7 - r;
		}

		internal static float CircleDistance(float px, float py, float cx, float cy, float r)
		{
			float num = px - cx;
			float num2 = py - cy;
			return (float)Math.Sqrt(num * num + num2 * num2) - r;
		}

		internal static float FillCoverage(float distance)
		{
			return Clamp01(0.5f - distance);
		}

		internal static float StrokeCoverage(float distance, float thickness)
		{
			float num = FillCoverage(distance);
			float num2 = FillCoverage(distance + thickness);
			return Clamp01(num - num2);
		}

		internal static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	internal static class UiFactory
	{
		internal static class Theme
		{
			internal static Color PanelBg = new Color(0.05f, 0.05f, 0.06f, 0.92f);

			internal static Color PanelBorder = new Color(1f, 0.45f, 0.1f, 0.9f);

			internal static Color Text = new Color(0.98f, 0.62f, 0.25f, 1f);

			internal static Color TextDim = new Color(0.75f, 0.6f, 0.45f, 1f);

			internal static Color ButtonBg = new Color(0.16f, 0.09f, 0.05f, 0.95f);

			internal static Color ButtonHighlight = new Color(0.45f, 0.22f, 0.07f, 1f);

			internal static Color ButtonPressed = new Color(0.7f, 0.33f, 0.08f, 1f);

			internal static Color ButtonDisabled = new Color(0.12f, 0.12f, 0.12f, 0.7f);

			internal static Color Danger = new Color(0.9f, 0.18f, 0.15f, 1f);

			internal static Color BackdropColor = new Color(0f, 0f, 0f, 0.72f);

			internal static int CornerRadius = 10;

			internal static float FontSize = 26f;
		}

		internal static readonly Vector2 ReferenceResolution = new Vector2(1920f, 1080f);

		internal static Canvas Canvas(string name, int sortingOrder, bool dontDestroyOnLoad = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			if (dontDestroyOnLoad)
			{
				Object.DontDestroyOnLoad((Object)(object)val);
			}
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = sortingOrder;
			obj.pixelPerfect = false;
			CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = ReferenceResolution;
			obj2.screenMatchMode = (ScreenMatchMode)0;
			obj2.matchWidthOrHeight = 0.5f;
			val.AddComponent<GraphicRaycaster>();
			return obj;
		}

		internal static RectTransform Rect(Transform parent, string name)
		{
			//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_0025: 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_0033: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = 5
			}.transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		internal static RectTransform Fill(RectTransform rt, float padding = 0f)
		{
			//IL_0001: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.offsetMin = new Vector2(padding, padding);
			rt.offsetMax = new Vector2(0f - padding, 0f - padding);
			return rt;
		}

		internal static RectTransform Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size, Vector2? pivot = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = anchor;
			rt.anchorMax = anchor;
			rt.pivot = pivot ?? anchor;
			rt.anchoredPosition = offset;
			rt.sizeDelta = size;
			return rt;
		}

		internal static RectTransform PlaceTL(RectTransform rt, float cx, float cy, float w, float h)
		{
			//IL_000b: 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_0035: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = new Vector2(0f, 1f);
			rt.anchorMax = new Vector2(0f, 1f);
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.anchoredPosition = new Vector2(cx, 0f - cy);
			rt.sizeDelta = new Vector2(w, h);
			return rt;
		}

		internal static LayoutElement Size(Component c, float width = -1f, float height = -1f, float flexibleWidth = -1f)
		{
			LayoutElement val = c.GetComponent<LayoutElement>();
			if ((Object)(object)val == (Object)null)
			{
				val = c.gameObject.AddComponent<LayoutElement>();
			}
			val.preferredWidth = width;
			val.preferredHeight = height;
			val.minHeight = height;
			val.flexibleWidth = flexibleWidth;
			return val;
		}

		internal static VerticalLayoutGroup Vertical(Component c, float spacing = 8f, int padding = 12)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			VerticalLayoutGroup obj = c.gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((LayoutGroup)obj).padding = new RectOffset(padding, padding, padding, padding);
			((LayoutGroup)obj).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			return obj;
		}

		internal static HorizontalLayoutGroup Horizontal(Component c, float spacing = 8f, int padding = 0)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			HorizontalLayoutGroup obj = c.gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((LayoutGroup)obj).padding = new RectOffset(padding, padding, padding, padding);
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			return obj;
		}

		internal static Image Panel(Transform parent, string name, Color? color = null, bool border = true)
		{
			//IL_004a: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Fill(Rect(parent, name));
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			obj.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			obj.type = (Type)1;
			((Graphic)obj).color = (Color)(((??)color) ?? Theme.PanelBg);
			((Graphic)obj).raycastTarget = true;
			if (border)
			{
				Image obj2 = ((Component)Fill(Rect((Transform)(object)val, "Border"))).gameObject.AddComponent<Image>();
				obj2.sprite = TexGen.Outline(Theme.CornerRadius);
				obj2.type = (Type)1;
				((Graphic)obj2).color = Theme.PanelBorder;
				((Graphic)obj2).raycastTarget = false;
			}
			return obj;
		}

		internal static TextMeshProUGUI Text(Transform parent, string text, float size = 0f, TextAlignmentOptions align = (TextAlignmentOptions)513, Color? color = null, string name = "Text")
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			TextMeshProUGUI val = ((Component)Rect(parent, name)).gameObject.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset val2 = FontProvider.Get();
			if ((Object)(object)val2 != (Object)null)
			{
				((TMP_Text)val).font = val2;
			}
			((TMP_Text)val).text = text;
			((TMP_Text)val).fontSize = ((size > 0f) ? size : Theme.FontSize);
			((TMP_Text)val).alignment = align;
			((Graphic)val).color = (Color)(((??)color) ?? Theme.Text);
			((TMP_Text)val).enableWordWrapping = true;
			((TMP_Text)val).overflowMode = (TextOverflowModes)1;
			((Graphic)val).raycastTarget = false;
			return val;
		}

		internal static Button Button(Transform parent, string label, Action onClick, Vector2? size = null, string? name = null)
		{
			//IL_0036: 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_0065: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, name ?? ("Button_" + label));
			obj.sizeDelta = (Vector2)(((??)size) ?? new Vector2(260f, 52f));
			Image val = ((Component)obj).gameObject.AddComponent<Image>();
			val.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			val.type = (Type)1;
			((Graphic)val).color = Color.white;
			Button val2 = ((Component)obj).gameObject.AddComponent<Button>();
			((Selectable)val2).targetGraphic = (Graphic)(object)val;
			((Selectable)val2).colors = Colors();
			((UnityEvent)val2.onClick).AddListener(Guarded("Button " + label, onClick));
			Fill(((TMP_Text)Text((Transform)(object)obj, label, 0f, (TextAlignmentOptions)514, null, "Label")).rectTransform, 6f);
			if (size.HasValue)
			{
				Size((Component)(object)val2, size.Value.x, size.Value.y);
			}
			return val2;
		}

		internal static Button CycleButton(Transform parent, Func<string> label, Action onClick, Vector2? size = null, string? name = null)
		{
			TextMeshProUGUI text = null;
			Button val = Button(parent, label(), delegate
			{
				onClick();
				if ((Object)(object)text != (Object)null)
				{
					((TMP_Text)text).text = label();
				}
			}, size, name);
			text = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
			return val;
		}

		internal static void SetLabel(Button button, string label)
		{
			TextMeshProUGUI componentInChildren = ((Component)button).GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)componentInChildren).text = label;
			}
		}

		internal static Image Picture(Transform parent, string name, Sprite? sprite, Color color, bool raycast = false)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)Rect(parent, name)).gameObject.AddComponent<Image>();
			obj.sprite = (Sprite)(((Object)(object)sprite != (Object)null) ? ((object)sprite) : ((object)TexGen.White()));
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = raycast;
			obj.preserveAspect = (Object)(object)sprite != (Object)null;
			return obj;
		}

		internal static Image Line(RectTransform parent, Vector2 from, Vector2 to, float thickness, Color color)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_001b: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			Image obj = Picture((Transform)(object)parent, "Line", null, color);
			UiMath.Line(from.x, from.y, to.x, to.y, out var centreX, out var centreY, out var length, out var angleDeg);
			PlaceTL(((Graphic)obj).rectTransform, centreX, centreY, length, thickness);
			((Transform)((Graphic)obj).rectTransform).localRotation = Quaternion.Euler(0f, 0f, angleDeg);
			return obj;
		}

		internal static RectTransform Backdrop(Transform parent, string name = "Backdrop", Color? color = null)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Fill(Rect(parent, name));
			Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
			obj2.sprite = TexGen.White();
			((Graphic)obj2).color = (Color)(((??)color) ?? Theme.BackdropColor);
			((Graphic)obj2).raycastTarget = true;
			return obj;
		}

		internal static Toggle Toggle(Transform parent, string label, bool value, Action<bool> onChanged, float height = 44f)
		{
			//IL_0019: 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_007a: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, "Toggle_" + label);
			obj.sizeDelta = new Vector2(400f, height);
			Size((Component)(object)obj, -1f, height, 1f);
			Image val = ((Component)obj).gameObject.AddComponent<Image>();
			val.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			val.type = (Type)1;
			((Graphic)val).color = Color.white;
			RectTransform obj2 = Place(Rect((Transform)(object)obj, "Box"), new Vector2(0f, 0.5f), new Vector2(8f, 0f), new Vector2(height - 12f, height - 12f));
			Image obj3 = ((Component)obj2).gameObject.AddComponent<Image>();
			obj3.sprite = TexGen.Outline(6);
			obj3.type = (Type)1;
			((Graphic)obj3).color = Theme.Text;
			((Graphic)obj3).raycastTarget = false;
			Image val2 = ((Component)Fill(Rect((Transform)(object)obj2, "Check"), 6f)).gameObject.AddComponent<Image>();
			val2.sprite = TexGen.RoundedRect(4);
			val2.type = (Type)1;
			((Graphic)val2).color = Theme.Text;
			((Graphic)val2).raycastTarget = false;
			TextMeshProUGUI obj4 = Text((Transform)(object)obj, label, 0f, (TextAlignmentOptions)513, null, "Label");
			((TMP_Text)obj4).rectTransform.anchorMin = new Vector2(0f, 0f);
			((TMP_Text)obj4).rectTransform.anchorMax = new Vector2(1f, 1f);
			((TMP_Text)obj4).rectTransform.offsetMin = new Vector2(height + 4f, 0f);
			((TMP_Text)obj4).rectTransform.offsetMax = new Vector2(-8f, 0f);
			Toggle obj5 = ((Component)obj).gameObject.AddComponent<Toggle>();
			((Selectable)obj5).targetGraphic = (Graphic)(object)val;
			obj5.graphic = (Graphic)(object)val2;
			((Selectable)obj5).colors = Colors(transparentNormal: true);
			obj5.isOn = value;
			UnityAction<bool> val3 = Guarded("Toggle " + label, onChanged);
			((UnityEvent<bool>)(object)obj5.onValueChanged).AddListener(val3);
			return obj5;
		}

		internal static ScrollRect ScrollList(Transform parent, string name, out RectTransform content, float spacing = 6f)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Fill(Rect(parent, name));
			ScrollRect val = ((Component)obj).gameObject.AddComponent<ScrollRect>();
			val.horizontal = false;
			val.vertical = true;
			val.movementType = (MovementType)2;
			val.scrollSensitivity = 30f;
			RectTransform val2 = Fill(Rect((Transform)(object)obj, "Viewport"));
			((Component)val2).gameObject.AddComponent<RectMask2D>();
			((Graphic)((Component)val2).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0f);
			content = Rect((Transform)(object)val2, "Content");
			content.anchorMin = new Vector2(0f, 1f);
			content.anchorMax = new Vector2(1f, 1f);
			content.pivot = new Vector2(0.5f, 1f);
			content.anchoredPosition = Vector2.zero;
			content.sizeDelta = Vector2.zero;
			((LayoutGroup)Vertical((Component)(object)content, spacing, 4)).childAlignment = (TextAnchor)1;
			ContentSizeFitter obj2 = ((Component)content).gameObject.AddComponent<ContentSizeFitter>();
			obj2.verticalFit = (FitMode)2;
			obj2.horizontalFit = (FitMode)0;
			val.viewport = val2;
			val.content = content;
			return val;
		}

		internal static void ScrollIntoView(ScrollRect scroll, RectTransform item)
		{
			ReflectionGuard.Run("UiFactory.ScrollIntoView", delegate
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: 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_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				RectTransform content = scroll.content;
				RectTransform val = (RectTransform)(((Object)(object)scroll.viewport != (Object)null) ? ((object)scroll.viewport) : ((object)(RectTransform)((Component)scroll).transform));
				if (!((Object)(object)content == (Object)null))
				{
					Canvas.ForceUpdateCanvases();
					Rect rect = content.rect;
					float height = ((Rect)(ref rect)).height;
					rect = val.rect;
					float height2 = ((Rect)(ref rect)).height;
					if (!(height <= height2))
					{
						float y = ((Transform)content).InverseTransformPoint(((Transform)item).position).y;
						rect = item.rect;
						float num = 0f - (y + ((Rect)(ref rect)).height * (1f - item.pivot.y));
						rect = item.rect;
						float num2 = num + ((Rect)(ref rect)).height;
						float num3 = content.anchoredPosition.y;
						if (num < num3)
						{
							num3 = num;
						}
						else if (num2 > num3 + height2)
						{
							num3 = num2 - height2;
						}
						num3 = Mathf.Clamp(num3, 0f, height - height2);
						content.anchoredPosition = new Vector2(content.anchoredPosition.x, num3);
					}
				}
			});
		}

		internal static void LinkVertical(IList<Selectable> items, bool wrap = true, Selectable? left = null, Selectable? right = null)
		{
			Link(items, wrap, vertical: true, left, right);
		}

		internal static void LinkHorizontal(IList<Selectable> items, bool wrap = false, Selectable? up = null, Selectable? down = null)
		{
			Link(items, wrap, vertical: false, up, down);
		}

		internal static void Select(Selectable? target)
		{
			ReflectionGuard.Run("UiFactory.Select", delegate
			{
				EventSystem current = EventSystem.current;
				if (!((Object)(object)current == (Object)null) && !((Object)(object)target == (Object)null) && ((Component)target).gameObject.activeInHierarchy)
				{
					current.SetSelectedGameObject((GameObject)null);
					current.SetSelectedGameObject(((Component)target).gameObject);
				}
			});
		}

		internal static void Destroy(Component? root)
		{
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root.gameObject);
			}
		}

		private static void Link(IList<Selectable> items, bool wrap, bool vertical, Selectable? sideA, Selectable? sideB)
		{
			//IL_00b4: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			List<Selectable> list = new List<Selectable>();
			foreach (Selectable item in items)
			{
				if ((Object)(object)item != (Object)null && ((Component)item).gameObject.activeInHierarchy && item.IsInteractable())
				{
					list.Add(item);
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				Selectable val = ((i > 0) ? list[i - 1] : ((wrap && list.Count > 1) ? list[list.Count - 1] : null));
				Selectable val2 = ((i < list.Count - 1) ? list[i + 1] : ((wrap && list.Count > 1) ? list[0] : null));
				Navigation val3 = default(Navigation);
				((Navigation)(ref val3)).mode = (Mode)4;
				Navigation navigation = val3;
				if (vertical)
				{
					((Navigation)(ref navigation)).selectOnUp = val;
					((Navigation)(ref navigation)).selectOnDown = val2;
					((Navigation)(ref navigation)).selectOnLeft = sideA;
					((Navigation)(ref navigation)).selectOnRight = sideB;
				}
				else
				{
					((Navigation)(ref navigation)).selectOnLeft = val;
					((Navigation)(ref navigation)).selectOnRight = val2;
					((Navigation)(ref navigation)).selectOnUp = sideA;
					((Navigation)(ref navigation)).selectOnDown = sideB;
				}
				list[i].navigation = navigation;
			}
		}

		private static ColorBlock Colors(bool transparentNormal = false)
		{
			//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_0026: 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_0032: 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_004a: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock;
			((ColorBlock)(ref defaultColorBlock)).normalColor = (Color)(transparentNormal ? new Color(0f, 0f, 0f, 0f) : Theme.ButtonBg);
			((ColorBlock)(ref defaultColorBlock)).highlightedColor = Theme.ButtonHighlight;
			((ColorBlock)(ref defaultColorBlock)).selectedColor = Theme.ButtonHighlight;
			((ColorBlock)(ref defaultColorBlock)).pressedColor = Theme.ButtonPressed;
			((ColorBlock)(ref defaultColorBlock)).disabledColor = Theme.ButtonDisabled;
			((ColorBlock)(ref defaultColorBlock)).colorMultiplier = 1f;
			((ColorBlock)(ref defaultColorBlock)).fadeDuration = 0.08f;
			return defaultColorBlock;
		}

		private static UnityAction Guarded(string key, Action action)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return (UnityAction)delegate
			{
				ReflectionGuard.Run(key, action);
			};
		}

		private static UnityAction<T> Guarded<T>(string key, Action<T> action)
		{
			return delegate(T v)
			{
				ReflectionGuard.Run(key, delegate
				{
					action(v);
				});
			};
		}
	}
	internal static class UiMath
	{
		internal static void Line(float fromX, float fromY, float toX, float toY, out float centreX, out float centreY, out float length, out float angleDeg)
		{
			float num = toX - fromX;
			float num2 = 0f - (toY - fromY);
			centreX = (fromX + toX) * 0.5f;
			centreY = (fromY + toY) * 0.5f;
			length = (float)Math.Sqrt(num * num + num2 * num2);
			angleDeg = ((length > 0f) ? ((float)(Math.Atan2(num2, num) * (180.0 / Math.PI))) : 0f);
		}
	}
}
namespace DualSenseHaptics
{
	internal static class Snapshots
	{
		internal const float MaxAge = 0.3f;

		internal static CruiserInputs Cruiser;

		internal static float CruiserTime = float.NegativeInfinity;

		internal static JetpackInputs Jetpack;

		internal static JetpackItem? JetpackSource;

		internal static float JetpackTime = float.NegativeInfinity;

		internal static void Clear()
		{
			Cruiser = default(CruiserInputs);
			CruiserTime = float.NegativeInfinity;
			Jetpack = default(JetpackInputs);
			JetpackSource = null;
			JetpackTime = float.NegativeInfinity;
		}
	}
	internal static class GameStateReader
	{
		internal static PlayerControllerB? LocalPlayer
		{
			get
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return null;
				}
				PlayerControllerB localPlayerController = instance.localPlayerController;
				if (!((Object)(object)localPlayerController != (Object)null))
				{
					return null;
				}
				return localPlayerController;
			}
		}

		internal static bool IsLocal(PlayerControllerB? player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			PlayerControllerB localPlayer = LocalPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return player == localPlayer;
			}
			return false;
		}

		internal static void Read(GameInputs g, bool focused, HapticsSettings settings)
		{
			g.Focused = focused;
			PlayerControllerB localPlayer = LocalPlayer;
			StartOfRound instance = StartOfRound.Instance;
			g.InGame = (Object)(object)localPlayer != (Object)null && (Object)(object)instance != (Object)null;
			if (!g.InGame || (Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null)
			{
				ImmersionReader.Clear(g);
				g.Dead = false;
				g.MenuOpen = false;
				g.Health = 100;
				g.CriticallyInjured = false;
				g.HeldItem = HeldItemKind.None;
				g.Jetpack = default(JetpackInputs);
				g.Cruiser = default(CruiserInputs);
				return;
			}
			g.Dead = localPlayer.isPlayerDead;
			g.Health = localPlayer.health;
			g.CriticallyInjured = localPlayer.criticallyInjured;
			QuickMenuManager quickMenuManager = localPlayer.quickMenuManager;
			g.MenuOpen = (Object)(object)quickMenuManager != (Object)null && quickMenuManager.isMenuOpen;
			float unscaledTime = Time.unscaledTime;
			GrabbableObject currentlyHeldObjectServer = localPlayer.currentlyHeldObjectServer;
			if ((Object)(object)currentlyHeldObjectServer == (Object)null)
			{
				g.HeldItem = HeldItemKind.None;
			}
			else if (currentlyHeldObjectServer is JetpackItem)
			{
				g.HeldItem = HeldItemKind.Jetpack;
			}
			else if (currentlyHeldObjectServer is Shovel || currentlyHeldObjectServer is KnifeItem)
			{
				g.HeldItem = HeldItemKind.Melee;
			}
			else
			{
				g.HeldItem = HeldItemKind.Other;
			}
			if (g.HeldItem == HeldItemKind.Jetpack && unscaledTime - Snapshots.JetpackTime < 0.3f && (object)Snapshots.JetpackSource == currentlyHeldObjectServer)
			{
				g.Jetpack = Snapshots.Jetpack;
			}
			else
			{
				g.Jetpack = default(JetpackInputs);
				if (g.HeldItem == HeldItemKind.Jetpack)
				{
					g.Jetpack.Held = true;
				}
				g.Jetpack.Battery01 = 1f;
			}
			g.Cruiser = ((unscaledTime - Snapshots.CruiserTime < 0.3f) ? Snapshots.Cruiser : default(CruiserInputs));
			ImmersionReader.Read(g, localPlayer, instance, settings, unscaledTime);
		}
	}
	internal static class BindingProbe
	{
		private const string Feature = "BindingProbe";

		internal static TriggerSide UseItemSide()
		{
			//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_0059: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				IngamePlayerSettings instance = IngamePlayerSettings.Instance;
				PlayerInput val = (((Object)(object)instance != (Object)null) ? instance.playerInput : null);
				InputAction val2 = (((Object)(object)val != (Object)null && (Object)(object)val.actions != (Object)null) ? val.actions.FindAction("ActivateItem", false) : null);
				if (val2 == null)
				{
					return TriggerSide.Right;
				}
				Enumerator<InputBinding> enumerator = val2.bindings.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						InputBinding current = enumerator.Current;
						if (!((InputBinding)(ref current)).isComposite)
						{
							TriggerSide? triggerSide = BindingPaths.TriggerSideOf(((InputBinding)(ref current)).effectivePath);
							if (triggerSide.HasValue && IsGamepadPath(((InputBinding)(ref current)).effectivePath))
							{
								return triggerSide.Value;
							}
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				Log.WarnOnce("BindingProbe.useitem", "[DualSenseHaptics] could not read the ActivateItem binding (" + ex.Message + "); item trigger effects go to R2");
			}
			return TriggerSide.Right;
		}

		internal static PedalBindings? BvcPedals()
		{
			//IL_0047: 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_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_005b: 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)
			try
			{
				List<(string, string, string)> list = new List<(string, string, string)>();
				foreach (InputAction item in InputSystem.ListEnabledActions())
				{
					string text = ((item.actionMap != null) ? item.actionMap.name : null);
					if (text == null || !text.StartsWith("uk.1a3.bettervehiclecontrols", StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					Enumerator<InputBinding> enumerator2 = item.bindings.GetEnumerator();
					try
					{
						while (enumerator2.MoveNext())
						{
							InputBinding current2 = enumerator2.Current;
							if (!((InputBinding)(ref current2)).isComposite)
							{
								list.Add((text, item.name, ((InputBinding)(ref current2)).effectivePath));
							}
						}
					}
					finally
					{
						((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
					}
				}
				return CruiserTriggerPolicy.Scan(list);
			}
			catch (Exception ex)
			{
				Log.WarnOnce("BindingProbe.bvc", "[DualSenseHaptics] could not read BetterVehicleControls bindings: " + ex.Message);
				return null;
			}
		}

		internal static bool BvcLoaded()
		{
			try
			{
				return Chainloader.PluginInfos.ContainsKey("uk.1a3.bettervehiclecontrols");
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool IsGamepadPath(string? path)
		{
			if (path != null)
			{
				if (!path.StartsWith("<Gamepad>", StringComparison.Ordinal) && !path.StartsWith("<DualSense", StringComparison.Ordinal) && !path.StartsWith("<XInput", StringComparison.Ordinal))
				{
					return path.StartsWith("<DualShock", StringComparison.Ordinal);
				}
				return true;
			}
			return false;
		}
	}
	internal static class Haptics
	{
		internal static HapticsEngine? Engine
		{
			get
			{
				HapticsRunner instance = HapticsRunner.Instance;
				if (!((Object)(object)instance != (Object)null))
				{
					return null;
				}
				return instance.Engine;
			}
		}

		internal static void Fire(HapticEvent e, float scale = 1f)
		{
			Engine?.Fire(e, scale);
		}

		internal static void Damage(int damage, bool fall)
		{
			Engine?.FireDamage(damage, fall);
		}

		internal static void Shake(ShakeKind kind)
		{
			Engine?.FireShake(kind);
		}

		internal static void StopShake()
		{
			Engine?.StopShake();
		}

		internal static void LocalDeath()
		{
			Engine?.OnLocalDeath();
		}

		internal static void HardReset(string why)
		{
			HapticsRunner instance = HapticsRunner.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.HardReset(why);
			}
		}

		internal static void TookWheel()
		{
			HapticsRunner instance = HapticsRunner.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.OnTookWheel();
			}
		}

		internal static void LeftWheel()
		{
			HapticsRunner instance = HapticsRunner.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.OnLeftWheel();
			}
		}
	}
	internal sealed class HapticsRunner : MonoBehaviour
	{
		private const string Key = "Runner.LateUpdate";

		private const string GyroKey = "Gyro.Controller";

		private const float RescanInterval = 1f;

		private readonly GameInputs _inputs = new GameInputs();

		private PluginConfig _cfg;

		private IHapticsBackend? _backend;

		private GyroController? _gyro;

		private bool _focused = true;

		private bool _rescan = true;

		private float _nextScan;

		private bool _wasInGame;

		private float _inGameSince;

		private bool _hintPending;

		private float _nextHintTry;

		private string _lastInventory = "";

		private bool _cruiserEvaluated;

		private bool _quitting;

		private bool _releasedOnQuit;

		private Gamepad? _seenPad;

		private float _seenPadSince;

		private float _nextTuneLog;

		private float _nextImmersionLog;

		private int _lastSentCount;

		private readonly HashSet<string> _loggedBackends = new HashSet<string>(StringComparer.Ordinal);

		internal static HapticsRunner? Instance { get; private set; }

		internal HapticsEngine Engine { get; private set; }

		internal IHapticsBackend? Backend => _backend;

		internal static HapticsRunner Create(PluginConfig cfg, Harmony gyroHarmony)
		{
			//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: Expected O, but got Unknown
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("DualSenseHaptics.Runner");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			HapticsRunner hapticsRunner = val.AddComponent<HapticsRunner>();
			hapticsRunner.Init(cfg, gyroHarmony);
			return hapticsRunner;
		}

		private void Init(PluginConfig cfg, Harmony gyroHarmony)
		{
			_cfg = cfg;
			Engine = new HapticsEngine(cfg.ToSettings(TriggerSide.Right));
			cfg.Changed += OnConfigChanged;
			try
			{
				_gyro = new GyroController(cfg.Gyro, gyroHarmony);
			}
			catch (Exception ex)
			{
				Log.Error("[DualSenseHaptics] gyro could not start (haptics unaffected): " + ex);
			}
		}

		private void Awake()
		{
			Instance = this;
			InputSystem.onDeviceChange += OnDeviceChange;
			SceneManager.activeSceneChanged += OnSceneChanged;
		}

		private void OnDestroy()
		{
			if (!_releasedOnQuit)
			{
				HardReset("runner destroyed", blocking: true);
			}
			InputSystem.onDeviceChange -= OnDeviceChange;
			SceneManager.activeSceneChanged -= OnSceneChanged;
			if (_cfg != null)
			{
				_cfg.Changed -= OnConfigChanged;
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private void OnApplicationQuit()
		{
			_quitting = true;
			_releasedOnQuit = HardReset("application quit", blocking: true);
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			try
			{
				_focused = hasFocus;
				if (_backend == null || Engine == null || _quitting)
				{
					return;
				}
				if (!hasFocus)
				{
					_inputs.Focused = false;
					if (!_backend.SendBlocking(Engine.Tick(0f, _inputs)))
					{
						Log.WarnOnce("focus.release", "[DualSenseHaptics] " + _backend.Name + ": focus-loss release rejected (the Input System may have disabled the pad in the background); retrying each frame while the game runs.");
					}
				}
				else
				{
					_backend.ForceResend();
				}
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Runner.Focus", e);
			}
		}

		private void LateUpdate()
		{
			if (_quitting || !PatchSafety.Enabled("Runner.LateUpdate"))
			{
				return;
			}
			try
			{
				Step();
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Runner.LateUpdate", e);
				if (!ReflectionGuard.IsEnabled("Runner.LateUpdate"))
				{
					HardReset("runner disabled after repeated errors", blocking: true);
				}
			}
		}

		private void Step()
		{
			float unscaledTime = Time.unscaledTime;
			Gamepad current = Gamepad.current;
			if (current != _seenPad)
			{
				_seenPad = current;
				_seenPadSince = unscaledTime;
			}
			if (_rescan || unscaledTime >= _nextScan)
			{
				_nextScan = unscaledTime + 1f;
				SelectBackend(_rescan, unscaledTime);
				_rescan = false;
			}
			GameStateReader.Read(_inputs, _focused, Engine.Settings);
			if (_inputs.InGame && !_wasInGame)
			{
				OnEnteredGame(unscaledTime);
			}
			if (!_inputs.InGame && _wasInGame)
			{
				Snapshots.Clear();
			}
			_wasInGame = _inputs.InGame;
			if (_inputs.Cruiser.InControl && !_cruiserEvaluated)
			{
				EvaluateCruiserTriggers();
			}
			_inputs.GyroLed = UpdateGyro(unscaledTime);
			DsState state = Engine.Tick(Time.unscaledDeltaTime, _inputs);
			_backend?.Apply(state, unscaledTime);
			if (_hintPending && _inputs.InGame && !_inputs.Dead && unscaledTime - _inGameSince > 8f && unscaledTime >= _nextHintTry)
			{
				ShowHint(unscaledTime);
			}
			if (_cfg.DebugLog.Value && unscaledTime >= _nextTuneLog)
			{
				LogTuning(unscaledTime);
			}
		}

		private GyroLedRequest UpdateGyro(float now)
		{
			if (_gyro == null || !PatchSafety.Enabled("Gyro.Controller"))
			{
				return GyroLedRequest.NotControlled;
			}
			try
			{
				return _gyro.Update(Engine, _backend, _inputs, now);
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Gyro.Controller", e);
				GyroState.Live = false;
				return GyroLedRequest.NotControlled;
			}
		}

		private void LogTuning(float now)
		{
			CruiserInputs cruiser = _inputs.Cruiser;
			JetpackInputs jetpack = _inputs.Jetpack;
			bool inControl = cruiser.InControl;
			bool flag = _inputs.HeldItem == HeldItemKind.Jetpack && jetpack.Held;
			if (_inputs.InGame && !_inputs.Dead && (_inputs.FearLevel > 0.05f || (_inputs.HeartbeatPlaying && Engine.Heartbeat.Detector.Active) || _inputs.Insanity01 > 0.5f || _inputs.WalkieRx || _inputs.WalkieTx || _inputs.EarsRingingTimer > 0f || _inputs.HauntVolume > 0f) && now >= _nextImmersionLog)
			{
				_nextImmersionLog = now + 1f;
				int num = ((_backend is DsDevice dsDevice) ? dsDevice.Scheduler.TotalSent : 0);
				Log.Info("[DualSenseHaptics] immersion: " + ImmersionReader.Describe(_inputs, Engine) + ((_backend is DsDevice) ? (" reports/s=" + (num - _lastSentCount)) : ""));
				_lastSentCount = num;
			}
			if (inControl || flag)
			{
				_nextTuneLog = now + 1f;
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				string text = "[DualSenseHaptics] tune:";
				if (inControl)
				{
					text = text + " cruiser rpm=" + cruiser.EngineRpm.ToString("F0", invariantCulture) + " rpm01=" + cruiser.Rpm01.ToString("F2", invariantCulture) + " speed=" + cruiser.Speed.ToString("F1", invariantCulture) + " gas=" + cruiser.GasPressed + " brake=" + cruiser.BrakePressed + " gear=" + cruiser.Gear.ToString() + " ignition=" + cruiser.Ignition + " triggers=" + Engine.CruiserDecision.Enabled;
				}
				if (flag)
				{
					text = text + " jetpack active=" + jetpack.Active + " power=" + jetpack.RawPower.ToString("F1", invariantCulture) + " power01=" + jetpack.Power01.ToString("F2", invariantCulture) + " battery=" + jetpack.Battery01.ToString("F2", invariantCulture);
				}
				Log.Info(text);
			}
		}

		internal void OnTookWheel()
		{
			_cruiserEvaluated = false;
			EvaluateCruiserTriggers();
		}

		internal void OnLeftWheel()
		{
			_cruiserEvaluated = false;
			Snapshots.Cruiser = default(CruiserInputs);
			Snapshots.CruiserTime = float.NegativeInfinity;
			_backend?.ForceResend();
		}

		internal bool HardReset(string why, bool blocking = false)
		{
			try
			{
				Snapshots.Clear();
				_cruiserEvaluated = false;
				if (Engine == null)
				{
					return true;
				}
				DsState state = Engine.HardReset();
				if (_quitting && _releasedOnQuit)
				{
					return true;
				}
				bool flag = true;
				if (_backend != null)
				{
					flag = (blocking ? _backend.SendBlocking(state) : _backend.SendImmediate(state));
					_backend.ForceResend();
					if (!flag && blocking)
					{
						Log.Warn("[DualSenseHaptics] " + _backend.Name + ": reset on " + why + " rejected " + 10 + " times; triggers may stay stiff until the pad is replugged (or run tools/hidtest/ds_report.py --off).");
					}
				}
				if (_cfg != null && _cfg.DebugLog.Value)
				{
					Log.Info("[DualSenseHaptics] hard reset (" + why + ")" + (flag ? "" : " - report rejected"));
				}
				return flag;
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Runner.HardReset", e);
				return false;
			}
		}

		private void OnEnteredGame(float now)
		{
			_inGameSince = now;
			TriggerSide triggerSide = BindingProbe.UseItemSide();
			Engine.Settings = _cfg.ToSettings(triggerSide);
			if (_cfg.DebugLog.Value)
			{
				Log.Info("[DualSenseHaptics] in game; item trigger effects on " + ((triggerSide == TriggerSide.Right) ? "R2" : "L2"));
			}
		}

		private void EvaluateCruiserTriggers()
		{
			_cruiserEvaluated = true;
			bool flag = BindingProbe.BvcLoaded();
			PedalBindings? bindings = (flag ? BindingProbe.BvcPedals() : ((PedalBindings?)null));
			CruiserTriggerDecision cruiserDecision = CruiserTriggerPolicy.Decide(_cfg.CruiserTriggers.Value, flag, bindings);
			Engine.CruiserDecision = cruiserDecision;
			if (Log.MarkOnce("cruiser." + cruiserDecision))
			{
				Log.Info("[DualSenseHaptics] Cruiser triggers " + cruiserDecision);
			}
		}

		private void SelectBackend(bool deviceChanged, float now)
		{
			bool padSettled = now - _seenPadSince >= 2f;
			IHapticsBackend hapticsBackend = DeviceSelector.Select(_cfg, _backend, _seenPad, padSettled);
			if (hapticsBackend != _backend)
			{
				try
				{
					_backend?.Release();
				}
				catch (Exception ex)
				{
					Log.Warn("[DualSenseHaptics] releasing " + _backend?.Name + " failed: " + ex.Message);
				}
				_backend = hapticsBackend;
				string text = "[DualSenseHaptics] " + (_backend?.Describe() ?? "backend=none (no gamepad)");
				if (_loggedBackends.Add(text))
				{
					Log.Info(text);
				}
				else if (_cfg.DebugLog.Value)
				{
					Log.Info(text + " (again)");
				}
				_backend?.ForceResend();
				deviceChanged = true;
			}
			else if (deviceChanged)
			{
				_backend?.ForceResend();
			}
			if (!deviceChanged)
			{
				return;
			}
			PadInventory inv = DeviceSelector.Inventory();
			string text2 = SteamInputAdvisor.Describe(inv);
			if (text2 != _lastInventory)
			{
				_lastInventory = text2;
				Log.Info("[DualSenseHaptics] pads: " + text2);
			}
			if (SteamInputAdvisor.ShouldShowHint(inv))
			{
				Log.WarnOnce("hint.log", "[DualSenseHaptics] Disable Steam Input for LC to get adaptive triggers and lightbar (Steam > Lethal Company > Properties > Controller > Disable Steam Input). With Steam Input on, the game gets Steam's virtual pad (rumble only), and Steam may also overwrite the DualSense's lightbar and rumble.");
				if (_cfg.ShowSteamInputHint.Value)
				{
					_hintPending = true;
				}
			}
			else
			{
				_hintPending = false;
			}
		}

		private void ShowHint(float now)
		{
			_nextHintTry = now + 3f;
			HUDManager instance = HUDManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.DisplayTip("DualSenseHaptics", "DualSense owners: disable Steam Input for Lethal Company (Steam > Properties > Controller) to get adaptive triggers and lightbar. Rumble works either way.", false, false, "LC_Tip1");
				TextMeshProUGUI tipsPanelHeader = instance.tipsPanelHeader;
				TextMeshProUGUI tipsPanelBody = instance.tipsPanelBody;
				if (!((Object)(object)tipsPanelHeader == (Object)null) && !((Object)(object)tipsPanelBody == (Object)null) && !(((TMP_Text)tipsPanelHeader).text != "DualSenseHaptics") && !(((TMP_Text)tipsPanelBody).text != "DualSense owners: disable Steam Input for Lethal Company (Steam > Properties > Controller) to get adaptive triggers and lightbar. Rumble works either way."))
				{
					_hintPending = false;
					_cfg.MarkHintShown();
					Log.Info("[DualSenseHaptics] Steam Input hint shown (disable it in the config: [Hints] Show Steam Input hint)");
				}
			}
		}

		private void OnDeviceChange(InputDevice device, InputDeviceChange change)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			try
			{
				if ((int)change == 0 || (int)change == 1 || (int)change == 3 || (int)change == 2 || (int)change == 4 || (int)change == 5)
				{
					if (_backend != null && _backend.Device == device && ((int)change == 1 || (int)change == 2))
					{
						_backend = null;
					}
					if (GyroState.Device == device && ((int)change == 1 || (int)change == 2))
					{
						GyroState.Capturing = false;
						GyroState.Live = false;
						GyroState.Device = null;
						GyroState.Pipeline.Reset();
					}
					_gyro?.OnDeviceChange();
					_rescan = true;
				}
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Runner.DeviceChange", e);
			}
		}

		private void OnSceneChanged(Scene from, Scene to)
		{
			HardReset("scene change to " + ((Scene)(ref to)).name);
		}

		private void OnConfigChanged()
		{
			Log.DebugEnabled = _cfg.DebugLog.Value;
			Engine.Settings = _cfg.ToSettings(BindingProbe.UseItemSide());
			_cruiserEvaluated = false;
			_rescan = true;
			try
			{
				_gyro?.ApplyConfig();
			}
			catch (Exception e)
			{
				PatchSafety.Fail("Gyro.Controller", e);
			}
		}
	}
	internal static class ImmersionReader
	{
		private const string Feature = "Immersion";

		private const string VoiceFeature = "Immersion.VoiceAmplitude";

		private const float GirlRescanSeconds = 1f;

		private static DressGirlAI? _girl;

		private static float _nextGirlScan;

		private static FieldInfo? _voiceField;

		private static bool _voiceFieldLooked;

		private static Type? _voiceType;

		private static PropertyInfo? _amplitude;

		internal static void Clear(GameInputs g)
		{
			g.HeartbeatTimer = 0f;
			g.HeartbeatPlaying = false;
			g.HeartbeatVolume = 0f;
			g.FearLevel = 0f;
			g.Insanity01 = 0f;
			g.InsideFactory = false;
			g.ShipPhase = true;
			g.WalkieRx = false;
			g.WalkieTx = false;
			g.RxAmplitude = -1f;
			g.EarsRingingTimer = 0f;
			g.HauntVolume = 0f;
			_girl = null;
		}

		internal static void Read(GameInputs g, PlayerControllerB p, StartOfRound round, HapticsSettings s, float now)
		{
			SoundManager instance = SoundManager.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				g.HeartbeatTimer = instance.heartbeatTimer;
				AudioSource heartbeatSFX = instance.heartbeatSFX;
				g.HeartbeatPlaying = (Object)(object)heartbeatSFX != (Object)null && heartbeatSFX.isPlaying;
				g.HeartbeatVolume = (((Object)(object)heartbeatSFX != (Object)null) ? heartbeatSFX.volume : 0f);
				g.EarsRingingTimer = instance.earsRingingTimer;
			}
			else
			{
				g.HeartbeatPlaying = false;
				g.EarsRingingTimer = 0f;
			}
			g.FearLevel = round.fearLevel;
			g.ShipPhase = round.inShipPhase;
			g.Insanity01 = ImmersionMath.Insanity01(p.insanityLevel, p.maxInsanityLevel);
			g.InsideFactory = p.isInsideFactory;
			ReadWalkie(g, p, s);
			g.HauntVolume = ((s.GhostGirlHeartbeat && s.IsEnabled(HapticCategory.Fear)) ? HauntVolume(p, now) : 0f);
		}

		private static void ReadWalkie(GameInputs g, PlayerControllerB p, HapticsSettings s)
		{
			g.WalkieTx = p.speakingToWalkieTalkie && !p.isPlayerDead;
			g.WalkieRx = false;
			g.RxAmplitude = -1f;
			if (!p.holdingWalkieTalkie)
			{
				return;
			}
			List<WalkieTalkie> allWalkieTalkies = WalkieTalkie.allWalkieTalkies;
			if (allWalkieTalkies == null)
			{
				return;
			}
			GrabbableObject currentlyHeldObjectServer = p.currentlyHeldObjectServer;
			WalkieTalkie val = (WalkieTalkie)(object)((currentlyHeldObjectServer is WalkieTalkie) ? currentlyHeldObjectServer : null);
			for (int i = 0; i < allWalkieTalkies.Count; i++)
			{
				WalkieTalkie val2 = allWalkieTalkies[i];
				if (!((Object)(object)val2 == (Object)null) && val2.clientIsHoldingAndSpeakingIntoThis && val2 != val)
				{
					g.WalkieRx = true;
					if (s.StaticFollowsVoice)
					{
						g.RxAmplitude = Amplitude(((GrabbableObject)val2).playerHeldBy);
					}
					break;
				}
			}
		}

		private static float HauntVolume(PlayerControllerB local, float now)
		{
			if (now >= _nextGirlScan)
			{
				_nextGirlScan = now + 1f;
				_girl = FindGirl(local);
			}
			DressGirlAI girl = _girl;
			if ((Object)(object)girl == (Object)null || ((EnemyAI)girl).isEnemyDead || girl.hauntingPlayer != local)
			{
				return 0f;
			}
			AudioSource heartbeatMusic = girl.heartbeatMusic;
			if (!((Object)(object)heartbeatMusic != (Object)null))
			{
				return 0f;
			}
			return Rgb.Clamp01(heartbeatMusic.volume);
		}

		private static DressGirlAI? FindGirl(PlayerControllerB local)
		{
			RoundManager instance = RoundManager.Instance;
			List<EnemyAI> list = (((Object)(object)instance != (Object)null) ? instance.SpawnedEnemies : null);
			if (list == null)
			{
				return null;
			}
			for (int i = 0; i < list.Count; i++)
			{
				EnemyAI obj = list[i];
				DressGirlAI val = (DressGirlAI)(object)((obj is DressGirlAI) ? obj : null);
				if (val != null && (Object)(object)val != (Object)null && val.hauntingPlayer == local)
				{
					return val;
				}
			}
			return null;
		}

		private static float Amplitude(PlayerControllerB? speaker)
		{
			if ((Object)(object)speaker == (Object)null || !ReflectionGuard.IsEnabled("Immersion.VoiceAmplitude"))
			{
				return -1f;
			}
			try
			{
				if (!_voiceFieldLooked)
				{
					_voiceFieldLooked = true;
					_voiceField = AccessTools.Field(typeof(PlayerControllerB), "voicePlayerState");
					if (_voiceField == null)
					{
						ReflectionGuard.Disable("Immersion.VoiceAmplitude", "PlayerControllerB.voicePlayerState not found");
					}
				}
				object obj = _voiceField?.GetValue(speaker);
				if (obj == null)
				{
					return -1f;
				}
				Type type = obj.GetType();
				if ((object)type != _voiceType)
				{
					_voiceType = type;
					_amplitude = type.GetProperty("Amplitude", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				return (_amplitude != null && _amplitude.GetValue(obj) is float num) ? num : (-1f);
			}
			catch (Exception ex)
			{
				ReflectionGuard.Disable("Immersion.VoiceAmplitude", "reading the voice amplitude failed: " + ex.Message);
				return -1f;
			}
		}

		internal static string Describe(GameInputs g, HapticsEngine e)
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return "fear=" + g.FearLevel.ToString("F2", invariantCulture) + " hbTimer=" + g.HeartbeatTimer.ToString("F2", invariantCulture) + " hbPlaying=" + g.HeartbeatPlaying + " hbVol=" + g.HeartbeatVolume.ToString("F2", invariantCulture) + " insanity=" + g.Insanity01.ToString("F2", invariantCulture) + " inside=" + g.InsideFactory + " walkieRx=" + g.WalkieRx + " walkieTx=" + g.WalkieTx + " ears=" + g.EarsRingingTimer.ToString("F2", invariantCulture) + " haunt=" + g.HauntVolume.ToString("F2", invariantCulture) + " beat=" + e.Heartbeat.Active.ToString() + (e.Heartbeat.Detector.FallbackActive ? "(fallback)" : "") + " beats=" + e.BeatCount;
		}
	}
	[BepInPlugin("dev.thatumi.DualSenseHaptics", "DualSenseHaptics", "0.2.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal const string GyroHarmonyId = "dev.thatumi.DualSenseHaptics.gyro";

		private Harmony? _harmony;

		private Harmony? _gyroHarmony;

		private void Awake()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			LogBepInEx.Init(((BaseUnityPlugin)this).Logger);
			PluginConfig pluginConfig;
			try
			{
				pluginConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
				Log.DebugEnabled = pluginConfig.DebugLog.Value;
				_harmony = new Harmony("dev.thatumi.DualSenseHaptics");
				_gyroHarmony = new Harmony("dev.thatumi.DualSenseHaptics.gyro");
				HapticsRunner.Create(pluginConfig, _gyroHarmony);
			}
			catch (Exception ex)
			{
				Log.Error("DualSenseHaptics could not start, plugin disabled: " + ex);
				return;
			}
			PatchClasses(pluginConfig, _harmony, _gyroHarmony);
			Log.Info("DualSenseHaptics 0.2.3 loaded (backend chosen when a gamepad is seen)");
		}

		private static void PatchClasses(PluginConfig cfg, Harmony main, Harmony gyro)
		{
			Type[] typesFromAssembly;
			try
			{
				typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly);
			}
			catch (Exception ex)
			{
				Log.Error("Harmony patching failed, game-event haptics disabled (lightbar/menus still work): " + ex);
				return;
			}
			int num = 0;
			Type[] array = typesFromAssembly;
			foreach (Type type in array)
			{
				if (!Attribute.IsDefined(type, typeof(HarmonyPatch), inherit: false))
				{
					continue;
				}
				bool flag = type == typeof(GyroLookPatch);
				if (flag && !cfg.Gyro.PatchLookInput.Value)
				{
					Log.Info("[DualSenseHaptics] gyro: [Gyro] PatchLookInput = false, look hook not installed");
					continue;
				}
				try
				{
					(flag ? gyro : main).CreateClassProcessor(type).Patch();
				}
				catch (Exception ex2)
				{
					num++;
					Log.Error("[DualSenseHaptics] Harmony patching " + type.Name + " failed; its hooks are off, the rest work: " + ex2);
				}
			}
			if (num > 0)
			{
				Log.Warn("[DualSenseHaptics] " + num + " patch class(es) failed (see above)");
			}
		}
	}
	internal enum BackendMode
	{
		Auto,
		FallbackOnly,
		Off
	}
	internal sealed class PluginConfig
	{
		private readonly ConfigFile _file;

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<BackendMode> Backend { get; }

		internal ConfigEntry<float> Intensity { get; }

		internal ConfigEntry<bool> DebugLog { get; }

		internal ConfigEntry<int> ConfigVersion { get; }

		internal ConfigEntry<bool> RumbleCameraShake { get; }

		internal ConfigEntry<bool> RumbleDamage { get; }

		internal ConfigEntry<bool> RumbleDeath { get; }

		internal ConfigEntry<bool> RumbleHeartbeat { get; }

		internal ConfigEntry<bool> RumbleLanding { get; }

		internal ConfigEntry<bool> RumbleShip { get; }

		internal ConfigEntry<bool> RumbleItems { get; }

		internal ConfigEntry<bool> RumbleJetpack { get; }

		internal ConfigEntry<bool> RumbleCruiser { get; }

		internal ConfigEntry<bool> RumbleFootsteps { get; }

		internal ConfigEntry<RumbleFlagMode> RumbleFlags { get; }

		internal ConfigEntry<bool> RumbleFear { get; }

		internal ConfigEntry<bool> RumbleFearJolt { get; }

		internal ConfigEntry<bool> RumbleInsanity { get; }

		internal ConfigEntry<bool> RumbleWalkie { get; }

		internal ConfigEntry<bool> RumbleWalkieStatic { get; }

		internal ConfigEntry<float> WalkieStaticStrength { get; }

		internal ConfigEntry<bool> StaticFollowsVoice { get; }

		internal ConfigEntry<bool> RumbleEarsRinging { get; }

		internal ConfigEntry<bool> GhostGirlHeartbeat { get; }

		internal ConfigEntry<bool> MicLedOnTransmit { get; }

		internal ConfigEntry<bool> TriggersEnabled { get; }

		internal ConfigEntry<float> TriggerStrength { get; }

		internal ConfigEntry<CruiserTriggerMode> CruiserTriggers { get; }

		internal ConfigEntry<bool> JetpackTrigger { get; }

		internal ConfigEntry<bool> MeleeTrigger { get; }

		internal ConfigEntry<LightbarMode> LightbarMode { get; }

		internal ConfigEntry<string> StaticColor { get; }

		internal ConfigEntry<float> LightbarBrightness { get; }

		internal ConfigEntry<bool> CriticalPulse { get; }

		internal ConfigEntry<bool> JetpackBatteryLight { get; }

		internal ConfigEntry<bool> BluetoothEnabled { get; }

		internal ConfigEntry<float> BluetoothKeepalive { get; }

		internal ConfigEntry<BtLayout> BluetoothLayout { get; }

		internal ConfigEntry<bool> ShowSteamInputHint { get; }

		internal GyroConfig Gyro { get; }

		internal event Action? Changed;

		internal PluginConfig(ConfigFile file)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Expected O, but got Unknown
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Expected O, but got Unknown
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Expected O, but got Unknown
			//IL_049f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a9: Expected O, but got Unknown
			//IL_0504: Unknown result type (might be due to invalid IL or missing references)
			//IL_050e: Expected O, but got Unknown
			_file = file;
			Enabled = file.Bind<bool>("General", "Enabled", true, "Master switch. Off = motors stop, triggers released, lightbar left alone.");
			Backend = file.Bind<BackendMode>("General", "Backend", BackendMode.Auto, "Auto: DualSense raw HID (rumble + adaptive triggers + lightbar) when the game sees the real pad (Steam Input off), otherwise rumble only through the generic gamepad API (XInput / Steam Input / other pads). FallbackOnly: never write HID reports. Off: do nothing. A DualSense that gets the fallback (FallbackOnly, Bluetooth with [Bluetooth] Enabled = false, or an unrecognised HID report layout) can go dark: Unity's own rumble command also sets the lightbar, to black when nothing set a colour. If a DualSense is connected but you play with an Xbox pad, use FallbackOnly (Auto keeps driving the DualSense, because Steam Input and DS4Windows mirror it as a virtual Xbox pad).");
			Intensity = file.Bind<float>("General", "Rumble intensity", 0.8f, new ConfigDescription("Global rumble strength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			DebugLog = file.Bind<bool>("General", "Debug log", false, "Extra log lines (backend decisions, send failures, and a 'tune:' line once a second while driving or holding a jetpack).");
			RumbleCameraShake = file.Bind<bool>("Rumble", "Camera shake", true, "Rumble whenever the game shakes the camera (mines, shotgun, Old Birds, worm, ship travel, lever...).");
			RumbleDamage = file.Bind<bool>("Rumble", "Damage", true, "Pulse scaled by damage; fall and impact damage is a heavy thud.");
			RumbleDeath = file.Bind<bool>("Rumble", "Death", true, "Long pulse when you die.");
			RumbleHeartbeat = file.Bind<bool>("Rumble", "Heartbeat", true, "Heartbeat while critically injured.");
			RumbleLanding = file.Bind<bool>("Rumble", "Landing", true, "Thump on hard landings.");
			RumbleShip = file.Bind<bool>("Rumble", "Ship", true, "Ship landing, take-off and the firing sequence.");
			RumbleItems = file.Bind<bool>("Rumble", "Items", true, "Shovel / knife swings and hits, shotgun recoil.");
			RumbleJetpack = file.Bind<bool>("Rumble", "Jetpack", true, "Continuous rumble proportional to jetpack thrust.");
			RumbleCruiser = file.Bind<bool>("Rumble", "Cruiser", true, "Engine rumble, ignition, gear shifts, turbo, destruction.");
			RumbleFootsteps = file.Bind<bool>("Rumble", "Footsteps", false, "Very light tick on every footstep.");
			RumbleFear = file.Bind<bool>("Rumble", "Fear", true, "Heartbeat in step with the game's own fear heartbeat sound (enemies, scares; also drunkenness and poison). Takes over from the critical-injury heartbeat (one heartbeat, never two), 1.2x stronger while critically injured.");
			RumbleFearJolt = file.Bind<bool>("Rumble", "Fear jolt", false, "Sharp thud when your fear level jumps (a scare). Needs 'Fear'.");
			RumbleInsanity = file.Bind<bool>("Rumble", "Insanity", false, "Faint slow heartbeat on the heavy motor when you have been alone inside the facility for a while (insanity >= 60%).");
			RumbleWalkie = file.Bind<bool>("Rumble", "Walkie", true, "Walkie-talkie: crackle when someone keys up, squelch when they stop, click on your own push-to-talk.");
			RumbleWalkieStatic = file.Bind<bool>("Rumble", "Walkie static", true, "Faint random static while you hear someone on your walkie. Needs 'Walkie'.");
			WalkieStaticStrength = file.Bind<float>("Rumble", "Walkie static strength", 0.5f, new ConfigDescription("0.5 = default level, 1 = twice as strong.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			StaticFollowsVoice = file.Bind<bool>("Rumble", "Static follows voice", false, "Modulate the walkie static by the speaker's voice volume (the voice level range is UNVERIFIED, hence off by default).");
			RumbleEarsRinging = file.Bind<bool>("Rumble", "Ears ringing", true, "High-frequency buzz that fades with the ringing ears (stun grenade, shotgun, Butler...).");
			GhostGirlHeartbeat = file.Bind<bool>("Rumble", "Ghost girl heartbeat", true, "When the ghost girl stares at you (you are the haunted player), her heartbeat music drives the fear heartbeat. Needs 'Fear'.");
			RumbleFlags = file.Bind<RumbleFlagMode>("Rumble", "Rumble report flags", RumbleFlagMode.Classic, "HID mode only. Classic = the flags Unity itself uses. V2 = 'compatible vibration 2' (Linux driver uses it for firmware >= 2.21). Switch to V2 only if rumble does nothing in HID mode.");
			TriggersEnabled = file.Bind<bool>("Adaptive triggers", "Enabled", true, "Adaptive trigger effects (HID mode only). Triggers are always released on death, menus, quit.");
			TriggerStrength = file.Bind<float>("Adaptive triggers", "Strength", 1f, new ConfigDescription("Scales every trigger effect.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			CruiserTriggers = file.Bind<CruiserTriggerMode>("Adaptive triggers", "Cruiser triggers", CruiserTriggerMode.Auto, "Gas = resistance rising with RPM (idle buzz while standing), brake = resistance from mid travel (+ vibration when braking hard). This mod never changes how the Cruiser is driven: BetterVehicleControls binds Gas/Brake to R2/L2 by default (vanilla has no trigger driving; L2 is Sprint). Auto = on only when BetterVehicleControls is loaded and its Gas or Brake is bound to a trigger, following your InputUtils rebinds.");
			JetpackTrigger = file.Bind<bool>("Adaptive triggers", "Jetpack", true, "Use-item trigger vibrates with jetpack thrust while a jetpack is held.");
			MeleeTrigger = file.Bind<bool>("Adaptive triggers", "Melee", true, "Use-item trigger gets a 'click' while a shovel or knife is held.");
			LightbarMode = file.Bind<LightbarMode>("Lightbar", "Mode", DualSenseHaptics.Core.LightbarMode.Health, "Off = untouched. Health = green to red with your health (red pulse when critically injured, fades out on death). CruiserHp = Cruiser health while in it, else Health. Static = the colour below.");
			StaticColor = file.Bind<string>("Lightbar", "Static colour", "#0050FF", "Static mode / main menu colour: #RRGGBB or R,G,B.");
			LightbarBrightness = file.Bind<float>("Lightbar", "Brightness", 1f, new ConfigDescription("Multiplies the lightbar colour.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			CriticalPulse = file.Bind<bool>("Lightbar", "Critical pulse", true, "Pulse red while critically injured.");
			JetpackBatteryLight = file.Bind<bool>("Lightbar", "Jetpack battery", true, "Show the held jetpack's battery (blue = full, red = empty).");
			MicLedOnTransmit = file.Bind<bool>("Lightbar", "Mic LED on walkie transmit", true, "HID mode: the mic mute LED (under the touchpad) is on while you speak into a walkie, off otherwise.");
			BluetoothEnabled = file.Bind<bool>("Bluetooth", "Enabled", true, "Drive a Bluetooth DualSense with our own 0x31 reports (CRC32). UNVERIFIED under Proton/Wine: turn off if it misbehaves.");
			BluetoothKeepalive = file.Bind<float>("Bluetooth", "Keepalive seconds", 1f, new ConfigDescription("Resend the full state this often over Bluetooth even without changes (0 = never).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			BluetoothLayout = file.Bind<BtLayout>("Bluetooth", "Report layout", BtLayout.Kernel, "Kernel = [31][seq][10][data] (Linux hid-playstation, default). Compact = [31][02][data] (pydualsense/SDL). Try Compact if nothing happens over Bluetooth (test with tools/hidtest/ds_report.py --bt-layout).");
			ShowSteamInputHint = file.Bind<bool>("Hints", "Show Steam Input hint", true, "Show the 'disable Steam Input' tip once when Steam Input is detected (Steam's virtual pad, or a DualSense mirrored as an Xbox pad). Turned off automatically after it was shown.");
			ConfigVersion = file.Bind<int>("General", "Config version", 1, new ConfigDescription("Internal marker for default migrations; leave it alone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 2), Array.Empty<object>()));
			if (ConfigVersion.Value < 2)
			{
				if (!RumbleFear.Value)
				{
					RumbleFear.Value = true;
				}
				if (RumbleInsanity.Value)
				{
					RumbleInsanity.Value = false;
				}
				ConfigVersion.Value = 2;
			}
			Gyro = new GyroConfig(file);
			file.SettingChanged += delegate
			{
				try
				{
					this.Changed?.Invoke();
				}
				catch (Exception ex)
				{
					Log.Error("config change handler: " + ex);
				}
			};
		}

		internal HapticsSettings ToSettings(TriggerSide useItemSide)
		{
			HapticsSettings hapticsSettings = new HapticsSettings
			{
				Enabled = (Enabled.Value && Backend.Value != BackendMode.Off),
				Intensity = Intensity.Value,
				RumbleMode = RumbleFlags.Value,
				TriggersEnabled = TriggersEnabled.Value,
				TriggerStrength = TriggerStrength.Value,
				MeleeTrigger = MeleeTrigger.Value,
				JetpackTrigger = JetpackTrigger.Value,
				UseItemSide = useItemSide,
				Lightbar = LightbarMode.Value,
				LightbarBrightness = LightbarBrightness.Value,
				CriticalPulse = CriticalPulse.Value,
				JetpackBatteryLight = JetpackBatteryLight.Value,
				FearJolt = RumbleFearJolt.Value,
				WalkieStatic = RumbleWalkieStatic.Value,
				WalkieStaticStrength = WalkieStaticStrength.Value,
				StaticFollowsVoice = StaticFollowsVoice.Value,
				MicLedOnTransmit = MicLedOnTransmit.Value,
				GhostGirlHeartbeat = GhostGirlHeartbeat.Value,
				GyroLedIndicator = Gyro.LedIndicator.Value
			};
			if (Rgb.TryParse(StaticColor.Value, out var value))
			{
				hapticsSettings.StaticColor = value;
			}
			else
			{
				Log.WarnOnce("cfg.StaticColor", "Lightbar 'Static colour' '" + StaticColor.Value + "' is not #RRGGBB or R,G,B; using " + hapticsSettings.StaticColor);
			}
			hapticsSettings.SetCategory(HapticCategory.CameraShake, RumbleCameraShake.Value);
			hapticsSettings.SetCategory(HapticCategory.Damage, RumbleDamage.Value);
			hapticsSettings.SetCategory(HapticCategory.Death, RumbleDeath.Value);
			hapticsSettings.SetCategory(HapticCategory.Heartbeat, RumbleHeartbeat.Value);
			hapticsSettings.SetCategory(HapticCategory.Landing, RumbleLanding.Value);
			hapticsSettings.SetCategory(HapticCategory.Ship, RumbleShip.Value);
			hapticsSettings.SetCategory(HapticCategory.Items, RumbleItems.Value);
			hapticsSettings.SetCategory(HapticCategory.Jetpack, RumbleJetpack.Value);
			hapticsSettings.SetCategory(HapticCategory.Cruiser, RumbleCruiser.Value);
			hapticsSettings.SetCategory(HapticCategory.Footsteps, RumbleFootsteps.Value);
			hapticsSettings.SetCategory(HapticCategory.Fear, RumbleFear.Value);
			hapticsSettings.SetCategory(HapticCategory.Insanity, RumbleInsanity.Value);
			hapticsSettings.SetCategory(HapticCategory.Walkie, RumbleWalkie.Value);
			hapticsSettings.SetCategory(HapticCategory.Ears, RumbleEarsRinging.Value);
			hapticsSettings.SetCategory(HapticCategory.Feedback, Gyro.ToggleRumbleTick.Value);
			return hapticsSettings;
		}

		internal void MarkHintShown()
		{
			ShowSteamInputHint.Value = false;
			try
			{
				_file.Save();
			}
			catch (Exception ex)
			{
				Log.Warn("could not save config: " + ex.Message);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "dev.thatumi.DualSenseHaptics";

		public const string PLUGIN_NAME = "DualSenseHaptics";

		public const string PLUGIN_VERSION = "0.2.3";
	}
}
namespace DualSenseHaptics.Core
{
	public static class Crc32
	{
		public const uint Polynomial = 3988292384u;

		private static readonly uint[] Table = BuildTable();

		public const uint Start = uint.MaxValue;

		public static uint Compute(byte[] data, int offset, int count)
		{
			return Finish(Update(uint.MaxValue, data, offset, count));
		}

		public static uint Compute(byte[] data)
		{
			return Compute(data, 0, data.Length);
		}

		public static uint ComputeSeeded(byte seed, byte[] data, int offset, int count)
		{
			return Finish(Update(UpdateByte(uint.MaxValue, seed), data, offset, count));
		}

		public static uint Finish(uint crc)
		{
			return ~crc;
		}

		public static uint UpdateByte(uint crc, byte b)
		{
			return Table[(crc ^ b) & 0xFF] ^ (crc >> 8);
		}

		public static uint Update(uint crc, byte[] data, int offset, int count)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (offset < 0 || count < 0 || offset + count > data.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			for (int i = offset; i < offset + count; i++)
			{
				crc = Table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
			}
			return crc;
		}

		private static uint[] BuildTable()
		{
			uint[] array = new uint[256];
			for (uint num = 0u; num < 256; num++)
			{
				uint num2 = num;
				for (int i = 0; i < 8; i++)
				{
					num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
				}
				array[num] = num2;
			}
			return array;
		}
	}
	public enum CruiserTriggerMode
	{
		Auto,
		On,
		Off
	}
	public readonly struct PedalBindings
	{
		public bool Found { get; }

		public TriggerSide? Gas { get; }

		public TriggerSide? Brake { get; }

		public bool AnyOnTrigger
		{
			get
			{
				if (!Gas.HasValue)
				{
					return Brake.HasValue;
				}
				return true;
			}
		}

		public PedalBindings(bool found, TriggerSide? gas, TriggerSide? brake)
		{
			Found = found;
			Gas = gas;
			Brake = brake;
		}

		public override string ToString()
		{
			if (!Found)
			{
				return "BVC actions not found";
			}
			return "gas=" + (Gas?.ToString() ?? "not on a trigger") + " brake=" + (Brake?.ToString() ?? "not on a trigger");
		}
	}
	public readonly struct CruiserTriggerDecision
	{
		public bool Enabled { get; }

		public TriggerSide GasSide { get; }

		public TriggerSide BrakeSide { get; }

		public string Reason { get; }

		public CruiserTriggerDecision(bool enabled, TriggerSide gasSide, TriggerSide brakeSide, string reason)
		{
			Enabled = enabled;
			GasSide = gasSide;
			BrakeSide = brakeSide;
			Reason = reason;
		}

		public override string ToString()
		{
			return (Enabled ? "on" : "off") + " (gas=" + GasSide.ToString() + " brake=" + BrakeSide.ToString() + "): " + Reason;
		}
	}
	public static class CruiserTriggerPolicy
	{
		public const string BvcGuid = "uk.1a3.bettervehiclecontrols";

		public const string BvcActionMapPrefix = "uk.1a3.bettervehiclecontrols";

		public const string GasAction = "GasPedalKey";

		public const string BrakeAction = "BrakePedalKey";

		public static CruiserTriggerDecision Decide(CruiserTriggerMode mode, bool bvcLoaded, PedalBindings? bindings)
		{
			TriggerSide triggerSide = (TriggerSide)(((int?)bindings?.Gas) ?? ((bindings.HasValue && bindings.GetValueOrDefault().Brake == TriggerSide.Right) ? 1 : 0));
			TriggerSide triggerSide2 = (TriggerSide)(((int?)bindings?.Brake) ?? ((triggerSide == TriggerSide.Right) ? 1 : 0));
			if (triggerSide == triggerSide2)
			{
				triggerSide2 = ((triggerSide == TriggerSide.Right) ? TriggerSide.Left : TriggerSide.Right);
			}
			switch (mode)
			{
			case CruiserTriggerMode.Off:
				return new CruiserTriggerDecision(enabled: false, triggerSide, triggerSide2, "config: Off");
			case CruiserTriggerMode.On:
				return new CruiserTriggerDecision(enabled: true, triggerSide, triggerSide2, "config: On");
			default:
				if (!bvcLoaded)
				{
					return new CruiserTriggerDecision(enabled: false, triggerSide, triggerSide2, "Auto: BetterVehicleControls not loaded (vanilla gas/brake are on the left stick)");
				}
				if (!bindings.HasValue || !bindings.Value.Found)
				{
					return new CruiserTriggerDecision(enabled: true, triggerSide, triggerSide2, "Auto: BetterVehicleControls loaded, its bindings could not be read; assuming triggers");
				}
				if (!bindings.Value.AnyOnTrigger)
				{
					return new CruiserTriggerDecision(enabled: false, triggerSide, triggerSide2, "Auto: " + bindings.Value);
				}
				return new CruiserTriggerDecision(enabled: true, triggerSide, triggerSide2, "Auto: " + bindings.Value);
			}
		}

		public static PedalBindings Scan(IEnumerable<(string map, string action, string path)> bindings)
		{
			if (bindings == null)
			{
				throw new ArgumentNullException("bindings");
			}
			bool found = false;
			TriggerSide? gas = null;
			TriggerSide? brake = null;
			foreach (var (text, a, path) in bindings)
			{
				if (text == null || !text.StartsWith("uk.1a3.bettervehiclecontrols", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				bool flag = string.Equals(a, "GasPedalKey", StringComparison.Ordinal);
				bool flag2 = string.Equals(a, "BrakePedalKey", StringComparison.Ordinal);
				if (!flag && !flag2)
				{
					continue;
				}
				found = true;
				TriggerSide? triggerSide = BindingPaths.TriggerSideOf(path);
				if (triggerSide.HasValue)
				{
					if (flag && !gas.HasValue)
					{
						gas = triggerSide;
					}
					if (flag2 && !brake.HasValue)
					{
						brake = triggerSide;
					}
				}
			}
			return new PedalBindings(found, gas, brake);
		}
	}
	public static class BindingPaths
	{
		public static TriggerSide? TriggerSideOf(string? path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return null;
			}
			if (path.IndexOf("Not-Bound", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return null;
			}
			int num = path.LastIndexOf('/');
			string text = ((num >= 0) ? path.Substring(num + 1) : path);
			if (text.StartsWith("rightTrigger", StringComparison.OrdinalIgnoreCase))
			{
				return TriggerSide.Right;
			}
			if (text.StartsWith("leftTrigger", StringComparison.OrdinalIgnoreCase))
			{
				return TriggerSide.Left;
			}
			return null;
		}
	}
	public enum BtLayout
	{
		Kernel,
		Compact
	}
	public static class DsReportBuilder
	{
		public const byte UsbReportId = 2;

		public const byte BtReportId = 49;

		public const byte BtTag = 16;

		public const byte BtCrcSeed = 162;

		public const int CommonSize = 47;

		public const int UsbCommonOffset = 1;

		public const int BtCommonOffset = 3;

		public const int BtCompactCommonOffset = 2;

		public const byte BtCompactTag = 2;

		public const int UsbMinSize = 48;

		public const int UsbKernelSize = 63;

		public const int BtSize = 78;

		public const int BtCrcOffset = 74;

		public const int MaxReportSize = 1024;

		public const int CommonFlag0 = 0;

		public const int CommonFlag1 = 1;

		public const int CommonMotorHigh = 2;

		public const int CommonMotorLow = 3;

		public const int CommonMuteButtonLed = 8;

		public const int CommonRightTrigger = 10;

		public const int CommonLeftTrigger = 21;

		public const int CommonFlag2 = 38;

		public const int CommonLightbarSetup = 41;

		public const int CommonLedBrightness = 42;

		public const int CommonPlayerLeds = 43;

		public const int CommonRed = 44;

		public const int CommonGreen = 45;

		public const int CommonBlue = 46;

		public const byte Flag0CompatibleVibration = 1;

		public const byte Flag0HapticsSelect = 2;

		public const byte Flag0RightTriggerEffect = 4;

		public const byte Flag0LeftTriggerEffect = 8;

		public const byte Flag1MicMuteLed = 1;

		public const byte Flag1PowerSave = 2;

		public const byte Flag1Lightbar = 4;

		public const byte Flag1ReleaseLeds = 8;

		public const byte Flag1PlayerLeds = 16;

		public const byte Flag2LedBrightness = 1;

		public const byte Flag2LightbarSetup = 2;

		public const byte Flag2CompatibleVibration2 = 4;

		public const byte LightbarSetupLightOut = 2;

		public static void WriteCommon(DsState state, byte[] dest, int offset)
		{
			if (state == null)
			{
				throw new ArgumentNullException("state");
			}
			if (dest == null)
			{
				throw new Argum