Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DualSenseHaptics v0.2.3
plugins/DualSenseHaptics/DualSenseHaptics.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
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