using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using HarmonyLib; using Il2CppSLZ.Marrow; using MelonLoader; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.XR; using ViveWandThumbFix; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(ThumbFixMod), "Vive Wand Thumb Fix", "1.0.1", "Codex", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ViveWandThumbFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ViveWandThumbFix")] [assembly: AssemblyTitle("ViveWandThumbFix")] [assembly: AssemblyVersion("1.0.0.0")] 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; } } } namespace ViveWandThumbFix { public sealed class ThumbFixMod : MelonMod { public override void OnInitializeMelon() { ((MelonBase)this).LoggerInstance.Msg("Active. Vive Wand thumb curl now follows the physical trackpad touch state."); } } internal static class ViveTouchReader { private const float AxisFallbackDeadzone = 0.075f; private static readonly InputFeatureUsage TouchpadTouchUsage = new InputFeatureUsage("TouchpadTouch"); private static readonly InputFeatureUsage TrackpadTouchUsage = new InputFeatureUsage("TrackpadTouch"); private static bool _announcedLeft; private static bool _announcedRight; private static bool _warnedLeft; private static bool _warnedRight; internal static bool IsViveWand(OpenController controller) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 return (int)((BaseController)controller).Type == 2; } internal static bool TryGetTouch(OpenController controller, out bool touched) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) touched = false; bool flag = (int)((BaseController)controller).handedness == 1; bool flag2 = (int)((BaseController)controller).handedness == 2; if (!flag && !flag2) { return false; } InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode((XRNode)(flag ? 4 : 5)); AnnounceDevice(flag, deviceAtXRNode); if (((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.primary2DAxisTouch, ref touched)) { return true; } if (((InputDevice)(ref deviceAtXRNode)).isValid && (((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(TouchpadTouchUsage, ref touched) || ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(TrackpadTouchUsage, ref touched))) { return true; } Vector2 val = default(Vector2); if (((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.primary2DAxis, ref val)) { bool flag4 = default(bool); bool flag3 = ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.primary2DAxisClick, ref flag4) && flag4; touched = flag3 || ((Vector2)(ref val)).sqrMagnitude > 0.005625f; WarnAboutFallback(flag); return true; } WarnAboutFallback(flag); touched = false; return true; } internal static void Apply(OpenController controller, bool touched) { float simThumbAxis = (((BaseController)controller)._processedThumb = (touched ? 1f : 0f)); ((BaseController)controller)._simThumbAxis = simThumbAxis; ((BaseController)controller)._isThumbTouch = touched; ((BaseController)controller)._touchPadTouch = touched; } private static void AnnounceDevice(bool left, InputDevice device) { if (!(left ? _announcedLeft : _announcedRight)) { if (left) { _announcedLeft = true; } else { _announcedRight = true; } MelonLogger.Msg($"Vive Wand {(left ? "left" : "right")} detected: {(((InputDevice)(ref device)).isValid ? ((InputDevice)(ref device)).name : "XR device not ready yet")}."); } } private static void WarnAboutFallback(bool left) { if (!(left ? _warnedLeft : _warnedRight)) { if (left) { _warnedLeft = true; } else { _warnedRight = true; } MelonLogger.Warning("No direct trackpad-touch feature for the " + (left ? "left" : "right") + " Wand; using axis/click fallback."); } } } [HarmonyPatch(typeof(OpenController), "OnUpdate")] internal static class OpenControllerUpdatePatch { [HarmonyPostfix] private static void Postfix(OpenController __instance) { if (ViveTouchReader.IsViveWand(__instance) && ViveTouchReader.TryGetTouch(__instance, out var touched)) { ViveTouchReader.Apply(__instance, touched); } } } [HarmonyPatch(typeof(OpenController), "GetThumbCurlAxis")] internal static class ThumbCurlGetterPatch { [HarmonyPostfix] private static void Postfix(OpenController __instance, ref float __result) { if (ViveTouchReader.IsViveWand(__instance) && ViveTouchReader.TryGetTouch(__instance, out var touched)) { __result = (touched ? 1f : 0f); } } } [HarmonyPatch(typeof(OpenController), "GetThumbTouch")] internal static class ThumbTouchGetterPatch { [HarmonyPostfix] private static void Postfix(OpenController __instance, ref bool __result) { if (ViveTouchReader.IsViveWand(__instance) && ViveTouchReader.TryGetTouch(__instance, out var touched)) { __result = touched; } } } }