Decompiled source of PSVR2 v1.0.2

Mods/PSVR2.dll

Decompiled 2 months 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.EyeGaze;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using PSVR2;
using PSVR2Toolkit.CAPI;
using PSVR2Toolkit.VRCFT;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "PSVR2", "1.0.2", "Checkerboard", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[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")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PSVR2Toolkit.VRCFT
{
	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 = 1;

		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 int m_gazePumpPeriodMs = 8;

		private CommandDataServerGazeDataResult? m_lastGazeState;

		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();
					((MelonBase)Core.Instance).LoggerInstance.Msg("[IPC_CLIENT] Connected to server successfully.");
					return true;
				}
				return false;
			}
			catch (SocketException ex)
			{
				((MelonBase)Core.Instance).LoggerInstance.Error($"[IPC_CLIENT] Connection failed. LastError = {ex.SocketErrorCode}");
				((MelonBase)Core.Instance).LoggerInstance.Msg("[IPC_CLIENT] Did you install PSVR2 Toolkit?");
				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 = 1;
				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)
						{
							((MelonBase)Core.Instance).LoggerInstance.Warning("[IPC_CLIENT] Disconnected from server.");
							break;
						}
						if (num2 < Marshal.SizeOf<CommandHeader>())
						{
							((MelonBase)Core.Instance).LoggerInstance.Warning("[IPC_CLIENT] Received invalid command header size.");
						}
						else
						{
							HandleIpcCommand(array, num2);
						}
					}
					else
					{
						Thread.Sleep(1);
					}
				}
			}
			catch (OperationCanceledException)
			{
			}
			catch (Exception ex2)
			{
				if (m_running)
				{
					((MelonBase)Core.Instance).LoggerInstance.Warning("[IPC_CLIENT] Error in receive loop: " + ex2.Message);
				}
			}
		}

		private void HandleIpcCommand(byte[] pBuffer, int bytesReceived)
		{
			CommandHeader commandHeader = ByteArrayToStructure<CommandHeader>(pBuffer, 0);
			switch (commandHeader.type)
			{
			case ECommandType.ServerPong:
				((MelonBase)Core.Instance).LoggerInstance.Msg("[IPC_CLIENT] Received Pong from server.");
				break;
			case ECommandType.ServerHandshakeResult:
				if (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerHandshakeResult>())
				{
					CommandDataServerHandshakeResult commandDataServerHandshakeResult = ByteArrayToStructure<CommandDataServerHandshakeResult>(pBuffer, Marshal.SizeOf<CommandHeader>());
					switch (commandDataServerHandshakeResult.result)
					{
					case EHandshakeResult.Success:
						((MelonBase)Core.Instance).LoggerInstance.Msg("[IPC_CLIENT] Handshake successful!");
						break;
					case EHandshakeResult.Failed:
						((MelonBase)Core.Instance).LoggerInstance.Error("[IPC_CLIENT] Handshake failed!");
						break;
					case EHandshakeResult.Outdated:
						((MelonBase)Core.Instance).LoggerInstance.Error($"[IPC_CLIENT] Handshake failed with reason: Outdated client. Please upgrade to an IPC version of {commandDataServerHandshakeResult.ipcVersion}");
						break;
					}
				}
				break;
			case ECommandType.ServerGazeDataResult:
				if (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerGazeDataResult>())
				{
					CommandDataServerGazeDataResult value = ByteArrayToStructure<CommandDataServerGazeDataResult>(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 CommandDataServerGazeDataResult RequestEyeTrackingData()
		{
			if (!m_running)
			{
				return default(CommandDataServerGazeDataResult);
			}
			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;
	}
	public struct CommandDataServerGazeDataResult
	{
		public GazeEyeResult leftEye;

		public GazeEyeResult 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
{
	public static class AssemblyUtilities
	{
		public static void LoadAllValid<T>(Assembly assembly, Action<Type> runOnValid)
		{
			if (assembly.FullName.Contains("System"))
			{
				return;
			}
			Type[] types = assembly.GetTypes();
			foreach (Type type in types)
			{
				if ((!type.Name.Contains("Mono") || !type.Name.Contains("Security")) && typeof(T).IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
				{
					try
					{
						runOnValid(type);
					}
					catch (Exception ex)
					{
						((MelonBase)Core.Instance).LoggerInstance.Error(ex.Message);
					}
				}
			}
		}
	}
	public class Core : MelonMod
	{
		internal static Core Instance { get; private set; }

		internal static MelonPreferences_Category Category { get; private set; }

		public static Page RootPage { get; private set; }

		internal static bool ToolkitDriverLoaded { get; private set; }

		public override void OnInitializeMelon()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Category = MelonPreferences.CreateCategory("PSVR2", "PSVR2 Settings");
			Category.SaveToFile(false);
			RootPage = Page.Root.CreatePage("PSVR2", Color.cyan, 0, true);
			FeatureManager.Initialize();
			try
			{
				ToolkitDriverLoaded = IpcClient.Instance().Start();
			}
			catch (Exception ex)
			{
				((MelonBase)Instance).LoggerInstance.Error("Failed to start IPC client: " + ex.Message);
			}
		}

		public override void OnUpdate()
		{
			FeatureManager.Update();
		}

		public override void OnFixedUpdate()
		{
			FeatureManager.FixedUpdate();
		}

		public override void OnLateUpdate()
		{
			FeatureManager.LateUpdate();
		}

		public override void OnGUI()
		{
			FeatureManager.OnGUI();
		}
	}
	public abstract class Feature
	{
		public abstract void OnRegisterFeature();

		public abstract void Update();

		public abstract void FixedUpdate();

		public abstract void LateUpdate();

		public abstract void OnGUI();
	}
	internal static class FeatureManager
	{
		private static List<Feature> features;

		internal static bool IsInitialized { get; private set; }

		internal static void Initialize()
		{
			features = new List<Feature>();
			LoadFeaturesFromAssembly(((MelonBase)Core.Instance).MelonAssembly.Assembly);
		}

		internal static void Update()
		{
			if (!IsInitialized)
			{
				return;
			}
			foreach (Feature feature in features)
			{
				feature.Update();
			}
		}

		internal static void FixedUpdate()
		{
			if (!IsInitialized)
			{
				return;
			}
			foreach (Feature feature in features)
			{
				feature.FixedUpdate();
			}
		}

		internal static void LateUpdate()
		{
			if (!IsInitialized)
			{
				return;
			}
			foreach (Feature feature in features)
			{
				feature.LateUpdate();
			}
		}

		internal static void OnGUI()
		{
			if (!IsInitialized)
			{
				return;
			}
			foreach (Feature feature in features)
			{
				feature.OnGUI();
			}
		}

		public static void LoadFeaturesFromAssembly(Assembly assembly)
		{
			if (assembly == null)
			{
				throw new NullReferenceException("Can't register from a null assembly!");
			}
			AssemblyUtilities.LoadAllValid<Feature>(assembly, RegisterFunction);
			IsInitialized = true;
		}

		private static void RegisterFunction(Type type)
		{
			if (Activator.CreateInstance(type) is Feature feature)
			{
				feature.OnRegisterFeature();
				features.Add(feature);
			}
		}
	}
}
namespace PSVR2.EyeTracking
{
	public class PSVR2EyeGazeImplementation : EyeGazeImplementation
	{
		private const int k_noiseFilterSamples = 15;

		private LowPassFilter? leftEyeFilter;

		private LowPassFilter? rightEyeFilter;

		public override string Name => "PSVR2";

		public override string DeviceId => "psvr2.ipc";

		public override bool IsLoaded => Core.ToolkitDriverLoaded;

		public override void Initialize()
		{
			if (Core.ToolkitDriverLoaded)
			{
				leftEyeFilter = new LowPassFilter(15);
				rightEyeFilter = new LowPassFilter(15);
			}
		}

		public override void Update()
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			if (!Core.ToolkitDriverLoaded)
			{
				return;
			}
			CommandDataServerGazeDataResult commandDataServerGazeDataResult = IpcClient.Instance().RequestEyeTrackingData();
			if (commandDataServerGazeDataResult.leftEye.isBlinkValid)
			{
				float num = ((!commandDataServerGazeDataResult.leftEye.blink) ? 1 : 0);
				if (leftEyeFilter != null)
				{
					num = leftEyeFilter.FilterValue(num);
				}
				Tracking.Data.Eye.Left.Openness = num;
			}
			if (commandDataServerGazeDataResult.rightEye.isBlinkValid)
			{
				float num2 = ((!commandDataServerGazeDataResult.rightEye.blink) ? 1 : 0);
				if (rightEyeFilter != null)
				{
					num2 = rightEyeFilter.FilterValue(num2);
				}
				Tracking.Data.Eye.Right.Openness = num2;
			}
			if (commandDataServerGazeDataResult.leftEye.isGazeDirValid)
			{
				Tracking.Data.Eye.Left.Gaze = Utils.FlipXCoordinates(new Vector2(commandDataServerGazeDataResult.leftEye.gazeDirNorm.x, commandDataServerGazeDataResult.leftEye.gazeDirNorm.y));
			}
			if (commandDataServerGazeDataResult.rightEye.isGazeDirValid)
			{
				Tracking.Data.Eye.Right.Gaze = Utils.FlipXCoordinates(new Vector2(commandDataServerGazeDataResult.rightEye.gazeDirNorm.x, commandDataServerGazeDataResult.rightEye.gazeDirNorm.y));
			}
			if (commandDataServerGazeDataResult.leftEye.isPupilDiaValid)
			{
				Tracking.Data.Eye.Left.PupilDiameterMm = commandDataServerGazeDataResult.leftEye.pupilDiaMm;
			}
			if (commandDataServerGazeDataResult.rightEye.isPupilDiaValid)
			{
				Tracking.Data.Eye.Right.PupilDiameterMm = commandDataServerGazeDataResult.rightEye.pupilDiaMm;
			}
			Tracking.Data.Eye.MinDilation = 0f;
			Tracking.Data.Eye.MaxDilation = 10f;
		}
	}
}
namespace PSVR2.Features
{
	public enum FeedbackMode
	{
		None,
		Weapon,
		Vibration
	}
	public class AdaptiveTriggers : Feature
	{
		[HarmonyPatch(typeof(Gun))]
		private class GunPatches
		{
			[HarmonyPatch("Update")]
			[HarmonyPostfix]
			public static void Update(Gun __instance)
			{
				//IL_00df: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f7: Expected I4, but got Unknown
				if (!EnableAdaptiveTriggers.Value || !Player.HandsExist)
				{
					return;
				}
				Grip triggerGrip = __instance.triggerGrip;
				Hand val = ((triggerGrip != null) ? triggerGrip.GetHand() : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				EVRControllerType eVRControllerType;
				if ((Object)(object)val == (Object)(object)Player.LeftHand)
				{
					eVRControllerType = EVRControllerType.Left;
					if (DisableLeft.Value)
					{
						return;
					}
				}
				else
				{
					if (!((Object)(object)val == (Object)(object)Player.RightHand))
					{
						return;
					}
					eVRControllerType = EVRControllerType.Right;
				}
				if (__instance._magState == null)
				{
					if (GetCurrentFeedbackMode(eVRControllerType) != 0)
					{
						IpcClient.Instance().TriggerEffectDisable(eVRControllerType);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.None);
					}
					return;
				}
				if ((Object)(object)__instance._ammoInventory == (Object)null)
				{
					if (GetCurrentFeedbackMode(eVRControllerType) != 0)
					{
						IpcClient.Instance().TriggerEffectDisable(eVRControllerType);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.None);
					}
					return;
				}
				if (__instance.AmmoCount() <= 0 || (Object)(object)__instance.chamberedCartridge == (Object)null)
				{
					if (GetCurrentFeedbackMode(eVRControllerType) != 0)
					{
						IpcClient.Instance().TriggerEffectDisable(eVRControllerType);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.None);
					}
					return;
				}
				FireMode fireMode = __instance.fireMode;
				switch ((int)fireMode)
				{
				case 0:
					if (GetCurrentFeedbackMode(eVRControllerType) != FeedbackMode.Weapon)
					{
						IpcClient.Instance().TriggerEffectWeapon(eVRControllerType, WeaponStart, WeaponEnd, WeaponStrength);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.Weapon);
					}
					break;
				case 1:
					if (GetCurrentFeedbackMode(eVRControllerType) != FeedbackMode.Weapon)
					{
						IpcClient.Instance().TriggerEffectWeapon(eVRControllerType, WeaponStart, WeaponEnd, WeaponStrength);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.Weapon);
					}
					break;
				case 2:
					if (GetCurrentFeedbackMode(eVRControllerType) != FeedbackMode.Vibration)
					{
						byte frequency = Convert.ToByte(Math.Min(40, (int)Math.Round(__instance.roundsPerMinute / 60f)));
						IpcClient.Instance().TriggerEffectVibration(eVRControllerType, VibrationPosition, VibrationAmplitude, frequency);
						SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.Vibration);
					}
					break;
				}
			}

			[HarmonyPatch("OnTriggerGripDetached")]
			[HarmonyPostfix]
			public static void OnTriggerGripDetached(Gun __instance, Hand hand)
			{
				if (!Player.HandsExist)
				{
					return;
				}
				EVRControllerType eVRControllerType;
				if ((Object)(object)hand == (Object)(object)Player.LeftHand)
				{
					eVRControllerType = EVRControllerType.Left;
				}
				else
				{
					if (!((Object)(object)hand == (Object)(object)Player.RightHand))
					{
						return;
					}
					eVRControllerType = EVRControllerType.Right;
				}
				IpcClient.Instance().TriggerEffectDisable(eVRControllerType);
				SetCurrentFeedbackMode(eVRControllerType, FeedbackMode.None);
			}
		}

		internal static MelonPreferences_Entry<bool> EnableAdaptiveTriggers;

		internal static MelonPreferences_Entry<bool> DisableLeft;

		internal static MelonPreferences_Entry<byte> SingleFireFeedback;

		public static byte WeaponStart = 2;

		public static byte WeaponEnd = 4;

		public static byte VibrationPosition = 4;

		public static byte VibrationAmplitude = 8;

		public static FeedbackMode CurrentRightFeedbackType = FeedbackMode.None;

		public static FeedbackMode CurrentLeftFeedbackType = FeedbackMode.None;

		public static byte WeaponStrength => SingleFireFeedback.Value;

		public override void OnRegisterFeature()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			EnableAdaptiveTriggers = Core.Category.CreateEntry<bool>("AdaptiveTriggers", true, "Adaptive Triggers", (string)null, false, false, (ValueValidator)null, (string)null);
			DisableLeft = Core.Category.CreateEntry<bool>("DisableLeft", true, "Disable Left Controller", "Disables the (bugged) left controller use of adaptive triggers.", false, false, (ValueValidator)null, (string)null);
			SingleFireFeedback = Core.Category.CreateEntry<byte>("SingleFireFeedback", (byte)4, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			Page obj = Core.RootPage.CreatePage("Adaptive Triggers", Color.white, 0, true);
			obj.CreateBool("Enabled", Color.white, EnableAdaptiveTriggers.Value, (Action<bool>)delegate(bool b)
			{
				EnableAdaptiveTriggers.Value = b;
				Core.Category.SaveToFile(false);
			});
			obj.CreateInt("Single Fire Resistance", Color.white, (int)SingleFireFeedback.Value, 1, 0, 8, (Action<int>)delegate(int i)
			{
				SingleFireFeedback.Value = Convert.ToByte(i);
				Core.Category.SaveToFile(false);
			});
			obj.CreateBool("Disable Left Controller", Color.white, DisableLeft.Value, (Action<bool>)delegate(bool b)
			{
				DisableLeft.Value = b;
				Core.Category.SaveToFile(false);
			});
			Hooking.OnLevelLoaded += OnLevelLoad;
			Hooking.OnLevelUnloaded += OnLevelUnload;
		}

		private void OnLevelLoad(LevelInfo info)
		{
			SetCurrentFeedbackMode(EVRControllerType.Left, FeedbackMode.None);
			SetCurrentFeedbackMode(EVRControllerType.Right, FeedbackMode.None);
			IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Left);
			IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Right);
		}

		private void OnLevelUnload()
		{
			SetCurrentFeedbackMode(EVRControllerType.Left, FeedbackMode.None);
			SetCurrentFeedbackMode(EVRControllerType.Right, FeedbackMode.None);
			IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Left);
			IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Right);
		}

		public override void Update()
		{
		}

		public override void FixedUpdate()
		{
		}

		public override void LateUpdate()
		{
		}

		public override void OnGUI()
		{
		}

		public static void SetCurrentFeedbackMode(EVRControllerType controller, FeedbackMode feedbackMode)
		{
			switch (controller)
			{
			case EVRControllerType.Left:
				CurrentLeftFeedbackType = feedbackMode;
				break;
			case EVRControllerType.Right:
				CurrentRightFeedbackType = feedbackMode;
				break;
			}
		}

		public static FeedbackMode GetCurrentFeedbackMode(EVRControllerType controller)
		{
			return controller switch
			{
				EVRControllerType.Left => CurrentLeftFeedbackType, 
				EVRControllerType.Right => CurrentRightFeedbackType, 
				_ => FeedbackMode.None, 
			};
		}
	}
	[HarmonyPatch]
	internal class FingerCurl : Feature
	{
		[HarmonyPatch(typeof(OpenController))]
		private class OpenControllerPatches
		{
			[HarmonyPatch("ProcessFingers")]
			[HarmonyPostfix]
			public static void ProcessFingers(OpenController __instance)
			{
				if (EnableFingerCurl.Value && Player.HandsExist)
				{
					((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);
				}
			}
		}

		internal static MelonPreferences_Entry<bool> EnableFingerCurl;

		public override void OnRegisterFeature()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			EnableFingerCurl = Core.Category.CreateEntry<bool>("FingerCurl", true, "Finger Curl", (string)null, false, false, (ValueValidator)null, (string)null);
			Core.RootPage.CreateBool("Finger Curl", Color.white, EnableFingerCurl.Value, (Action<bool>)delegate(bool b)
			{
				EnableFingerCurl.Value = b;
				Core.Category.SaveToFile(false);
			});
		}

		public override void Update()
		{
		}

		public override void FixedUpdate()
		{
		}

		public override void LateUpdate()
		{
		}

		public override void OnGUI()
		{
		}
	}
}