Decompiled source of PSVR2 v1.1.0

Mods/PSVR2.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Sockets;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BoneLib;
using BoneLib.BoneMenu;
using EyeTracking;
using EyeTracking.TrackingProviders;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using PSVR2;
using PSVR2.BoneMenu;
using PSVR2.Features;
using PSVR2.PSVR2ToolKit;
using PSVR2.UserData;
using PSVR2.Utilities;
using PSVR2Toolkit.CAPI;
using PSVR2Toolkit.Utilities;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "PSVR2", "0.0.1", "Checkerboard", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "EyeTracking" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("PSVR2")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3795be138e366dd84cf08e39d4e190d23b7aeb1c")]
[assembly: AssemblyProduct("PSVR2")]
[assembly: AssemblyTitle("PSVR2")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 PSVR2Toolkit.Utilities
{
	public class LowPassFilter
	{
		private readonly float[] _samples;

		private int _index;

		public LowPassFilter(int count)
		{
			_samples = new float[count - 1];
			for (int i = 0; i < count - 1; i++)
			{
				_samples[i] = 0f;
			}
		}

		private float Sum()
		{
			float num = 0f;
			float[] samples = _samples;
			foreach (float num2 in samples)
			{
				num += num2;
			}
			return num;
		}

		public float FilterValue(float newValue)
		{
			_index++;
			if (_samples.Length == _index)
			{
				_index = 0;
			}
			_samples[_index] = newValue;
			return Sum() / (float)_samples.Length;
		}
	}
}
namespace PSVR2Toolkit.CAPI
{
	public class IpcClient
	{
		private const ushort IPC_SERVER_PORT = 3364;

		private const ushort k_unIpcVersion = 2;

		private static IpcClient m_pInstance;

		private bool m_running;

		private TcpClient m_client;

		private NetworkStream m_stream;

		private Thread m_receiveThread;

		private readonly object m_gazeStateLock = new object();

		private TaskCompletionSource<CommandDataServerGazeDataResult> m_gazeTask;

		private CancellationTokenSource m_forceShutdownToken;

		private ushort m_serverIpcVersion = 2;

		private int m_gazePumpPeriodMs = 8;

		private CommandDataServerGazeDataResult2? m_lastGazeState;

		public bool IsRunning => m_running;

		public static IpcClient Instance()
		{
			if (m_pInstance == null)
			{
				m_pInstance = new IpcClient();
			}
			return m_pInstance;
		}

		public bool Start()
		{
			if (m_running)
			{
				return false;
			}
			try
			{
				m_client = new TcpClient();
				m_client.Connect("127.0.0.1", 3364);
				if (m_client.Connected)
				{
					m_stream = m_client.GetStream();
					m_running = true;
					m_forceShutdownToken = new CancellationTokenSource();
					m_receiveThread = new Thread((ThreadStart)delegate
					{
						ReceiveLoop(m_forceShutdownToken.Token);
					});
					m_receiveThread.Start();
					return true;
				}
				return false;
			}
			catch (SocketException ex)
			{
				Console.WriteLine($"[IPC_CLIENT] Connection failed. LastError = {ex.SocketErrorCode}");
				return false;
			}
		}

		public void Stop()
		{
			if (m_running)
			{
				m_running = false;
				m_forceShutdownToken.Cancel();
				lock (m_gazeStateLock)
				{
					m_gazeTask?.TrySetCanceled();
					m_gazeTask = null;
				}
				try
				{
					m_stream?.Close();
					m_client?.Close();
				}
				catch
				{
				}
				if (m_receiveThread != null && m_receiveThread.IsAlive && !m_receiveThread.Join(2000))
				{
					m_receiveThread.Interrupt();
				}
				m_stream?.Dispose();
				m_client?.Dispose();
				m_forceShutdownToken.Dispose();
			}
		}

		private void ReceiveLoop(CancellationToken token)
		{
			byte[] array = new byte[1024];
			try
			{
				Socket client = m_client.Client;
				m_stream.ReadTimeout = 1;
				CommandDataClientRequestHandshake commandDataClientRequestHandshake = default(CommandDataClientRequestHandshake);
				commandDataClientRequestHandshake.ipcVersion = 2;
				commandDataClientRequestHandshake.processId = (uint)Process.GetCurrentProcess().Id;
				CommandDataClientRequestHandshake data = commandDataClientRequestHandshake;
				SendIpcCommand(ECommandType.ClientRequestHandshake, data);
				Stopwatch stopwatch = Stopwatch.StartNew();
				long num = stopwatch.ElapsedMilliseconds;
				while (m_running && !token.IsCancellationRequested)
				{
					long elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
					if (elapsedMilliseconds >= num)
					{
						SendIpcCommand(ECommandType.ClientRequestGazeData);
						num = elapsedMilliseconds + m_gazePumpPeriodMs;
					}
					if (client.Poll(1000, SelectMode.SelectRead) && client.Available > 0)
					{
						int available = client.Available;
						if (available > array.Length)
						{
							array = new byte[Math.Max(available, array.Length * 2)];
						}
						int num2 = m_stream.Read(array, 0, Math.Min(array.Length, available));
						if (num2 <= 0)
						{
							Console.WriteLine("[IPC_CLIENT] Disconnected from server.");
							break;
						}
						if (num2 < Marshal.SizeOf<CommandHeader>())
						{
							Console.WriteLine("[IPC_CLIENT] Received invalid command header size.");
						}
						else
						{
							HandleIpcCommand(array, num2);
						}
					}
					else
					{
						Thread.Sleep(1);
					}
				}
			}
			catch (OperationCanceledException)
			{
			}
			catch (Exception ex2)
			{
				if (m_running)
				{
					Console.WriteLine("[IPC_CLIENT] Error in receive loop: " + ex2.Message);
				}
			}
		}

		private GazeEyeResult2 UpgradeGazeEyeResult(GazeEyeResult eye)
		{
			GazeEyeResult2 result = default(GazeEyeResult2);
			result.isGazeOriginValid = eye.isGazeOriginValid;
			result.gazeOriginMm = eye.gazeOriginMm;
			result.isGazeDirValid = eye.isGazeDirValid;
			result.gazeDirNorm = eye.gazeDirNorm;
			result.isPupilDiaValid = eye.isPupilDiaValid;
			result.pupilDiaMm = eye.pupilDiaMm;
			result.isBlinkValid = eye.isBlinkValid;
			result.blink = eye.blink;
			result.isOpenEnabled = false;
			result.open = 0f;
			return result;
		}

		private CommandDataServerGazeDataResult2 UpgradeGazeDataResult(CommandDataServerGazeDataResult result)
		{
			CommandDataServerGazeDataResult2 result2 = default(CommandDataServerGazeDataResult2);
			result2.leftEye = UpgradeGazeEyeResult(result.leftEye);
			result2.rightEye = UpgradeGazeEyeResult(result.rightEye);
			return result2;
		}

		private void HandleIpcCommand(byte[] pBuffer, int bytesReceived)
		{
			CommandHeader commandHeader = ByteArrayToStructure<CommandHeader>(pBuffer, 0);
			switch (commandHeader.type)
			{
			case ECommandType.ServerPong:
				Console.WriteLine("[IPC_CLIENT] Received Pong from server.");
				break;
			case ECommandType.ServerHandshakeResult:
				if (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerHandshakeResult>())
				{
					CommandDataServerHandshakeResult commandDataServerHandshakeResult = ByteArrayToStructure<CommandDataServerHandshakeResult>(pBuffer, Marshal.SizeOf<CommandHeader>());
					m_serverIpcVersion = commandDataServerHandshakeResult.ipcVersion;
					switch (commandDataServerHandshakeResult.result)
					{
					case EHandshakeResult.Success:
						Console.WriteLine("[IPC_CLIENT] Handshake successful!");
						break;
					case EHandshakeResult.Failed:
						Console.WriteLine("[IPC_CLIENT] Handshake failed!");
						break;
					case EHandshakeResult.Outdated:
						Console.WriteLine($"[IPC_CLIENT] Handshake failed with reason: Outdated client. Please upgrade to an IPC version of {commandDataServerHandshakeResult.ipcVersion}");
						break;
					}
				}
				break;
			case ECommandType.ServerGazeDataResult:
				if (m_serverIpcVersion == 1)
				{
					if (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerGazeDataResult>())
					{
						CommandDataServerGazeDataResult result = ByteArrayToStructure<CommandDataServerGazeDataResult>(pBuffer, Marshal.SizeOf<CommandHeader>());
						m_lastGazeState = UpgradeGazeDataResult(result);
					}
				}
				else if (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerGazeDataResult2>())
				{
					CommandDataServerGazeDataResult2 value = ByteArrayToStructure<CommandDataServerGazeDataResult2>(pBuffer, Marshal.SizeOf<CommandHeader>());
					m_lastGazeState = value;
				}
				break;
			case ECommandType.ClientRequestHandshake:
			case ECommandType.ClientRequestGazeData:
				break;
			}
		}

		private void SendIpcCommand<T>(ECommandType type, T data = default(T)) where T : struct
		{
			if (m_running)
			{
				int num = ((!data.Equals(default(T))) ? Marshal.SizeOf<T>() : 0);
				byte[] array = new byte[Marshal.SizeOf<CommandHeader>() + num];
				CommandHeader commandHeader = default(CommandHeader);
				commandHeader.type = type;
				commandHeader.dataLen = num;
				CommandHeader structure = commandHeader;
				IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf<CommandHeader>());
				Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
				Marshal.Copy(intPtr, array, 0, Marshal.SizeOf<CommandHeader>());
				Marshal.FreeHGlobal(intPtr);
				if (num > 0)
				{
					IntPtr intPtr2 = Marshal.AllocHGlobal(num);
					Marshal.StructureToPtr(data, intPtr2, fDeleteOld: false);
					Marshal.Copy(intPtr2, array, Marshal.SizeOf<CommandHeader>(), num);
					Marshal.FreeHGlobal(intPtr2);
				}
				m_stream.Write(array, 0, array.Length);
			}
		}

		private void SendIpcCommand(ECommandType type)
		{
			if (m_running)
			{
				byte[] array = new byte[Marshal.SizeOf<CommandHeader>()];
				CommandHeader commandHeader = default(CommandHeader);
				commandHeader.type = type;
				commandHeader.dataLen = 0;
				CommandHeader structure = commandHeader;
				IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf<CommandHeader>());
				Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
				Marshal.Copy(intPtr, array, 0, Marshal.SizeOf<CommandHeader>());
				Marshal.FreeHGlobal(intPtr);
				m_stream.Write(array, 0, array.Length);
			}
		}

		private T ByteArrayToStructure<T>(byte[] bytes, int offset) where T : struct
		{
			int num = Marshal.SizeOf<T>();
			if (num > bytes.Length - offset)
			{
				throw new ArgumentException("Byte array is too small to contain the structure.");
			}
			IntPtr intPtr = Marshal.AllocHGlobal(num);
			Marshal.Copy(bytes, offset, intPtr, num);
			T result = (T)Marshal.PtrToStructure(intPtr, typeof(T));
			Marshal.FreeHGlobal(intPtr);
			return result;
		}

		public CommandDataServerGazeDataResult2 RequestEyeTrackingData()
		{
			if (!m_running)
			{
				return default(CommandDataServerGazeDataResult2);
			}
			return m_lastGazeState.GetValueOrDefault();
		}

		public void TriggerEffectDisable(EVRControllerType controllerType)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectOff commandDataClientTriggerEffectOff = default(CommandDataClientTriggerEffectOff);
				commandDataClientTriggerEffectOff.controllerType = controllerType;
				CommandDataClientTriggerEffectOff data = commandDataClientTriggerEffectOff;
				SendIpcCommand(ECommandType.ClientTriggerEffectOff, data);
			}
		}

		public void TriggerEffectFeedback(EVRControllerType controllerType, byte position, byte strength)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectFeedback commandDataClientTriggerEffectFeedback = default(CommandDataClientTriggerEffectFeedback);
				commandDataClientTriggerEffectFeedback.controllerType = controllerType;
				commandDataClientTriggerEffectFeedback.position = position;
				commandDataClientTriggerEffectFeedback.strength = strength;
				CommandDataClientTriggerEffectFeedback data = commandDataClientTriggerEffectFeedback;
				SendIpcCommand(ECommandType.ClientTriggerEffectFeedback, data);
			}
		}

		public void TriggerEffectWeapon(EVRControllerType controllerType, byte startPosition, byte endPosition, byte strength)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectWeapon commandDataClientTriggerEffectWeapon = default(CommandDataClientTriggerEffectWeapon);
				commandDataClientTriggerEffectWeapon.controllerType = controllerType;
				commandDataClientTriggerEffectWeapon.startPosition = startPosition;
				commandDataClientTriggerEffectWeapon.endPosition = endPosition;
				commandDataClientTriggerEffectWeapon.strength = strength;
				CommandDataClientTriggerEffectWeapon data = commandDataClientTriggerEffectWeapon;
				SendIpcCommand(ECommandType.ClientTriggerEffectWeapon, data);
			}
		}

		public void TriggerEffectVibration(EVRControllerType controllerType, byte position, byte amplitude, byte frequency)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectVibration commandDataClientTriggerEffectVibration = default(CommandDataClientTriggerEffectVibration);
				commandDataClientTriggerEffectVibration.controllerType = controllerType;
				commandDataClientTriggerEffectVibration.position = position;
				commandDataClientTriggerEffectVibration.amplitude = amplitude;
				commandDataClientTriggerEffectVibration.frequency = frequency;
				CommandDataClientTriggerEffectVibration data = commandDataClientTriggerEffectVibration;
				SendIpcCommand(ECommandType.ClientTriggerEffectVibration, data);
			}
		}

		public void TriggerEffectMultiplePositionFeedback(EVRControllerType controllerType, byte[] strength)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectMultiplePositionFeedback commandDataClientTriggerEffectMultiplePositionFeedback = default(CommandDataClientTriggerEffectMultiplePositionFeedback);
				commandDataClientTriggerEffectMultiplePositionFeedback.controllerType = controllerType;
				commandDataClientTriggerEffectMultiplePositionFeedback.strength = strength;
				CommandDataClientTriggerEffectMultiplePositionFeedback data = commandDataClientTriggerEffectMultiplePositionFeedback;
				SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionFeedback, data);
			}
		}

		public void TriggerEffectSlopeFeedback(EVRControllerType controllerType, byte startPosition, byte endPosition, byte startStrength, byte endStrength)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectSlopeFeedback commandDataClientTriggerEffectSlopeFeedback = default(CommandDataClientTriggerEffectSlopeFeedback);
				commandDataClientTriggerEffectSlopeFeedback.controllerType = controllerType;
				commandDataClientTriggerEffectSlopeFeedback.startPosition = startPosition;
				commandDataClientTriggerEffectSlopeFeedback.endPosition = endPosition;
				commandDataClientTriggerEffectSlopeFeedback.startStrength = startStrength;
				commandDataClientTriggerEffectSlopeFeedback.endStrength = endStrength;
				CommandDataClientTriggerEffectSlopeFeedback data = commandDataClientTriggerEffectSlopeFeedback;
				SendIpcCommand(ECommandType.ClientTriggerEffectSlopeFeedback, data);
			}
		}

		public void TriggerEffectMultiplePositionVibration(EVRControllerType controllerType, byte frequency, byte[] amplitude)
		{
			if (m_running)
			{
				CommandDataClientTriggerEffectMultiplePositionVibration commandDataClientTriggerEffectMultiplePositionVibration = default(CommandDataClientTriggerEffectMultiplePositionVibration);
				commandDataClientTriggerEffectMultiplePositionVibration.controllerType = controllerType;
				commandDataClientTriggerEffectMultiplePositionVibration.frequency = frequency;
				commandDataClientTriggerEffectMultiplePositionVibration.amplitude = amplitude;
				CommandDataClientTriggerEffectMultiplePositionVibration data = commandDataClientTriggerEffectMultiplePositionVibration;
				SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionVibration, data);
			}
		}
	}
	public enum ECommandType : ushort
	{
		ClientPing,
		ServerPong,
		ClientRequestHandshake,
		ServerHandshakeResult,
		ClientRequestGazeData,
		ServerGazeDataResult,
		ClientTriggerEffectOff,
		ClientTriggerEffectFeedback,
		ClientTriggerEffectWeapon,
		ClientTriggerEffectVibration,
		ClientTriggerEffectMultiplePositionFeedback,
		ClientTriggerEffectSlopeFeedback,
		ClientTriggerEffectMultiplePositionVibration
	}
	public enum EHandshakeResult : byte
	{
		Failed,
		Success,
		Outdated
	}
	public enum EVRControllerType : byte
	{
		Left,
		Right,
		Both
	}
	public struct CommandDataClientRequestHandshake
	{
		public ushort ipcVersion;

		public uint processId;
	}
	public struct CommandDataServerHandshakeResult
	{
		public EHandshakeResult result;

		public ushort ipcVersion;
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct GazeVector3
	{
		public float x;

		public float y;

		public float z;
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct GazeEyeResult
	{
		[MarshalAs(UnmanagedType.I1)]
		public bool isGazeOriginValid;

		public GazeVector3 gazeOriginMm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isGazeDirValid;

		public GazeVector3 gazeDirNorm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isPupilDiaValid;

		public float pupilDiaMm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isBlinkValid;

		[MarshalAs(UnmanagedType.I1)]
		public bool blink;
	}
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct GazeEyeResult2
	{
		[MarshalAs(UnmanagedType.I1)]
		public bool isGazeOriginValid;

		public GazeVector3 gazeOriginMm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isGazeDirValid;

		public GazeVector3 gazeDirNorm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isPupilDiaValid;

		public float pupilDiaMm;

		[MarshalAs(UnmanagedType.I1)]
		public bool isBlinkValid;

		[MarshalAs(UnmanagedType.I1)]
		public bool blink;

		[MarshalAs(UnmanagedType.I1)]
		public bool isOpenEnabled;

		public float open;
	}
	public struct CommandDataServerGazeDataResult
	{
		public GazeEyeResult leftEye;

		public GazeEyeResult rightEye;
	}
	public struct CommandDataServerGazeDataResult2
	{
		public GazeEyeResult2 leftEye;

		public GazeEyeResult2 rightEye;
	}
	public struct CommandHeader
	{
		public ECommandType type;

		public int dataLen;
	}
	public struct CommandDataClientTriggerEffectOff
	{
		public EVRControllerType controllerType;
	}
	public struct CommandDataClientTriggerEffectFeedback
	{
		public EVRControllerType controllerType;

		public byte position;

		public byte strength;
	}
	public struct CommandDataClientTriggerEffectWeapon
	{
		public EVRControllerType controllerType;

		public byte startPosition;

		public byte endPosition;

		public byte strength;
	}
	public struct CommandDataClientTriggerEffectVibration
	{
		public EVRControllerType controllerType;

		public byte position;

		public byte amplitude;

		public byte frequency;
	}
	public struct CommandDataClientTriggerEffectMultiplePositionFeedback
	{
		public EVRControllerType controllerType;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
		public byte[] strength;
	}
	public struct CommandDataClientTriggerEffectSlopeFeedback
	{
		public EVRControllerType controllerType;

		public byte startPosition;

		public byte endPosition;

		public byte startStrength;

		public byte endStrength;
	}
	public struct CommandDataClientTriggerEffectMultiplePositionVibration
	{
		public EVRControllerType controllerType;

		public byte frequency;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
		public byte[] amplitude;
	}
}
namespace PSVR2
{
	internal class Core : MelonMod
	{
		internal static Core Instance { get; private set; }

		internal PreferencesManager PreferencesManager { get; private set; }

		internal PSVR2ToolKitManager ToolkitManager { get; private set; }

		internal FeatureManager FeatureManager { get; private set; }

		internal BoneMenuManager MenuManager { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			PreferencesManager = new PreferencesManager();
			ToolkitManager = new PSVR2ToolKitManager();
			FeatureManager = new FeatureManager();
			MenuManager = new BoneMenuManager();
		}

		public override void OnUpdate()
		{
			FeatureManager.OnUpdate();
		}
	}
}
namespace PSVR2.Utilities
{
	internal static class MelonUtilities
	{
		internal static bool HasEyeTracking()
		{
			return MelonBase.FindMelon("EyeTracking", "Checkerboard") != null;
		}
	}
}
namespace PSVR2.UserData
{
	internal class PreferencesManager
	{
		internal MelonPreferences_Entry<bool> AdaptiveTriggers;

		internal MelonPreferences_Entry<byte> SingleFireFeedback;

		internal MelonPreferences_Entry<bool> FingerCurl;

		internal MelonPreferences_Entry<bool> EyeLidEstimation;

		private MelonPreferences_Category Category { get; set; }

		internal PreferencesManager()
		{
			Category = MelonPreferences.CreateCategory("PSVR2");
			AdaptiveTriggers = Category.CreateEntry<bool>("AdaptiveTriggers", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			SingleFireFeedback = Category.CreateEntry<byte>("SingleFireFeedback", (byte)4, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			FingerCurl = Category.CreateEntry<bool>("FingerCurl", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			EyeLidEstimation = Category.CreateEntry<bool>("EyeLidEstimation", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			Save();
		}

		internal void Save()
		{
			Category.SaveToFile(false);
		}
	}
}
namespace PSVR2.PSVR2ToolKit
{
	internal class PSVR2ToolKitManager
	{
		internal IpcClient IpcClient { get; private set; } = IpcClient.Instance();


		internal bool Loaded { get; private set; }

		internal PSVR2ToolKitManager()
		{
			try
			{
				Loaded = IpcClient.Start();
				if (!Loaded)
				{
					((MelonBase)Core.Instance).LoggerInstance.Error("Did you install the PSVR2 Toolkit Driver?");
				}
			}
			catch (Exception ex)
			{
				((MelonBase)Core.Instance).LoggerInstance.Error("Failed to start IPC client: " + ex.Message);
			}
		}
	}
}
namespace PSVR2.Features
{
	internal class AdaptiveTriggers : IFeature
	{
		private enum FeedbackMode
		{
			None,
			Weapon,
			Vibration
		}

		private class TriggerState
		{
			public FeedbackMode Mode;

			public byte Start;

			public byte End;

			public byte Strength;

			public byte Position;

			public byte Amplitude;

			public byte Frequency;

			public bool Dirty = true;
		}

		private static class TriggerManager
		{
			private static readonly Dictionary<EVRControllerType, TriggerState> States = new Dictionary<EVRControllerType, TriggerState>
			{
				{
					EVRControllerType.Left,
					new TriggerState()
				},
				{
					EVRControllerType.Right,
					new TriggerState()
				}
			};

			internal static void SetNone(EVRControllerType controller, bool force = false)
			{
				TriggerState triggerState = States[controller];
				if (force || triggerState.Mode != 0)
				{
					triggerState.Mode = FeedbackMode.None;
					triggerState.Dirty = true;
				}
			}

			internal static void SetWeapon(EVRControllerType controller, byte start, byte end, byte strength)
			{
				TriggerState triggerState = States[controller];
				if (triggerState.Mode != FeedbackMode.Weapon || triggerState.Start != start || triggerState.End != end || triggerState.Strength != strength)
				{
					triggerState.Mode = FeedbackMode.Weapon;
					triggerState.Start = start;
					triggerState.End = end;
					triggerState.Strength = strength;
					triggerState.Dirty = true;
				}
			}

			internal static void SetVibration(EVRControllerType controller, byte pos, byte amp, byte freq)
			{
				TriggerState triggerState = States[controller];
				if (triggerState.Mode != FeedbackMode.Vibration || triggerState.Position != pos || triggerState.Amplitude != amp || triggerState.Frequency != freq)
				{
					triggerState.Mode = FeedbackMode.Vibration;
					triggerState.Position = pos;
					triggerState.Amplitude = amp;
					triggerState.Frequency = freq;
					triggerState.Dirty = true;
				}
			}

			internal static void Apply()
			{
				foreach (var (controllerType, triggerState2) in States)
				{
					if (!triggerState2.Dirty)
					{
						continue;
					}
					try
					{
						IpcClient ipcClient = Core.Instance.ToolkitManager.IpcClient;
						switch (triggerState2.Mode)
						{
						case FeedbackMode.None:
							ipcClient.TriggerEffectDisable(controllerType);
							break;
						case FeedbackMode.Weapon:
							ipcClient.TriggerEffectWeapon(controllerType, triggerState2.Start, triggerState2.End, triggerState2.Strength);
							break;
						case FeedbackMode.Vibration:
							ipcClient.TriggerEffectVibration(controllerType, triggerState2.Position, triggerState2.Amplitude, triggerState2.Frequency);
							break;
						}
					}
					catch (Exception ex)
					{
						((MelonBase)Core.Instance).LoggerInstance.Error("Trigger IPC failed: " + ex.Message);
					}
					triggerState2.Dirty = false;
				}
			}

			internal static void ForceDisableAll()
			{
				SetNone(EVRControllerType.Left, force: true);
				SetNone(EVRControllerType.Right, force: true);
			}
		}

		[HarmonyPatch(typeof(Gun))]
		private static class GunPatches
		{
			[HarmonyPatch("Update")]
			[HarmonyPostfix]
			private static void Update(Gun __instance)
			{
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Invalid comparison between Unknown and I4
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Invalid comparison between Unknown and I4
				if (!Core.Instance.PreferencesManager.AdaptiveTriggers.Value)
				{
					return;
				}
				Grip triggerGrip = __instance.triggerGrip;
				Hand val = ((triggerGrip != null) ? triggerGrip.GetHand() : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)Player.LeftHand || !((Object)(object)val == (Object)(object)Player.RightHand))
				{
					return;
				}
				EVRControllerType controller = EVRControllerType.Right;
				if (__instance._magState == null || (Object)(object)__instance._ammoInventory == (Object)null || __instance.AmmoCount() <= 0 || (Object)(object)__instance.chamberedCartridge == (Object)null)
				{
					TriggerManager.SetNone(controller);
					return;
				}
				FireMode fireMode = __instance.fireMode;
				if ((int)fireMode > 1)
				{
					if ((int)fireMode == 2)
					{
						byte freq = Convert.ToByte(Math.Min(40, (int)Math.Round(__instance.roundsPerMinute / 60f)));
						TriggerManager.SetVibration(controller, VibrationPosition, VibrationAmplitude, freq);
					}
				}
				else
				{
					TriggerManager.SetWeapon(controller, WeaponStart, WeaponEnd, WeaponStrength);
				}
			}

			[HarmonyPatch("OnTriggerGripDetached")]
			[HarmonyPrefix]
			private static void OnTriggerGripDetached(Gun __instance, Hand hand)
			{
				if (!Core.Instance.PreferencesManager.AdaptiveTriggers.Value || (Object)(object)hand == (Object)null)
				{
					return;
				}
				EVRControllerType controller;
				if ((Object)(object)hand == (Object)(object)Player.LeftHand)
				{
					controller = EVRControllerType.Left;
				}
				else
				{
					if (!((Object)(object)hand == (Object)(object)Player.RightHand))
					{
						return;
					}
					controller = EVRControllerType.Right;
				}
				TriggerManager.SetNone(controller);
			}
		}

		internal static byte WeaponStart = 2;

		internal static byte WeaponEnd = 4;

		internal static byte VibrationPosition = 4;

		internal static byte VibrationAmplitude = 8;

		internal static byte WeaponStrength => Core.Instance.PreferencesManager.SingleFireFeedback.Value;

		public void Initialize()
		{
			Hooking.OnLevelLoaded += delegate
			{
				TriggerManager.ForceDisableAll();
			};
			Hooking.OnLevelUnloaded += TriggerManager.ForceDisableAll;
		}

		public void OnUpdate()
		{
			TriggerManager.Apply();
		}
	}
	internal class FeatureManager
	{
		private IFeature adaptiveTriggers { get; set; }

		private IFeature fingerCurl { get; set; }

		internal FeatureManager()
		{
			adaptiveTriggers = new AdaptiveTriggers();
			fingerCurl = new FingerCurl();
			adaptiveTriggers.Initialize();
			fingerCurl.Initialize();
		}

		internal void OnUpdate()
		{
			adaptiveTriggers.OnUpdate();
			fingerCurl.OnUpdate();
		}
	}
	internal class FingerCurl : IFeature
	{
		[HarmonyPatch(typeof(OpenController))]
		private static class OpenControllerPatches
		{
			[HarmonyPatch("ProcessFingers")]
			[HarmonyPostfix]
			private static void ProcessFingers(OpenController __instance)
			{
				if (Core.Instance.PreferencesManager.FingerCurl.Value && !((Object)(object)((Rig)((BaseController)__instance).contRig).manager != (Object)(object)Player.RigManager))
				{
					((BaseController)__instance)._processedMiddle = Mathf.Clamp(((BaseController)__instance)._processedMiddle, 0.1f, 1f);
					((BaseController)__instance)._processedIndex = Mathf.Clamp(((BaseController)__instance)._processedIndex, 0.1f, 1f);
					((BaseController)__instance)._processedRing = Mathf.Clamp(((BaseController)__instance)._processedRing, 0.1f, 1f);
					((BaseController)__instance)._processedPinky = Mathf.Clamp(((BaseController)__instance)._processedPinky, 0.1f, 1f);
				}
			}
		}

		public void Initialize()
		{
		}
	}
	internal interface IFeature
	{
		internal void Initialize();

		internal void OnUpdate()
		{
		}
	}
	public class PSVR2EyeTracking : TrackingProvider
	{
		private const int noiseFilterSamples = 8;

		private LowPassFilter leftEyeFilter;

		private LowPassFilter rightEyeFilter;

		public override string Name => "PSVR2";

		public override bool SupportsEye => true;

		public override bool SupportsFace => false;

		public override bool IsLoaded => (Core.Instance?.ToolkitManager?.Loaded).GetValueOrDefault();

		public override void Initialize()
		{
			leftEyeFilter = new LowPassFilter(8);
			rightEyeFilter = new LowPassFilter(8);
			Tracking.EyeData.MinDilation = 0f;
			Tracking.EyeData.MaxDilation = 10f;
		}

		public override void Update()
		{
			Core instance = Core.Instance;
			if (instance == null || !(instance.ToolkitManager?.Loaded).GetValueOrDefault() || !Core.Instance.ToolkitManager.IpcClient.IsRunning)
			{
				return;
			}
			CommandDataServerGazeDataResult2 commandDataServerGazeDataResult = Core.Instance.ToolkitManager.IpcClient.RequestEyeTrackingData();
			if (commandDataServerGazeDataResult.leftEye.isBlinkValid)
			{
				float num = ((!commandDataServerGazeDataResult.leftEye.isOpenEnabled || !Core.Instance.PreferencesManager.EyeLidEstimation.Value) ? ((float)((!commandDataServerGazeDataResult.leftEye.blink) ? 1 : 0)) : commandDataServerGazeDataResult.leftEye.open);
				if (leftEyeFilter != null)
				{
					num = leftEyeFilter.FilterValue(num);
				}
				Tracking.EyeData.Left.Openness = num;
			}
			if (commandDataServerGazeDataResult.rightEye.isBlinkValid)
			{
				float num2 = ((!commandDataServerGazeDataResult.rightEye.isOpenEnabled || !Core.Instance.PreferencesManager.EyeLidEstimation.Value) ? ((float)((!commandDataServerGazeDataResult.rightEye.blink) ? 1 : 0)) : commandDataServerGazeDataResult.rightEye.open);
				if (rightEyeFilter != null)
				{
					num2 = rightEyeFilter.FilterValue(num2);
				}
				Tracking.EyeData.Right.Openness = num2;
			}
			if (commandDataServerGazeDataResult.leftEye.isGazeDirValid)
			{
				Tracking.EyeData.Left.GazeX = 0f - commandDataServerGazeDataResult.leftEye.gazeDirNorm.x;
				Tracking.EyeData.Left.GazeY = commandDataServerGazeDataResult.leftEye.gazeDirNorm.y;
			}
			if (commandDataServerGazeDataResult.rightEye.isGazeDirValid)
			{
				Tracking.EyeData.Right.GazeX = 0f - commandDataServerGazeDataResult.rightEye.gazeDirNorm.x;
				Tracking.EyeData.Right.GazeY = commandDataServerGazeDataResult.rightEye.gazeDirNorm.y;
			}
			if (commandDataServerGazeDataResult.leftEye.isPupilDiaValid)
			{
				Tracking.EyeData.Left.PupilDiameterMm = commandDataServerGazeDataResult.leftEye.pupilDiaMm;
			}
			if (commandDataServerGazeDataResult.rightEye.isPupilDiaValid)
			{
				Tracking.EyeData.Right.PupilDiameterMm = commandDataServerGazeDataResult.rightEye.pupilDiaMm;
			}
		}
	}
}
namespace PSVR2.BoneMenu
{
	internal class BoneMenuManager
	{
		internal Page Page { get; private set; }

		internal BoolElement AdaptiveTriggers { get; private set; }

		internal BoolElement EyeLidEstimation { get; private set; }

		internal BoneMenuManager()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			Page = Page.Root.CreatePage("PSVR2", Color32.op_Implicit(new Color32((byte)0, (byte)100, (byte)220, byte.MaxValue)), 0, true);
			AdaptiveTriggers = Page.CreateBool("Adaptive Triggers", Color.white, Core.Instance.PreferencesManager.AdaptiveTriggers.Value, (Action<bool>)delegate(bool b)
			{
				PreferencesManager preferencesManager2 = Core.Instance.PreferencesManager;
				preferencesManager2.AdaptiveTriggers.Value = b;
				preferencesManager2.Save();
			});
			if (MelonUtilities.HasEyeTracking())
			{
				EyeLidEstimation = Page.CreateBool("EyeLid Estimation", Color.white, Core.Instance.PreferencesManager.EyeLidEstimation.Value, (Action<bool>)delegate(bool b)
				{
					PreferencesManager preferencesManager = Core.Instance.PreferencesManager;
					preferencesManager.EyeLidEstimation.Value = b;
					preferencesManager.Save();
				});
				((Element)EyeLidEstimation).SetTooltip("Whether to use PSVR2Toolkit EyeLid estimation instead of binary blink for eye tracking. \nWill only work if you have enabled EyeLid estimation in PSVR2Toolkit. \nPlease see their discord for instructions on how to enable this.");
			}
		}
	}
}