Decompiled source of EasyLogiWheelSupport v1.0.1
BepInEx\plugins\EasyLogiWheelSupport\EasyLogiWheelSupport.dll
Decompiled 9 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("EasyLogiWheelSupport")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("EasyLogiWheelSupport")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("5B99A0FA-9A67-4748-9F89-4B0B8F0E2D60")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.1.0.0")] [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; } } } public class LogitechGSDK { [StructLayout(LayoutKind.Sequential, Pack = 2)] public struct LogiControllerPropertiesData { public bool forceEnable; public int overallGain; public int springGain; public int damperGain; public bool defaultSpringEnabled; public int defaultSpringGain; public bool combinePedals; public int wheelRange; public bool gameSettingsEnabled; public bool allowGameSettings; } [StructLayout(LayoutKind.Sequential, Pack = 2)] public struct DIJOYSTATE2ENGINES { public int lX; public int lY; public int lZ; public int lRx; public int lRy; public int lRz; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] rglSlider; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public uint[] rgdwPOV; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] public byte[] rgbButtons; public int lVX; public int lVY; public int lVZ; public int lVRx; public int lVRy; public int lVRz; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] rglVSlider; public int lAX; public int lAY; public int lAZ; public int lARx; public int lARy; public int lARz; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] rglASlider; public int lFX; public int lFY; public int lFZ; public int lFRx; public int lFRy; public int lFRz; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] rglFSlider; } public const int LOGI_MAX_CONTROLLERS = 4; public const int LOGI_FORCE_NONE = -1; public const int LOGI_FORCE_SPRING = 0; public const int LOGI_FORCE_CONSTANT = 1; public const int LOGI_FORCE_DAMPER = 2; public const int LOGI_FORCE_SIDE_COLLISION = 3; public const int LOGI_FORCE_FRONTAL_COLLISION = 4; public const int LOGI_FORCE_DIRT_ROAD = 5; public const int LOGI_FORCE_BUMPY_ROAD = 6; public const int LOGI_FORCE_SLIPPERY_ROAD = 7; public const int LOGI_FORCE_SURFACE_EFFECT = 8; public const int LOGI_NUMBER_FORCE_EFFECTS = 9; public const int LOGI_FORCE_SOFTSTOP = 10; public const int LOGI_FORCE_CAR_AIRBORNE = 11; public const int LOGI_PERIODICTYPE_NONE = -1; public const int LOGI_PERIODICTYPE_SINE = 0; public const int LOGI_PERIODICTYPE_SQUARE = 1; public const int LOGI_PERIODICTYPE_TRIANGLE = 2; public const int LOGI_DEVICE_TYPE_NONE = -1; public const int LOGI_DEVICE_TYPE_WHEEL = 0; public const int LOGI_DEVICE_TYPE_JOYSTICK = 1; public const int LOGI_DEVICE_TYPE_GAMEPAD = 2; public const int LOGI_DEVICE_TYPE_OTHER = 3; public const int LOGI_NUMBER_DEVICE_TYPES = 4; public const int LOGI_MANUFACTURER_NONE = -1; public const int LOGI_MANUFACTURER_LOGITECH = 0; public const int LOGI_MANUFACTURER_MICROSOFT = 1; public const int LOGI_MANUFACTURER_OTHER = 2; public const int LOGI_MODEL_G27 = 0; public const int LOGI_MODEL_DRIVING_FORCE_GT = 1; public const int LOGI_MODEL_G25 = 2; public const int LOGI_MODEL_MOMO_RACING = 3; public const int LOGI_MODEL_MOMO_FORCE = 4; public const int LOGI_MODEL_DRIVING_FORCE_PRO = 5; public const int LOGI_MODEL_DRIVING_FORCE = 6; public const int LOGI_MODEL_NASCAR_RACING_WHEEL = 7; public const int LOGI_MODEL_FORMULA_FORCE = 8; public const int LOGI_MODEL_FORMULA_FORCE_GP = 9; public const int LOGI_MODEL_FORCE_3D_PRO = 10; public const int LOGI_MODEL_EXTREME_3D_PRO = 11; public const int LOGI_MODEL_FREEDOM_24 = 12; public const int LOGI_MODEL_ATTACK_3 = 13; public const int LOGI_MODEL_FORCE_3D = 14; public const int LOGI_MODEL_STRIKE_FORCE_3D = 15; public const int LOGI_MODEL_G940_JOYSTICK = 16; public const int LOGI_MODEL_G940_THROTTLE = 17; public const int LOGI_MODEL_G940_PEDALS = 18; public const int LOGI_MODEL_RUMBLEPAD = 19; public const int LOGI_MODEL_RUMBLEPAD_2 = 20; public const int LOGI_MODEL_CORDLESS_RUMBLEPAD_2 = 21; public const int LOGI_MODEL_CORDLESS_GAMEPAD = 22; public const int LOGI_MODEL_DUAL_ACTION_GAMEPAD = 23; public const int LOGI_MODEL_PRECISION_GAMEPAD_2 = 24; public const int LOGI_MODEL_CHILLSTREAM = 25; public const int LOGI_MODEL_G29 = 26; public const int LOGI_MODEL_G920 = 27; public const int LOGI_NUMBER_MODELS = 28; [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiSteeringInitialize(bool ignoreXInputControllers); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiUpdate(); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr LogiGetStateENGINES(int index); public static DIJOYSTATE2ENGINES LogiGetStateCSharp(int index) { DIJOYSTATE2ENGINES result = default(DIJOYSTATE2ENGINES); result.rglSlider = new int[2]; result.rgdwPOV = new uint[4]; result.rgbButtons = new byte[128]; result.rglVSlider = new int[2]; result.rglASlider = new int[2]; result.rglFSlider = new int[2]; try { result = (DIJOYSTATE2ENGINES)Marshal.PtrToStructure(LogiGetStateENGINES(index), typeof(DIJOYSTATE2ENGINES)); return result; } catch (ArgumentException) { } return result; } [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiGetDevicePath(int index, StringBuilder str, int size); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiGetFriendlyProductName(int index, StringBuilder str, int size); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiIsConnected(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiIsDeviceConnected(int index, int deviceType); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiIsManufacturerConnected(int index, int manufacturerName); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiIsModelConnected(int index, int modelName); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiButtonTriggered(int index, int buttonNbr); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiButtonReleased(int index, int buttonNbr); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiButtonIsPressed(int index, int buttonNbr); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiGenerateNonLinearValues(int index, int nonLinCoeff); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern int LogiGetNonLinearValue(int index, int inputValue); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiHasForceFeedback(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiIsPlaying(int index, int forceType); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlaySpringForce(int index, int offsetPercentage, int saturationPercentage, int coefficientPercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopSpringForce(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayConstantForce(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopConstantForce(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayDamperForce(int index, int coefficientPercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopDamperForce(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlaySideCollisionForce(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayFrontalCollisionForce(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayDirtRoadEffect(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopDirtRoadEffect(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayBumpyRoadEffect(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopBumpyRoadEffect(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlaySlipperyRoadEffect(int index, int magnitudePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopSlipperyRoadEffect(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlaySurfaceEffect(int index, int type, int magnitudePercentage, int period); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopSurfaceEffect(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayCarAirborne(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopCarAirborne(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlaySoftstopForce(int index, int usableRangePercentage); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiStopSoftstopForce(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiSetPreferredControllerProperties(LogiControllerPropertiesData properties); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiGetCurrentControllerProperties(int index, ref LogiControllerPropertiesData properties); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern int LogiGetShifterMode(int index); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiSetOperatingRange(int index, int range); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiGetOperatingRange(int index, ref int range); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern bool LogiPlayLeds(int index, float currentRPM, float rpmFirstLedTurnsOn, float rpmRedLine); [DllImport("LogitechSteeringWheelEnginesWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern void LogiSteeringShutdown(); } namespace EasyLogiWheelSupport { [BepInPlugin("shibe.easydeliveryco.logiwheel", "LogiWheel", "1.0.1")] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(sCarController), "SetInput", new Type[] { typeof(Vector2) })] private static class CarController_SetInput_Vector2_Patch { private static readonly FieldRef<sCarController, Vector2> TargetInputRef = AccessTools.FieldRefAccess<sCarController, Vector2>("targetInput"); private static bool Prefix(sCarController __instance, Vector2 input) { //IL_0029: 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_0068: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return true; } if (!ShouldApply() || __instance.GuyActive) { return true; } if (!TryGetWheelLastInput(out var _, out var _)) { return true; } Vector2 val = input; if (__instance.throttleCurve != null) { val.y = __instance.throttleCurve.Evaluate(Math.Abs(val.y)) * (float)Math.Sign(val.y); } TargetInputRef.Invoke(__instance) = val; return false; } } [HarmonyPatch(typeof(sCarController), "Move")] private static class CarController_Move_Prefix_Patch { private static void Prefix(sCarController __instance) { if (!((Object)(object)__instance == (Object)null) && ShouldApply() && !__instance.GuyActive && TryGetWheelLastInput(out var steer, out var accel)) { __instance.input.x = steer; __instance.input.y = accel; } } } internal enum PedalKind { Throttle, Brake, Clutch } internal enum AxisId { lX, lY, lZ, lRx, lRy, lRz, slider0, slider1 } internal enum ButtonBindAction { InteractOk = 0, Back = 1, MapItems = 2, Pause = 3, Camera = 5, ResetVehicle = 6, Headlights = 7, Horn = 8, RadioPower = 9, RadioScanToggle = 10, RadioScanLeft = 11, RadioScanRight = 12 } internal enum BindingLayer { Normal, Modified } internal enum BindingKind { None, Button, Pov } internal struct BindingInput { public BindingKind Kind; public int Code; } internal enum FfbTestEffect { None, Shake, Bumpy } [HarmonyPatch(typeof(InteriorInteraction), "AnimateSteeringWheel")] private static class InteriorInteraction_AnimateSteeringWheel_Patch { private static readonly FieldRef<InteriorInteraction, sCarController> CarRef = AccessTools.FieldRefAccess<InteriorInteraction, sCarController>("car"); private static readonly FieldRef<InteriorInteraction, Transform> HandPivotRef = AccessTools.FieldRefAccess<InteriorInteraction, Transform>("handPivot"); private static readonly FieldRef<InteriorInteraction, Vector3[]> HandRestLocalPositionsRef = AccessTools.FieldRefAccess<InteriorInteraction, Vector3[]>("handRestLocalPositions"); private static bool Prefix(InteriorInteraction __instance) { //IL_008c: 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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return true; } if (!ShouldApply()) { return true; } if (!TryGetWheelLastInput(out var _, out var accel)) { return true; } sCarController val = CarRef.Invoke(__instance); if ((Object)(object)val == (Object)null) { return true; } if (val.GuyActive) { return true; } if ((Object)(object)__instance.steeringWheel == (Object)null) { return false; } if (!TryGetWheelLastInputRecent(0.1f, out var steer2, out accel)) { steer2 = Mathf.Clamp(val.input.x, -1f, 1f); } float num = steer2 * 420f; __instance.steeringWheel.localEulerAngles = Vector3.up * num; if ((Object)(object)__instance.handRest != (Object)null && (Object)(object)__instance.hand != (Object)null) { float num2 = Mathf.Clamp01((Mathf.Abs(num) - 30f) / 60f); Vector3[] array = HandRestLocalPositionsRef.Invoke(__instance); if (array != null && array.Length >= 2) { __instance.handRest.localPosition = array[(!(num < 0f)) ? 1u : 0u]; } Transform val2 = HandPivotRef.Invoke(__instance); if ((Object)(object)val2 != (Object)null) { ((Component)__instance.hand).transform.position = Vector3.Lerp(val2.position, __instance.handRest.position, num2); } } return false; } } private static ManualLogSource _log; private static ConfigEntry<bool> _enableMod; private static ConfigEntry<bool> _debugLogging; private static ConfigEntry<bool> _logDetectedDevices; private static ConfigEntry<bool> _desktopMenuIconVisible; private static ConfigEntry<string> _desktopMenuIconX; private static ConfigEntry<string> _desktopMenuIconY; private static ConfigEntry<bool> _ignoreXInputControllers; internal const string PrefKeyFfbEnabled = "G920FfbEnabled"; internal const string PrefKeyFfbOverall = "G920FfbOverall"; internal const string PrefKeyFfbSpring = "G920FfbSpring"; internal const string PrefKeyFfbDamper = "G920FfbDamper"; internal const string PrefKeyWheelRange = "G920WheelRange"; internal const string PrefKeyCalSteerCenter = "G920Cal_SteerCenter"; internal const string PrefKeyCalSteerLeft = "G920Cal_SteerLeft"; internal const string PrefKeyCalSteerRight = "G920Cal_SteerRight"; internal const string PrefKeyCalThrottleReleased = "G920Cal_ThrottleReleased"; internal const string PrefKeyCalThrottlePressed = "G920Cal_ThrottlePressed"; internal const string PrefKeyCalBrakeReleased = "G920Cal_BrakeReleased"; internal const string PrefKeyCalBrakePressed = "G920Cal_BrakePressed"; internal const string PrefKeyCalClutchReleased = "G920Cal_ClutchReleased"; internal const string PrefKeyCalClutchPressed = "G920Cal_ClutchPressed"; internal const string PrefKeySteeringGain = "G920SteerGain"; internal const string PrefKeySteeringDeadzone = "G920SteerDeadzone"; internal const string PrefKeySteeringAxis = "G920Axis_Steer"; internal const string PrefKeyThrottleAxis = "G920Axis_Throttle"; internal const string PrefKeyBrakeAxis = "G920Axis_Brake"; internal const string PrefKeyClutchAxis = "G920Axis_Clutch"; private const int BindingPovOffset = 1000; private const string PrefKeyBindModifier = "G920Bind_Modifier"; private static int _wheelInputCacheFrame = -1; private static bool _wheelInputCacheValid; private static LogitechGSDK.DIJOYSTATE2ENGINES _wheelInputCacheState; private static bool[] _wheelButtonsDown; private static bool[] _wheelButtonsPressed; private static bool[] _wheelButtonsReleased; private static bool[] _wheelPovDown; private static bool[] _wheelPovPressed; private static bool[] _wheelPovReleased; private static int _wheelPovDirDown = -1; private static bool _logiInitAttempted; private static bool _logiAvailable; private static bool _logiConnected; private static int _logiIndex; private static int _logiInitAttemptCount; private static float _logiNextInitAttemptTime; private static bool _logiIgnoreXInputUsed; private static bool _logiWasConnected; private static string _logiLastName; private static string _logiLastPath; private static bool _isInWalkingMode; private static float _currentSpeedKmh; private static bool _isOffRoad; private static bool _isSliding; private static int _wheelLastUpdateFrame; private static float _wheelLastUpdateTime; private static float _wheelLastSteer; private static float _wheelLastAccel; private static float _wheelMenuHeartbeatTime; private static float _ffbPageHeartbeatTime; private static float _bindingCaptureHeartbeatTime; private static FfbTestEffect _ffbTestEffect; private static float _ffbTestEndTime; public const string PluginGuid = "shibe.easydeliveryco.logiwheel"; public const string PluginName = "LogiWheel"; public const string PluginVersion = "1.0.1"; private static string GetBindPrefKey(BindingLayer layer, ButtonBindAction action) { int num = (int)layer; string text = num.ToString(); num = (int)action; return "G920Bind_" + text + "_" + num; } private static string GetLegacyBindPrefKey(BindingLayer layer, ButtonBindAction action) { return "G920Bind_" + layer.ToString() + "_" + action; } internal static BindingInput GetBinding(BindingLayer layer, ButtonBindAction action) { int @int = PlayerPrefs.GetInt(GetBindPrefKey(layer, action), -1); if (@int < 0) { @int = PlayerPrefs.GetInt(GetLegacyBindPrefKey(layer, action), -1); } BindingInput result; if (@int < 0) { result = default(BindingInput); result.Kind = BindingKind.None; result.Code = 0; return result; } if (@int >= 1000) { result = default(BindingInput); result.Kind = BindingKind.Pov; result.Code = Mathf.Clamp(@int - 1000, 0, 3); return result; } result = default(BindingInput); result.Kind = BindingKind.Button; result.Code = Mathf.Clamp(@int, 0, 127); return result; } internal static void SetBinding(BindingLayer layer, ButtonBindAction action, BindingInput input) { int num = ((input.Kind == BindingKind.Button) ? Mathf.Clamp(input.Code, 0, 127) : ((input.Kind != BindingKind.Pov) ? (-1) : (1000 + Mathf.Clamp(input.Code, 0, 3)))); PlayerPrefs.SetInt(GetBindPrefKey(layer, action), num); } internal static bool TryGetPovDir(out int dir) { dir = -1; if (!TryGetCachedWheelState(out var state)) { return false; } uint pov = uint.MaxValue; if (state.rgdwPOV != null && state.rgdwPOV.Length != 0) { pov = state.rgdwPOV[0]; } dir = PovToDir(pov); return true; } internal static bool TryGetPov8Vector(out Vector2 v) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0110: 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) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) v = Vector2.zero; if (!TryGetCachedWheelState(out var state)) { return false; } uint num = uint.MaxValue; if (state.rgdwPOV != null && state.rgdwPOV.Length != 0) { num = state.rgdwPOV[0]; } if (num == uint.MaxValue) { return true; } switch (Mathf.RoundToInt((float)(int)(num % 36000) / 4500f) % 8) { case 0: v = new Vector2(0f, -1f); break; case 1: v = new Vector2(-1f, -1f); break; case 2: v = new Vector2(-1f, 0f); break; case 3: v = new Vector2(-1f, 1f); break; case 4: v = new Vector2(0f, 1f); break; case 5: v = new Vector2(1f, 1f); break; case 6: v = new Vector2(1f, 0f); break; default: v = new Vector2(1f, -1f); break; } return true; } internal static BindingInput GetModifierBinding() { int @int = PlayerPrefs.GetInt("G920Bind_Modifier", -1); BindingInput result; if (@int < 0) { result = default(BindingInput); result.Kind = BindingKind.None; result.Code = 0; return result; } if (@int >= 1000) { result = default(BindingInput); result.Kind = BindingKind.Pov; result.Code = Mathf.Clamp(@int - 1000, 0, 3); return result; } result = default(BindingInput); result.Kind = BindingKind.Button; result.Code = Mathf.Clamp(@int, 0, 127); return result; } internal static void SetModifierBinding(BindingInput input) { int num = ((input.Kind == BindingKind.Button) ? Mathf.Clamp(input.Code, 0, 127) : ((input.Kind != BindingKind.Pov) ? (-1) : (1000 + Mathf.Clamp(input.Code, 0, 3)))); PlayerPrefs.SetInt("G920Bind_Modifier", num); } internal static string GetBindingLabel(BindingInput input) { if (input.Kind == BindingKind.Button) { return "But. " + (Mathf.Clamp(input.Code, 0, 127) + 1); } if (input.Kind == BindingKind.Pov) { return Mathf.Clamp(input.Code, 0, 3) switch { 0 => "DP Up", 1 => "DP Right", 2 => "DP Down", _ => "DP Left", }; } return "None"; } internal static string GetChordLabel(BindingInput input, bool modified) { string bindingLabel = GetBindingLabel(input); if (!modified || input.Kind == BindingKind.None) { return bindingLabel; } return "M+" + bindingLabel; } internal static string GetActionLabel(ButtonBindAction action) { return action switch { ButtonBindAction.InteractOk => "Interact", ButtonBindAction.Back => "Back", ButtonBindAction.MapItems => "Map/Items", ButtonBindAction.Pause => "Pause", ButtonBindAction.Camera => "Camera", ButtonBindAction.ResetVehicle => "Reset", ButtonBindAction.Headlights => "Lights", ButtonBindAction.Horn => "Horn", ButtonBindAction.RadioPower => "Radio Pwr", ButtonBindAction.RadioScanToggle => "Scan", ButtonBindAction.RadioScanLeft => "Prev Ch", ButtonBindAction.RadioScanRight => "Next Ch", _ => action.ToString(), }; } private static void EnsureWheelInputArrays() { if (_wheelButtonsDown == null) { _wheelButtonsDown = new bool[128]; } if (_wheelButtonsPressed == null) { _wheelButtonsPressed = new bool[128]; } if (_wheelButtonsReleased == null) { _wheelButtonsReleased = new bool[128]; } if (_wheelPovDown == null) { _wheelPovDown = new bool[4]; } if (_wheelPovPressed == null) { _wheelPovPressed = new bool[4]; } if (_wheelPovReleased == null) { _wheelPovReleased = new bool[4]; } } private static int PovToDir(uint pov) { if (pov == uint.MaxValue) { return -1; } return (int)(pov % 36000 + 4500) / 9000 % 4; } private static void UpdateWheelInputCache() { EnsureWheelInputArrays(); int frameCount = Time.frameCount; if (frameCount == _wheelInputCacheFrame) { return; } _wheelInputCacheFrame = frameCount; Array.Clear(_wheelButtonsPressed, 0, _wheelButtonsPressed.Length); Array.Clear(_wheelButtonsReleased, 0, _wheelButtonsReleased.Length); Array.Clear(_wheelPovPressed, 0, _wheelPovPressed.Length); Array.Clear(_wheelPovReleased, 0, _wheelPovReleased.Length); _wheelInputCacheValid = TryGetLogiState(out var state); if (!_wheelInputCacheValid) { return; } _wheelInputCacheState = state; if (state.rgbButtons != null) { int num = Math.Min(state.rgbButtons.Length, _wheelButtonsDown.Length); for (int i = 0; i < num; i++) { bool flag = state.rgbButtons[i] >= 128; if (flag && !_wheelButtonsDown[i]) { _wheelButtonsPressed[i] = true; } else if (!flag && _wheelButtonsDown[i]) { _wheelButtonsReleased[i] = true; } _wheelButtonsDown[i] = flag; } } int num2 = -1; if (state.rgdwPOV != null && state.rgdwPOV.Length != 0) { num2 = PovToDir(state.rgdwPOV[0]); } if (num2 != _wheelPovDirDown) { if (_wheelPovDirDown >= 0) { _wheelPovReleased[_wheelPovDirDown] = true; } if (num2 >= 0) { _wheelPovPressed[num2] = true; } } _wheelPovDirDown = num2; for (int j = 0; j < 4; j++) { _wheelPovDown[j] = num2 == j; } } internal static bool TryGetCachedWheelState(out LogitechGSDK.DIJOYSTATE2ENGINES state) { UpdateWheelInputCache(); state = _wheelInputCacheState; return _wheelInputCacheValid; } internal static bool TryCaptureNextBinding(out BindingInput input) { input = default(BindingInput); UpdateWheelInputCache(); if (!_wheelInputCacheValid) { return false; } for (int i = 0; i < _wheelButtonsPressed.Length; i++) { if (_wheelButtonsPressed[i]) { input = new BindingInput { Kind = BindingKind.Button, Code = i }; return true; } } for (int j = 0; j < _wheelPovPressed.Length; j++) { if (_wheelPovPressed[j]) { input = new BindingInput { Kind = BindingKind.Pov, Code = j }; return true; } } return false; } internal static bool IsBindingDownForCurrentFrame(BindingInput input) { UpdateWheelInputCache(); if (!_wheelInputCacheValid) { return false; } return IsBindingDown(input); } internal static bool IsBindingPressedThisFrameForCurrentFrame(BindingInput input) { UpdateWheelInputCache(); if (!_wheelInputCacheValid) { return false; } return IsBindingPressedThisFrame(input); } internal static bool IsBindingReleasedThisFrameForCurrentFrame(BindingInput input) { UpdateWheelInputCache(); if (!_wheelInputCacheValid) { return false; } return IsBindingReleasedThisFrame(input); } private static bool IsBindingDown(BindingInput input) { if (input.Kind == BindingKind.Button) { int num = Mathf.Clamp(input.Code, 0, 127); if (_wheelButtonsDown != null && num < _wheelButtonsDown.Length) { return _wheelButtonsDown[num]; } return false; } if (input.Kind == BindingKind.Pov) { int num2 = Mathf.Clamp(input.Code, 0, 3); if (_wheelPovDown != null && num2 < _wheelPovDown.Length) { return _wheelPovDown[num2]; } return false; } return false; } private static bool IsBindingPressedThisFrame(BindingInput input) { if (input.Kind == BindingKind.Button) { int num = Mathf.Clamp(input.Code, 0, 127); if (_wheelButtonsPressed != null && num < _wheelButtonsPressed.Length) { return _wheelButtonsPressed[num]; } return false; } if (input.Kind == BindingKind.Pov) { int num2 = Mathf.Clamp(input.Code, 0, 3); if (_wheelPovPressed != null && num2 < _wheelPovPressed.Length) { return _wheelPovPressed[num2]; } return false; } return false; } private static bool IsBindingReleasedThisFrame(BindingInput input) { if (input.Kind == BindingKind.Button) { int num = Mathf.Clamp(input.Code, 0, 127); if (_wheelButtonsReleased != null && num < _wheelButtonsReleased.Length) { return _wheelButtonsReleased[num]; } return false; } if (input.Kind == BindingKind.Pov) { int num2 = Mathf.Clamp(input.Code, 0, 3); if (_wheelPovReleased != null && num2 < _wheelPovReleased.Length) { return _wheelPovReleased[num2]; } return false; } return false; } private static bool ShouldApply() { if (_enableMod != null) { return _enableMod.Value; } return false; } internal static bool GetFfbEnabled() { return PlayerPrefs.GetInt("G920FfbEnabled", 1) != 0; } internal static void SetFfbEnabled(bool enabled) { PlayerPrefs.SetInt("G920FfbEnabled", enabled ? 1 : 0); if (!enabled) { StopAllForces(); } ApplyControllerPropertiesIfReady(); } internal static float GetFfbOverallGain() { return Mathf.Clamp01(PlayerPrefs.GetFloat("G920FfbOverall", 0.55f)); } internal static void SetFfbOverallGain(float value) { value = Mathf.Clamp01(value); PlayerPrefs.SetFloat("G920FfbOverall", value); ApplyControllerPropertiesIfReady(); } internal static float GetFfbSpringGain() { return Mathf.Clamp01(PlayerPrefs.GetFloat("G920FfbSpring", 0.6f)); } internal static void SetFfbSpringGain(float value) { value = Mathf.Clamp01(value); PlayerPrefs.SetFloat("G920FfbSpring", value); ApplyControllerPropertiesIfReady(); } internal static float GetFfbDamperGain() { return Mathf.Clamp01(PlayerPrefs.GetFloat("G920FfbDamper", 0.2f)); } internal static void SetFfbDamperGain(float value) { value = Mathf.Clamp01(value); PlayerPrefs.SetFloat("G920FfbDamper", value); ApplyControllerPropertiesIfReady(); } internal static int GetWheelRange() { return Mathf.Clamp(PlayerPrefs.GetInt("G920WheelRange", 270), 180, 900); } internal static void SetWheelRange(int degrees) { degrees = Mathf.Clamp(degrees, 180, 900); PlayerPrefs.SetInt("G920WheelRange", degrees); ApplyWheelRangeIfReady(); } internal static float GetSteeringGain() { return Mathf.Clamp(PlayerPrefs.GetFloat("G920SteerGain", 1f), 0.5f, 3f); } internal static void SetSteeringGain(float value) { value = Mathf.Clamp(value, 0.5f, 3f); PlayerPrefs.SetFloat("G920SteerGain", value); } internal static void ResetFfbDefaults() { PlayerPrefs.SetInt("G920FfbEnabled", 1); PlayerPrefs.SetFloat("G920FfbOverall", 0.55f); PlayerPrefs.SetFloat("G920FfbSpring", 0.6f); PlayerPrefs.SetFloat("G920FfbDamper", 0.2f); StopAllForces(); ApplyControllerPropertiesIfReady(); } internal static void ResetSteeringDefaults() { PlayerPrefs.SetInt("G920WheelRange", 270); PlayerPrefs.SetFloat("G920SteerGain", 1f); PlayerPrefs.SetFloat("G920SteerDeadzone", 0.01f); ApplyWheelRangeIfReady(); } internal static float GetSteeringDeadzone() { return Mathf.Clamp(PlayerPrefs.GetFloat("G920SteerDeadzone", 0.01f), 0f, 0.12f); } internal static void SetSteeringDeadzone(float value) { value = Mathf.Clamp(value, 0f, 0.12f); PlayerPrefs.SetFloat("G920SteerDeadzone", value); } internal static AxisId GetSteeringAxis() { return (AxisId)Mathf.Clamp(PlayerPrefs.GetInt("G920Axis_Steer", 0), 0, 7); } internal static void SetSteeringAxis(AxisId axis) { PlayerPrefs.SetInt("G920Axis_Steer", (int)axis); PlayerPrefs.DeleteKey("G920Cal_SteerCenter"); PlayerPrefs.DeleteKey("G920Cal_SteerLeft"); PlayerPrefs.DeleteKey("G920Cal_SteerRight"); } internal static AxisId GetThrottleAxis() { return (AxisId)Mathf.Clamp(PlayerPrefs.GetInt("G920Axis_Throttle", 1), 0, 7); } internal static AxisId GetBrakeAxis() { return (AxisId)Mathf.Clamp(PlayerPrefs.GetInt("G920Axis_Brake", 5), 0, 7); } internal static AxisId GetClutchAxis() { return (AxisId)Mathf.Clamp(PlayerPrefs.GetInt("G920Axis_Clutch", 4), 0, 7); } internal static void SetThrottleAxis(AxisId axis) { PlayerPrefs.SetInt("G920Axis_Throttle", (int)axis); PlayerPrefs.DeleteKey("G920Cal_ThrottleReleased"); PlayerPrefs.DeleteKey("G920Cal_ThrottlePressed"); } internal static void SetBrakeAxis(AxisId axis) { PlayerPrefs.SetInt("G920Axis_Brake", (int)axis); PlayerPrefs.DeleteKey("G920Cal_BrakeReleased"); PlayerPrefs.DeleteKey("G920Cal_BrakePressed"); } internal static void SetClutchAxis(AxisId axis) { PlayerPrefs.SetInt("G920Axis_Clutch", (int)axis); PlayerPrefs.DeleteKey("G920Cal_ClutchReleased"); PlayerPrefs.DeleteKey("G920Cal_ClutchPressed"); } private static void DetectWheelOnce() { if (!ShouldApply()) { return; } string[] joystickNames; try { joystickNames = Input.GetJoystickNames(); } catch (Exception ex) { _log.LogWarning((object)("Failed to query joystick names: " + ex.GetType().Name + ": " + ex.Message)); return; } if (joystickNames == null || joystickNames.Length == 0) { LogDebug("No joysticks detected by Unity."); } else if (_logDetectedDevices != null && _logDetectedDevices.Value) { for (int i = 0; i < joystickNames.Length; i++) { _log.LogInfo((object)$"Joystick {i + 1}: '{joystickNames[i]}'"); } } } private static void TryInitLogitech() { if (_logiAvailable || (_logiInitAttempted && Time.unscaledTime < _logiNextInitAttemptTime)) { return; } _logiInitAttempted = true; _logiInitAttemptCount++; try { _logiIndex = 0; bool flag = _ignoreXInputControllers == null || _ignoreXInputControllers.Value; if (TryInitLogitechInternal(flag)) { return; } if (TryInitLogitechInternal(!flag)) { _log.LogInfo((object)$"Logitech SDK initialized with ignoreXInputControllers={!flag} fallback."); return; } if (_logiInitAttemptCount == 1) { _log.LogInfo((object)"Logitech SDK not ready yet; retrying..."); _log.LogInfo((object)"If this never initializes, make sure Logitech G HUB/LGS is installed and the wheel is connected/powered."); } else if (_logiInitAttemptCount == 5) { _log.LogWarning((object)"Still retrying Logitech SDK init (wheel/G HUB may still be starting). You can also use 'Retry SDK' in wheel.exe."); } else { LogDebug($"Logitech SDK init still not ready (attempt {_logiInitAttemptCount})."); } _logiNextInitAttemptTime = Time.unscaledTime + 3f; } catch (DllNotFoundException ex) { _logiAvailable = false; _log.LogWarning((object)"Logitech SDK DLL not found (LogitechSteeringWheelEnginesWrapper.dll). FFB/input disabled."); LogDebug(ex.ToString()); _logiNextInitAttemptTime = Time.unscaledTime + 10f; } catch (BadImageFormatException ex2) { _logiAvailable = false; _log.LogWarning((object)"Logitech SDK DLL is wrong architecture. FFB/input disabled."); LogDebug(ex2.ToString()); _logiNextInitAttemptTime = Time.unscaledTime + 10f; } catch (Exception ex3) { _logiAvailable = false; _log.LogWarning((object)("Logitech SDK init threw " + ex3.GetType().Name + ". FFB/input disabled.")); LogDebug(ex3.ToString()); _logiNextInitAttemptTime = Time.unscaledTime + 10f; } } private static bool TryInitLogitechInternal(bool ignoreXInputControllers) { if (!LogitechGSDK.LogiSteeringInitialize(ignoreXInputControllers)) { return false; } _logiIgnoreXInputUsed = ignoreXInputControllers; _logiAvailable = true; _log.LogMessage((object)$"Logitech SDK initialized (ignoreXInputControllers={ignoreXInputControllers})."); ApplyControllerPropertiesIfReady(); ApplyWheelRangeIfReady(); return true; } internal static void ForceReinitLogitech() { ForceReinitLogitech(forceReconnect: false); } internal static void ForceReinitLogitech(bool forceReconnect) { try { if (_logiAvailable) { SafeLogiUpdate(); _logiConnected = LogitechGSDK.LogiIsConnected(_logiIndex); } } catch { } if (!forceReconnect && _logiAvailable && _logiConnected) { ApplyControllerPropertiesIfReady(); ApplyWheelRangeIfReady(); return; } if (forceReconnect && _log != null) { _log.LogInfo((object)"Forcing Logitech SDK reconnect..."); } _logiAvailable = false; _logiConnected = false; _logiInitAttempted = false; _logiInitAttemptCount = 0; _logiNextInitAttemptTime = 0f; try { StopAllForces(); LogitechGSDK.LogiSteeringShutdown(); } catch { } _logiWasConnected = false; _logiLastName = null; _logiLastPath = null; TryInitLogitech(); } internal static string GetLogitechStatus() { if (!_logiInitAttempted) { return "Not initialized"; } if (!_logiAvailable) { return "Retrying"; } if (!_logiConnected) { return "No wheel"; } return "Connected"; } private static void MaybeLogWheelDetected() { if (_log == null) { return; } if (!_logiConnected) { _logiWasConnected = false; return; } string text = TryGetWheelFriendlyName(_logiIndex); string text2 = TryGetWheelDevicePath(_logiIndex); bool num = !_logiWasConnected || (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, _logiLastName, StringComparison.Ordinal)) || (!string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, _logiLastPath, StringComparison.Ordinal)); _logiWasConnected = true; if (!string.IsNullOrWhiteSpace(text)) { _logiLastName = text; } if (!string.IsNullOrWhiteSpace(text2)) { _logiLastPath = text2; } if (!num) { return; } if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2)) { _log.LogMessage((object)("Wheel detected: " + text + " (" + text2 + ")")); return; } if (!string.IsNullOrWhiteSpace(text)) { _log.LogMessage((object)("Wheel detected: " + text)); return; } if (!string.IsNullOrWhiteSpace(text2)) { _log.LogMessage((object)("Wheel detected: " + text2)); return; } try { if (LogitechGSDK.LogiIsModelConnected(_logiIndex, 27)) { _log.LogMessage((object)"Wheel detected: Logitech G920"); } else if (LogitechGSDK.LogiIsModelConnected(_logiIndex, 26)) { _log.LogMessage((object)"Wheel detected: Logitech G29"); } else { _log.LogMessage((object)"Wheel detected."); } } catch { _log.LogMessage((object)"Wheel detected."); } } private static string TryGetWheelFriendlyName(int index) { try { StringBuilder stringBuilder = new StringBuilder(256); if (LogitechGSDK.LogiGetFriendlyProductName(index, stringBuilder, stringBuilder.Capacity)) { string text = stringBuilder.ToString(); return string.IsNullOrWhiteSpace(text) ? null : text.Trim(); } } catch { } return null; } private static string TryGetWheelDevicePath(int index) { try { StringBuilder stringBuilder = new StringBuilder(512); if (LogitechGSDK.LogiGetDevicePath(index, stringBuilder, stringBuilder.Capacity)) { string text = stringBuilder.ToString(); return string.IsNullOrWhiteSpace(text) ? null : text.Trim(); } } catch { } return null; } internal static void SetWheelLastInput(float steer, float accel) { _wheelLastUpdateFrame = Time.frameCount; _wheelLastUpdateTime = Time.unscaledTime; _wheelLastSteer = Mathf.Clamp(steer, -1f, 1f); _wheelLastAccel = Mathf.Clamp(accel, -1f, 1f); } internal static bool TryGetWheelLastInput(out float steer, out float accel) { steer = 0f; accel = 0f; if (Time.frameCount != _wheelLastUpdateFrame) { return false; } steer = _wheelLastSteer; accel = _wheelLastAccel; return true; } internal static bool TryGetWheelLastInputRecent(float maxAgeSeconds, out float steer, out float accel) { steer = 0f; accel = 0f; if (Time.unscaledTime - _wheelLastUpdateTime > maxAgeSeconds) { return false; } steer = _wheelLastSteer; accel = _wheelLastAccel; return true; } internal static void SetWheelMenuActive(bool active) { if (active) { _wheelMenuHeartbeatTime = Time.unscaledTime; } } internal static void SetBindingCaptureActive(bool active) { if (active) { _bindingCaptureHeartbeatTime = Time.unscaledTime; } } internal static void SetFfbPageActive(bool active) { if (active) { _ffbPageHeartbeatTime = Time.unscaledTime; ApplyControllerPropertiesIfReady(true); } else { ApplyControllerPropertiesIfReady(null); } } private static bool IsWheelMenuActive() { return Time.unscaledTime - _wheelMenuHeartbeatTime < 0.5f; } private static bool IsFfbPageActive() { return Time.unscaledTime - _ffbPageHeartbeatTime < 0.5f; } internal static bool IsBindingCaptureActive() { return Time.unscaledTime - _bindingCaptureHeartbeatTime < 0.5f; } internal static bool TryGetLogiState(out LogitechGSDK.DIJOYSTATE2ENGINES state) { state = default(LogitechGSDK.DIJOYSTATE2ENGINES); if (!ShouldApply()) { return false; } if (!_logiInitAttempted) { TryInitLogitech(); } if (!_logiAvailable) { return false; } if (!SafeLogiUpdate()) { return false; } _logiConnected = LogitechGSDK.LogiIsConnected(_logiIndex); if (!_logiConnected) { _logiWasConnected = false; return false; } MaybeLogWheelDetected(); state = LogitechGSDK.LogiGetStateCSharp(_logiIndex); EnsureDefaultCalibrationFromState(state); return true; } private static void EnsureDefaultCalibrationFromState(LogitechGSDK.DIJOYSTATE2ENGINES state) { int axisValue = GetAxisValue(state, GetThrottleAxis()); if (!PlayerPrefs.HasKey("G920Cal_ThrottleReleased")) { PlayerPrefs.SetInt("G920Cal_ThrottleReleased", axisValue); } if (!PlayerPrefs.HasKey("G920Cal_ThrottlePressed")) { PlayerPrefs.SetInt("G920Cal_ThrottlePressed", GuessPressedFromReleased(PlayerPrefs.GetInt("G920Cal_ThrottleReleased"))); } int axisValue2 = GetAxisValue(state, GetBrakeAxis()); if (!PlayerPrefs.HasKey("G920Cal_BrakeReleased")) { PlayerPrefs.SetInt("G920Cal_BrakeReleased", axisValue2); } if (!PlayerPrefs.HasKey("G920Cal_BrakePressed")) { PlayerPrefs.SetInt("G920Cal_BrakePressed", GuessPressedFromReleased(PlayerPrefs.GetInt("G920Cal_BrakeReleased"))); } int axisValue3 = GetAxisValue(state, GetClutchAxis()); if (!PlayerPrefs.HasKey("G920Cal_ClutchReleased")) { PlayerPrefs.SetInt("G920Cal_ClutchReleased", axisValue3); } if (!PlayerPrefs.HasKey("G920Cal_ClutchPressed")) { PlayerPrefs.SetInt("G920Cal_ClutchPressed", GuessPressedFromReleased(PlayerPrefs.GetInt("G920Cal_ClutchReleased"))); } if (!PlayerPrefs.HasKey("G920Cal_SteerCenter")) { PlayerPrefs.SetInt("G920Cal_SteerCenter", state.lX); } if (!PlayerPrefs.HasKey("G920Cal_SteerLeft")) { PlayerPrefs.SetInt("G920Cal_SteerLeft", -32768); } if (!PlayerPrefs.HasKey("G920Cal_SteerRight")) { PlayerPrefs.SetInt("G920Cal_SteerRight", 32767); } } private static int GuessPressedFromReleased(int released) { if (released >= 16000) { return -32768; } if (released <= -16000) { return 32767; } return -32768; } internal static bool HasCalibration() { if (PlayerPrefs.HasKey("G920Cal_SteerLeft") && PlayerPrefs.HasKey("G920Cal_SteerRight") && PlayerPrefs.HasKey("G920Cal_ThrottleReleased") && PlayerPrefs.HasKey("G920Cal_ThrottlePressed") && PlayerPrefs.HasKey("G920Cal_BrakeReleased")) { return PlayerPrefs.HasKey("G920Cal_BrakePressed"); } return false; } internal static void ClearCalibration() { PlayerPrefs.DeleteKey("G920Cal_SteerCenter"); PlayerPrefs.DeleteKey("G920Cal_SteerLeft"); PlayerPrefs.DeleteKey("G920Cal_SteerRight"); PlayerPrefs.DeleteKey("G920Cal_ThrottleReleased"); PlayerPrefs.DeleteKey("G920Cal_ThrottlePressed"); PlayerPrefs.DeleteKey("G920Cal_BrakeReleased"); PlayerPrefs.DeleteKey("G920Cal_BrakePressed"); PlayerPrefs.DeleteKey("G920Cal_ClutchReleased"); PlayerPrefs.DeleteKey("G920Cal_ClutchPressed"); } internal static float NormalizeSteering(int rawX) { int @int = PlayerPrefs.GetInt("G920Cal_SteerLeft", -32768); int int2 = PlayerPrefs.GetInt("G920Cal_SteerRight", 32767); int int3 = PlayerPrefs.GetInt("G920Cal_SteerCenter", (@int + int2) / 2); if (int2 == @int) { return 0f; } float num2; if (rawX >= int3) { float num = int2 - int3; num2 = ((num <= 0.001f) ? 0f : ((float)(rawX - int3) / num)); } else { float num3 = int3 - @int; num2 = ((num3 <= 0.001f) ? 0f : ((float)(-(int3 - rawX)) / num3)); } num2 = Mathf.Clamp(num2, -1f, 1f); float steeringDeadzone = GetSteeringDeadzone(); float num4 = Mathf.Abs(num2); num2 = ((!(num4 <= steeringDeadzone)) ? (Mathf.Sign(num2) * Mathf.Clamp01((num4 - steeringDeadzone) / Mathf.Max(0.0001f, 1f - steeringDeadzone))) : 0f); float steeringGain = GetSteeringGain(); return Mathf.Clamp(num2 * steeringGain, -1f, 1f); } internal static float NormalizePedal(int rawAxis, PedalKind kind) { string text; string text2; switch (kind) { case PedalKind.Throttle: text = "G920Cal_ThrottleReleased"; text2 = "G920Cal_ThrottlePressed"; break; case PedalKind.Brake: text = "G920Cal_BrakeReleased"; text2 = "G920Cal_BrakePressed"; break; default: text = "G920Cal_ClutchReleased"; text2 = "G920Cal_ClutchPressed"; break; } int @int = PlayerPrefs.GetInt(text, 32767); int int2 = PlayerPrefs.GetInt(text2, -32768); float num = Mathf.InverseLerp((float)@int, (float)int2, (float)rawAxis); num = Mathf.Clamp01(num); if (num < 0.05f) { num = 0f; } return num; } internal static int GetAxisValue(LogitechGSDK.DIJOYSTATE2ENGINES state, AxisId axis) { switch (axis) { case AxisId.lX: return state.lX; case AxisId.lY: return state.lY; case AxisId.lZ: return state.lZ; case AxisId.lRx: return state.lRx; case AxisId.lRy: return state.lRy; case AxisId.lRz: return state.lRz; case AxisId.slider0: if (state.rglSlider == null || state.rglSlider.Length == 0) { return 0; } return state.rglSlider[0]; case AxisId.slider1: if (state.rglSlider == null || state.rglSlider.Length <= 1) { return 0; } return state.rglSlider[1]; default: return 0; } } private static void ShutdownLogitech() { if (!_logiAvailable) { return; } try { StopAllForces(); LogitechGSDK.LogiSteeringShutdown(); } catch { } } internal static void StartFfbTest(FfbTestEffect effect, float durationSeconds = 2.5f) { if (effect == FfbTestEffect.None) { StopFfbTest(); return; } _ffbTestEffect = effect; _ffbTestEndTime = Time.unscaledTime + Mathf.Clamp(durationSeconds, 0.25f, 10f); LogDebug("FFB test start: " + effect); } internal static void StopFfbTest() { if (_ffbTestEffect != 0) { LogDebug("FFB test stop"); } _ffbTestEffect = FfbTestEffect.None; _ffbTestEndTime = 0f; StopAllForces(); } internal static bool IsFfbTestActive() { if (_ffbTestEffect == FfbTestEffect.None) { return false; } if (Time.unscaledTime < _ffbTestEndTime) { return true; } _ffbTestEffect = FfbTestEffect.None; _ffbTestEndTime = 0f; return false; } private static void UpdateFfb() { if (!ShouldApply()) { return; } TryInitLogitech(); if (!_logiAvailable) { return; } bool flag = IsWheelMenuActive(); bool flag2 = IsFfbPageActive(); if (!flag2 && !GetFfbEnabled()) { return; } if ((PauseSystem.paused || IsInputLocked()) && (!flag || !GetFfbEnabled()) && !flag2) { StopAllForces(); } else if (_isInWalkingMode && (!flag || !GetFfbEnabled()) && !flag2) { StopAllForces(); } else { if (!SafeLogiUpdate()) { return; } _logiConnected = LogitechGSDK.LogiIsConnected(_logiIndex); if (!_logiConnected) { _logiWasConnected = false; return; } MaybeLogWheelDetected(); if (!LogitechGSDK.LogiHasForceFeedback(_logiIndex)) { return; } bool flag3 = flag2 || (flag && GetFfbEnabled()); float num = (flag3 ? 0f : Mathf.Clamp(_currentSpeedKmh, 0f, 200f)); int num2 = Mathf.RoundToInt(Mathf.Lerp(5f, 35f, Mathf.Clamp01(num / 120f)) * GetFfbDamperGain()); num2 = Mathf.Clamp(num2, 0, 100); LogitechGSDK.LogiPlayDamperForce(_logiIndex, num2); int num3 = Mathf.RoundToInt(Mathf.Lerp(20f, 95f, Mathf.Clamp01(num / 100f)) * GetFfbSpringGain()); num3 = Mathf.Clamp(num3, 0, 100); LogitechGSDK.LogiPlaySpringForce(_logiIndex, 0, num3, num3); if (IsFfbTestActive()) { LogitechGSDK.LogiStopDirtRoadEffect(_logiIndex); LogitechGSDK.LogiStopBumpyRoadEffect(_logiIndex); if (_ffbTestEffect == FfbTestEffect.Shake) { int num4 = Mathf.RoundToInt(Mathf.Sin(Time.unscaledTime * 14f) * 70f); num4 = Mathf.Clamp(num4, -100, 100); LogitechGSDK.LogiPlayConstantForce(_logiIndex, num4); } else if (_ffbTestEffect == FfbTestEffect.Bumpy) { LogitechGSDK.LogiStopConstantForce(_logiIndex); LogitechGSDK.LogiPlayBumpyRoadEffect(_logiIndex, 25); } } else if (!flag3 && _isOffRoad && num > 5f) { int num5 = Mathf.RoundToInt(Mathf.Lerp(10f, 35f, Mathf.Clamp01(num / 70f))); num5 = Mathf.Clamp(num5, 0, 100); LogitechGSDK.LogiPlayDirtRoadEffect(_logiIndex, num5); } else if (!flag3 && _isSliding) { LogitechGSDK.LogiPlayBumpyRoadEffect(_logiIndex, 8); } else { LogitechGSDK.LogiStopDirtRoadEffect(_logiIndex); LogitechGSDK.LogiStopBumpyRoadEffect(_logiIndex); } } } private static bool IsInputLocked() { try { sInputManager[] players = sInputManager.players; if (players == null || players.Length == 0 || (Object)(object)players[0] == (Object)null) { return false; } return players[0].lockInput || players[0].freeCamMode; } catch { return false; } } private static bool SafeLogiUpdate() { try { return LogitechGSDK.LogiUpdate(); } catch { return false; } } private static void StopAllForces() { if (!_logiAvailable) { return; } try { LogitechGSDK.LogiStopSpringForce(_logiIndex); LogitechGSDK.LogiStopDamperForce(_logiIndex); LogitechGSDK.LogiStopDirtRoadEffect(_logiIndex); LogitechGSDK.LogiStopBumpyRoadEffect(_logiIndex); LogitechGSDK.LogiStopConstantForce(_logiIndex); LogitechGSDK.LogiStopSurfaceEffect(_logiIndex); } catch { } } private static void ApplyControllerPropertiesIfReady(bool? forceEnableOverride) { if (!_logiAvailable) { return; } try { bool forceEnable = (forceEnableOverride.HasValue ? forceEnableOverride.Value : GetFfbEnabled()); LogitechGSDK.LogiControllerPropertiesData properties = default(LogitechGSDK.LogiControllerPropertiesData); properties.forceEnable = forceEnable; properties.overallGain = Mathf.RoundToInt(GetFfbOverallGain() * 100f); properties.springGain = Mathf.RoundToInt(GetFfbSpringGain() * 100f); properties.damperGain = Mathf.RoundToInt(GetFfbDamperGain() * 100f); properties.defaultSpringEnabled = false; properties.defaultSpringGain = 0; properties.combinePedals = false; properties.wheelRange = GetWheelRange(); properties.gameSettingsEnabled = true; properties.allowGameSettings = true; LogitechGSDK.LogiSetPreferredControllerProperties(properties); } catch { } } private static void ApplyControllerPropertiesIfReady() { ApplyControllerPropertiesIfReady(null); } private static void ApplyWheelRangeIfReady() { if (!_logiAvailable) { return; } try { LogitechGSDK.LogiSetOperatingRange(_logiIndex, GetWheelRange()); } catch { } } private static float ParseDesktopIconFloat(string value, float fallback, string label) { if (string.IsNullOrWhiteSpace(value)) { return fallback; } string s = value.Trim(); if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } if (float.TryParse(s, out result)) { return result; } LogDebug($"Failed to parse {label}='{value}', using {fallback:0.###}."); return fallback; } private static void DesktopDotExe_Setup_Postfix(object __instance) { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) if (__instance == null) { return; } DesktopDotExe val = (DesktopDotExe)((__instance is DesktopDotExe) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } bool visible = _desktopMenuIconVisible == null || _desktopMenuIconVisible.Value; float num = ParseDesktopIconFloat((_desktopMenuIconX != null) ? _desktopMenuIconX.Value : null, 4f, "wheel_menu_icon_x"); float num2 = ParseDesktopIconFloat((_desktopMenuIconY != null) ? _desktopMenuIconY.Value : null, 3.25f, "wheel_menu_icon_y"); Vector2 position = default(Vector2); ((Vector2)(ref position))..ctor(num, num2); File val2 = null; foreach (File file in val.files) { if (file != null && string.Equals(file.name, "wheel", StringComparison.OrdinalIgnoreCase)) { val2 = file; break; } } if (val2 == null) { File item = new File(((ScreenProgram)val).R, val) { name = "wheel", type = (FileType)1, data = "listener_G920Menu", icon = 7, iconHover = 7, position = position, visible = visible, cantFolder = false }; val.files.Add(item); } else { val2.icon = 7; val2.iconHover = 7; val2.position = position; val2.visible = visible; } Transform transform = ((Component)val).transform; if ((Object)(object)transform.Find("G920Menu") == (Object)null) { GameObject val3 = new GameObject("G920Menu"); val3.transform.SetParent(transform, false); val3.AddComponent<WheelMenuWindow>(); } } internal static void LogDebug(string message) { if (_debugLogging != null && _debugLogging.Value && _log != null) { _log.LogInfo((object)("[debug] " + message)); } } private void Awake() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown //IL_015a: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "enable_mod", true, "Enables/disables the mod entirely."); _logDetectedDevices = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "log_detected_devices", true, "Log joystick names detected by Unity on startup."); _debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "debug_logging", false, "Log debug information."); _desktopMenuIconVisible = ((BaseUnityPlugin)this).Config.Bind<bool>("Menu", "show_wheel_menu_icon", true, "Show/hide the Wheel Settings icon on the Main Menu."); _desktopMenuIconX = ((BaseUnityPlugin)this).Config.Bind<string>("Menu", "wheel_menu_icon_x", "4", "Main Menu icon X position. Example: 4"); _desktopMenuIconY = ((BaseUnityPlugin)this).Config.Bind<string>("Menu", "wheel_menu_icon_y", "3.25", "Main Menu icon Y position. Example: 3.25"); _ignoreXInputControllers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ignore_xinput_controllers", true, "Pass 'ignoreXInputControllers' to the Logitech SDK init (recommended)."); if (!_enableMod.Value) { _log.LogInfo((object)"G920 mod disabled via config."); return; } Harmony val = new Harmony("shibe.easydeliveryco.logiwheel"); PatchByName(val, "DesktopDotExe", "Setup", null, "DesktopDotExe_Setup_Postfix"); PatchByName(val, "sCarController", "Update", "SCarController_Update_Prefix"); PatchByName(val, "sInputManager", "GetInput", null, "SInputManager_GetInput_Postfix"); DetectWheelOnce(); TryInitLogitech(); _log.LogInfo((object)"EasyLogiWheelSupport loaded."); } private void Update() { UpdateFfb(); } private void OnDestroy() { ShutdownLogitech(); } private static void SInputManager_GetInput_Postfix(sInputManager __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) if (ShouldApply() && !((Object)(object)__instance == (Object)null)) { InjectWheelButtonBindings(__instance); if (!__instance.lockInput && !PauseSystem.paused && !_isInWalkingMode && TryGetCachedWheelState(out var state)) { int axisValue = GetAxisValue(state, GetSteeringAxis()); int axisValue2 = GetAxisValue(state, GetThrottleAxis()); int axisValue3 = GetAxisValue(state, GetBrakeAxis()); float num = NormalizeSteering(axisValue); float num2 = NormalizePedal(axisValue2, PedalKind.Throttle); float num3 = NormalizePedal(axisValue3, PedalKind.Brake); float num4 = Mathf.Clamp(num2 - num3, -1f, 1f); __instance.driveInput = new Vector2(num, num4); __instance.breakPressed = num3 > 0.1f; SetWheelLastInput(num, num4); } } } private static void InjectWheelButtonBindings(sInputManager input) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)input == (Object)null || !TryGetCachedWheelState(out var _)) { return; } BindingInput modifierBinding = GetModifierBinding(); BindingLayer layer = ((modifierBinding.Kind != 0 && IsBindingDownForCurrentFrame(modifierBinding)) ? BindingLayer.Modified : BindingLayer.Normal); bool allowPovBinds = !_isInWalkingMode && !PauseSystem.paused && !input.lockInput; if (PauseSystem.paused && !IsBindingCaptureActive() && TryGetPov8Vector(out var v)) { Vector2 val = -v; if (val != Vector2.zero) { if (Mathf.Abs(val.x) > 0.1f && Mathf.Abs(val.y) > 0.1f) { ((Vector2)(ref val)).Normalize(); } input.mouseInput = val; } } if (IsWheelMenuActive()) { return; } if (_isInWalkingMode && !PauseSystem.paused && !input.lockInput && TryGetPov8Vector(out var v2)) { Vector2 val2 = -v2; if (val2 != Vector2.zero) { input.playerInput = val2; } } BindingInput binding = GetBinding(layer, ButtonBindAction.InteractOk); if (binding.Kind != 0) { if (Pressed(binding)) { input.selectPressed = true; } if (Released(binding)) { input.selectReleased = true; } } BindingInput binding2 = GetBinding(layer, ButtonBindAction.Back); if (binding2.Kind != 0) { if (Pressed(binding2)) { input.backPressed = true; } if (Released(binding2)) { input.backReleased = true; } } BindingInput binding3 = GetBinding(layer, ButtonBindAction.Pause); if (binding3.Kind != 0 && Pressed(binding3)) { input.pausePressed = true; } BindingInput binding4 = GetBinding(layer, ButtonBindAction.MapItems); if (binding4.Kind != 0) { if (_isInWalkingMode) { if (Pressed(binding4)) { input.inventoryPressed = true; } if (Released(binding4)) { input.inventoryReleased = true; } if (Down(binding4)) { input.inventoryHeld = true; } } else if (Pressed(binding4)) { input.mapPressed = true; } } BindingInput binding5 = GetBinding(layer, ButtonBindAction.Camera); if (binding5.Kind != 0 && Pressed(binding5)) { input.cameraPressed = true; } BindingInput binding6 = GetBinding(layer, ButtonBindAction.ResetVehicle); if (binding6.Kind != 0) { if (Pressed(binding6)) { input.resetPressed = true; } if (Down(binding6)) { input.resetHeld = true; } } BindingInput binding7 = GetBinding(layer, ButtonBindAction.Headlights); if (binding7.Kind != 0 && Pressed(binding7)) { input.headlightsPressed = true; } BindingInput binding8 = GetBinding(layer, ButtonBindAction.Horn); if (binding8.Kind != 0 && Pressed(binding8)) { input.hornPressed = true; } if (!_isInWalkingMode && !PauseSystem.paused && !input.lockInput) { Vector2 zero = Vector2.zero; bool flag = false; BindingInput binding9 = GetBinding(layer, ButtonBindAction.RadioPower); if (binding9.Kind != 0 && Pressed(binding9)) { zero.y = -1f; flag = true; } BindingInput binding10 = GetBinding(layer, ButtonBindAction.RadioScanRight); if (binding10.Kind != 0 && Pressed(binding10)) { zero.x = 1f; flag = true; } BindingInput binding11 = GetBinding(layer, ButtonBindAction.RadioScanLeft); if (binding11.Kind != 0 && Pressed(binding11)) { zero.x = -1f; flag = true; } BindingInput binding12 = GetBinding(layer, ButtonBindAction.RadioScanToggle); if (binding12.Kind != 0 && Pressed(binding12)) { zero.y = 1f; flag = true; } if (flag) { input.radioPressed = true; input.radioInput = zero; } } bool Down(BindingInput b) { if (b.Kind == BindingKind.Pov && !allowPovBinds) { return false; } return IsBindingDownForCurrentFrame(b); } bool Pressed(BindingInput b) { if (b.Kind == BindingKind.Pov && !allowPovBinds) { return false; } return IsBindingPressedThisFrameForCurrentFrame(b); } bool Released(BindingInput b) { if (b.Kind == BindingKind.Pov && !allowPovBinds) { return false; } return IsBindingReleasedThisFrameForCurrentFrame(b); } } private static void SCarController_Update_Prefix(object __instance) { //IL_003b: 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) if (!ShouldApply() || __instance == null) { return; } sCarController val = (sCarController)((__instance is sCarController) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } _isInWalkingMode = val.GuyActive; if ((Object)(object)val.rb != (Object)null) { Vector3 linearVelocity = val.rb.linearVelocity; _currentSpeedKmh = ((Vector3)(ref linearVelocity)).magnitude * 3.6f; } if (val.wheels == null) { return; } float num = 0f; bool isOffRoad = false; Wheel[] wheels = val.wheels; foreach (Wheel val2 in wheels) { if (val2 != null) { num += val2.slide; if ((Object)(object)val2.suspention != (Object)null && string.Equals(val2.suspention.contactTag, "offRoad", StringComparison.OrdinalIgnoreCase)) { isOffRoad = true; } } } _isOffRoad = isOffRoad; _isSliding = num > 2f; } private static void PatchByName(Harmony harmony, string typeName, string methodName, string prefix = null, string postfix = null) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown Type type = AccessTools.TypeByName(typeName); if (type == null) { LogDebug("Type '" + typeName + "' not found for patch " + methodName + "."); return; } MethodInfo methodInfo; try { methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); } catch (AmbiguousMatchException) { methodInfo = ResolveAmbiguousMethod(type, methodName); } if (methodInfo == null) { LogDebug("Method '" + typeName + "." + methodName + "' not found for patch."); return; } HarmonyMethod val = null; HarmonyMethod val2 = null; if (!string.IsNullOrWhiteSpace(prefix)) { val = new HarmonyMethod(typeof(Plugin), prefix, (Type[])null); } if (!string.IsNullOrWhiteSpace(postfix)) { val2 = new HarmonyMethod(typeof(Plugin), postfix, (Type[])null); } harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static MethodInfo ResolveAmbiguousMethod(Type type, string methodName) { MethodInfo result = null; int num = int.MaxValue; Type type2 = type; while (type2 != null) { MethodInfo[] methods = type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal)) { int num2 = methodInfo.GetParameters().Length; if (num2 == 0) { return methodInfo; } if (num2 < num) { num = num2; result = methodInfo; } } } type2 = type2.BaseType; } return result; } } public class UIUtil { public DesktopDotExe M; public GamepadNavigation Nav; public MiniRenderer R; public void Label(string name, float x, float y) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); R.fontOptions.alignment = (Alignment)1; R.fput(name, x, y, 0f, 13f, 0f, -1); } public void ValueLabel(string value, float x, float y) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) R.fontOptions.alignment = (Alignment)2; R.fput(value, x, y, 0f, 13f, 0f, -1); } public bool Button(string name, float x, float y) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); if (M.MouseOver((int)x - 2, (int)y, name.Length * 8 + 4, 8)) { ((ScreenProgram)M).mouseIcon = 128; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fontOptions.alignment = (Alignment)2; R.fput(name, x - 4f, y, 0f, 13f, 0f, -1); return false; } public bool SimpleButton(string name, float x, float y) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); if (M.MouseOver((int)x - 32, (int)y, 64, 8)) { ((ScreenProgram)M).mouseIcon = 128; x += 4f; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fontOptions.alignment = (Alignment)1; R.fput(name, x, y, 0f, 13f, 0f, -1); return false; } public bool SimpleButtonRaw(string name, float x, float y) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (M.MouseOver((int)x - 32, (int)y, 64, 8)) { ((ScreenProgram)M).mouseIcon = 128; x += 4f; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fontOptions.alignment = (Alignment)1; R.fput(name, x, y, 0f, 13f, 0f, -1); return false; } public bool FancyButton(string title, float centerX, float y) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) title = LocalizationDictionary.Translate(title); R.fontOptions.alignment = (Alignment)1; float num = R.fput(title, centerX, -32f, 0f, 13f, 0f, -1) / 8f + 1f; float num2 = centerX / 8f; float num3 = y / 8f; if (M.MouseOver((int)((num2 - num / 2f) * 8f), (int)(num3 * 8f), (int)((num + 1f) * 8f), 24)) { ((ScreenProgram)M).mouseIcon = 128; num += 2f; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; num -= 2f; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fput(title, num2 * 8f + 4f, num3 * 8f + 8f, 0f, 13f, 0f, -1); ((ScreenProgram)M).drawBox(num2 - num / 2f, num3, num, 2f); return false; } public bool CycleButton(string name, string value, float x, float y) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); string text = "[" + value + "]"; R.put(text, x + 4f, y); if (M.MouseOver((int)x - 2, (int)y, text.Length * 8 + 4, 8)) { ((ScreenProgram)M).mouseIcon = 128; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fontOptions.alignment = (Alignment)2; R.fput(name, x - 4f, y, 0f, 13f, 0f, -1); return false; } public bool CycleButtonRaw(string name, string value, float x, float y) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) string text = "[" + value + "]"; R.put(text, x + 4f, y); if (M.MouseOver((int)x - 2, (int)y, text.Length * 8 + 4, 8)) { ((ScreenProgram)M).mouseIcon = 128; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { return true; } } R.fontOptions.alignment = (Alignment)2; R.fput(name, x - 4f, y, 0f, 13f, 0f, -1); return false; } public bool? Toggle(string name, bool state, float x, float y) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); bool? result = null; string text = "[" + (state ? "on" : "off") + "]"; R.put(text, x + 4f, y); if (M.MouseOver((int)x - 2, (int)y, text.Length * 8 + 4, 8)) { ((ScreenProgram)M).mouseIcon = 128; name = ">" + name; if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; } if (((ScreenProgram)M).mouseButtonUp) { result = !state; } } R.fontOptions.alignment = (Alignment)2; R.fput(name, x - 4f, y, 0f, 13f, 0f, -1); return result; } public float? Slider(string name, float value, float x, float y, ref float mouseYLock) { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) name = LocalizationDictionary.Translate(name); float? result = null; int num = 10; for (int i = 0; i < num; i++) { R.spr(32f, 0f, x + 4f + (float)i * 8f, y, 8f, 8f); } float num2 = x + value * (float)num * 8f; R.spr(0f, 24f, num2, y, 8f, 8f); if (M.MouseOver((int)x - 8, (int)y, num * 8 + 16, 8)) { ((ScreenProgram)M).mouseIcon = 128; name = ">" + name; if (Nav != null && Nav.menuInput.x < 0f) { result = Mathf.Clamp01(value - Time.unscaledDeltaTime / 2f); } if (Nav != null && Nav.menuInput.x > 0f) { result = Mathf.Clamp01(value + Time.unscaledDeltaTime / 2f); } if (((ScreenProgram)M).mouseButton) { ((ScreenProgram)M).mouseIcon = 160; value = Mathf.InverseLerp(x + 4f, x + 4f + (float)num * 8f, ((ScreenProgram)M).mouse.x); value = Mathf.Clamp01(value); result = value; if (mouseYLock == 0f) { mouseYLock = ((ScreenProgram)M).mouse.y; } } } R.fontOptions.alignment = (Alignment)2; R.fput(name, x - 4f, y, 0f, 13f, 0f, -1); return result; } } public class WheelMenuWindow : MonoBehaviour { private enum BindingsPage { Axes, Global, Vehicle, Radio } private struct BindingConflict { public Plugin.BindingLayer Layer; public Plugin.ButtonBindAction Action; } private enum CalStep { None, SteeringCenter, SteeringLeft, SteeringRight, ThrottleReleased, ThrottlePressed, BrakeReleased, BrakePressed, ClutchReleased, ClutchPressed } private enum Page { Main, Ffb, Steering, Calibration, CalibrationWizard, Bindings, BindingCapture } public const string FileName = "wheel"; public const string ListenerName = "G920Menu"; public const string ListenerData = "listener_G920Menu"; private float _mouseYLock; private UIUtil _util; private Page _page; private CalStep _calStep; private BindingsPage _bindingsPage; private bool _bindingCaptureModifier; private Plugin.ButtonBindAction _bindingCaptureAction; private bool _bindingDupConfirmActive; private Plugin.BindingInput _bindingDupPendingCaptured; private Plugin.BindingLayer _bindingDupPendingLayer; private List<BindingConflict> _bindingDupConflicts; private static readonly Plugin.ButtonBindAction[] AllBindableActions = new Plugin.ButtonBindAction[12] { Plugin.ButtonBindAction.InteractOk, Plugin.ButtonBindAction.Back, Plugin.ButtonBindAction.MapItems, Plugin.ButtonBindAction.Pause, Plugin.ButtonBindAction.Camera, Plugin.ButtonBindAction.ResetVehicle, Plugin.ButtonBindAction.Headlights, Plugin.ButtonBindAction.Horn, Plugin.ButtonBindAction.RadioPower, Plugin.ButtonBindAction.RadioScanRight, Plugin.ButtonBindAction.RadioScanLeft, Plugin.ButtonBindAction.RadioScanToggle }; public void FrameUpdate(WindowView view) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_006c: 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_0088: 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_00dd: Unknown result type (might be due to invalid IL or missing references) if (view != null) { if (_util == null) { _util = new UIUtil(); } _util.M = view.M; _util.R = view.R; _util.Nav = view.M.nav; Rect p = default(Rect); ((Rect)(ref p))..ctor(view.position * 8f, view.size * 8f); ((Rect)(ref p)).position = ((Rect)(ref p)).position + new Vector2(8f, 8f); if (((ScreenProgram)_util.M).mouseButtonUp) { _mouseYLock = 0f; } if (_mouseYLock > 0f) { ((ScreenProgram)_util.M).mouse.y = _mouseYLock; } DrawMenu(p); } } public void BackButtonPressed() { if (_page == Page.CalibrationWizard) { _calStep = CalStep.None; _page = Page.Calibration; } else if (_page == Page.BindingCapture) { Plugin.LogDebug("Bindings: capture cancelled via back button"); _bindingCaptureModifier = false; _bindingDupConfirmActive = false; _page = Page.Bindings; } else if (_page != 0) { _calStep = CalStep.None; _page = Page.Main; } } private void DrawMenu(Rect p) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) float center = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f - 16f; float y = ((Rect)(ref p)).y + 10f; float num = 12f; float num2 = 4f; Plugin.SetWheelMenuActive(active: true); Plugin.SetFfbPageActive(_page == Page.Ffb); Plugin.SetBindingCaptureActive(_page == Page.BindingCapture); if (_page == Page.Main) { _util.Label("Wheel Settings", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += num + num2; } if (_page == Page.Main) { DrawMain(p, center, ref y, num, num2); } else if (_page == Page.Ffb) { DrawFfb(p, center, ref y, num, num2); } else if (_page == Page.Steering) { DrawSteering(p, center, ref y, num, num2); } else if (_page == Page.Calibration) { DrawCalibration(p, center, ref y, num, num2); } else if (_page == Page.CalibrationWizard) { DrawCalibrationWizard(p, center, ref y, num, num2); } else if (_page == Page.BindingCapture) { DrawBindingCapture(p, center, ref y, num, num2); } else { DrawBindings(p, center, ref y, num, num2); } } private void DrawMain(Rect p, float center, ref float y, float line, float sectionGap) { float num = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f; float num2 = 22f; if (_util.FancyButton("Force Feedback", num, y)) { _page = Page.Ffb; } y += num2; if (_util.FancyButton("Steering", num, y)) { _page = Page.Steering; } y += num2; if (_util.FancyButton("Calibration", num, y)) { _page = Page.Calibration; } y += num2; if (_util.FancyButton("Bindings", num, y)) { _page = Page.Bindings; Plugin.LogDebug("Bindings: opened bindings menu"); } float y2 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 30f; float y3 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 18f; _util.Label("Logitech SDK: " + Plugin.GetLogitechStatus(), num, y2); if (_util.SimpleButton("Retry SDK", num, y3)) { Plugin.ForceReinitLogitech(forceReconnect: true); } } private void DrawFfb(Rect p, float center, ref float y, float line, float sectionGap) { _util.Label("Force Feedback", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line; float x = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f; float y2 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 30f; float y3 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 18f; if (_util.SimpleButton("Reset Defaults", x, y2)) { Plugin.ResetFfbDefaults(); } if (_util.SimpleButton("Back", x, y3)) { _page = Page.Main; return; } y += sectionGap; bool ffbEnabled = Plugin.GetFfbEnabled(); bool? flag = _util.Toggle("Enable FFB", ffbEnabled, center, y); if (flag.HasValue) { Plugin.SetFfbEnabled(flag.Value); } y += line; float ffbOverallGain = Plugin.GetFfbOverallGain(); _util.ValueLabel($"{Mathf.RoundToInt(ffbOverallGain * 100f)}%", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float? num = _util.Slider("Strength", ffbOverallGain, center, y, ref _mouseYLock); if (num.HasValue) { Plugin.SetFfbOverallGain(num.Value); } y += line; float ffbSpringGain = Plugin.GetFfbSpringGain(); _util.ValueLabel($"{Mathf.RoundToInt(ffbSpringGain * 100f)}%", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float? num2 = _util.Slider("Spring", ffbSpringGain, center, y, ref _mouseYLock); if (num2.HasValue) { Plugin.SetFfbSpringGain(num2.Value); } y += line; float ffbDamperGain = Plugin.GetFfbDamperGain(); _util.ValueLabel($"{Mathf.RoundToInt(ffbDamperGain * 100f)}%", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float? num3 = _util.Slider("Damper", ffbDamperGain, center, y, ref _mouseYLock); if (num3.HasValue) { Plugin.SetFfbDamperGain(num3.Value); } } private void DrawSteering(Rect p, float center, ref float y, float line, float sectionGap) { _util.Label("Steering", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line; float x = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f; float y2 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 30f; float y3 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 18f; if (_util.SimpleButton("Reset Defaults", x, y2)) { Plugin.ResetSteeringDefaults(); } if (_util.SimpleButton("Back", x, y3)) { _page = Page.Main; return; } y += sectionGap; int wheelRange = Plugin.GetWheelRange(); _util.ValueLabel($"{wheelRange} deg", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float value = Mathf.InverseLerp(180f, 900f, (float)wheelRange); float? num = _util.Slider("Range", value, center, y, ref _mouseYLock); if (num.HasValue) { Plugin.SetWheelRange(Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(180f, 900f, num.Value)), 180, 900)); } y += line; float steeringGain = Plugin.GetSteeringGain(); _util.ValueLabel($"{steeringGain:0.00}x", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float value2 = Mathf.InverseLerp(0.5f, 3f, steeringGain); float? num2 = _util.Slider("Steer Sens", value2, center, y, ref _mouseYLock); if (num2.HasValue) { Plugin.SetSteeringGain(Mathf.Lerp(0.5f, 3f, num2.Value)); } y += line; float steeringDeadzone = Plugin.GetSteeringDeadzone(); _util.ValueLabel($"{Mathf.RoundToInt(steeringDeadzone * 100f)}%", ((Rect)(ref p)).x + ((Rect)(ref p)).width - 12f, y); float value3 = Mathf.InverseLerp(0f, 0.12f, steeringDeadzone); float? num3 = _util.Slider("Deadzone", value3, center, y, ref _mouseYLock); if (num3.HasValue) { Plugin.SetSteeringDeadzone(Mathf.Lerp(0f, 0.12f, num3.Value)); } } private void DrawBindings(Rect p, float center, ref float y, float line, float sectionGap) { //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) _util.Label("Bindings", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line; float x = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f; float y2 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 18f; float x2 = ((Rect)(ref p)).x + 40f; float x3 = ((Rect)(ref p)).x + ((Rect)(ref p)).width - 40f; if (_util.SimpleButtonRaw("Prev", x2, y2)) { _bindingsPage = PrevBindingsPage(_bindingsPage); Plugin.LogDebug("Bindings: page -> " + _bindingsPage); } if (_util.SimpleButton("Back", x, y2)) { _page = Page.Main; return; } if (_util.SimpleButtonRaw("Next", x3, y2)) { _bindingsPage = NextBindingsPage(_bindingsPage); Plugin.LogDebug("Bindings: page -> " + _bindingsPage); } y += sectionGap; _util.Label(GetBindingsPageTitle(_bindingsPage), ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line + sectionGap; int num = (int)(_bindingsPage + 1); _util.Label(num + "/" + 4, ((Rect)(ref p)).x + ((Rect)(ref p)).width - 18f, ((Rect)(ref p)).y + 10f); if (_bindingsPage == BindingsPage.Axes) { DrawBindingsAxes(p, center, ref y, line, sectionGap); } else if (_bindingsPage == BindingsPage.Global) { DrawBindingsButtonsPage(p, center, ref y, line, new Plugin.ButtonBindAction[5] { Plugin.ButtonBindAction.InteractOk, Plugin.ButtonBindAction.Back, Plugin.ButtonBindAction.MapItems, Plugin.ButtonBindAction.Pause, Plugin.ButtonBindAction.ResetVehicle }); } else if (_bindingsPage == BindingsPage.Vehicle) { DrawBindingsButtonsPage(p, center, ref y, line, new Plugin.ButtonBindAction[2] { Plugin.ButtonBindAction.Headlights, Plugin.ButtonBindAction.Horn }); } else { DrawBindingsButtonsPage(p, center, ref y, line, new Plugin.ButtonBindAction[4] { Plugin.ButtonBindAction.RadioPower, Plugin.ButtonBindAction.RadioScanRight, Plugin.ButtonBindAction.RadioScanLeft, Plugin.ButtonBindAction.RadioScanToggle }); } } private static BindingsPage NextBindingsPage(BindingsPage p) { int num = (int)(p + 1); if (num > 3) { num = 0; } return (BindingsPage)num; } private static BindingsPage PrevBindingsPage(BindingsPage p) { int num = (int)(p - 1); if (num < 0) { num = 3; } return (BindingsPage)num; } private static string GetBindingsPageTitle(BindingsPage p) { return p switch { BindingsPage.Axes => "Axis Mapping", BindingsPage.Global => "Global", BindingsPage.Vehicle => "Vehicle", BindingsPage.Radio => "Radio", _ => "Global", }; } private void DrawBindingsAxes(Rect p, float center, ref float y, float line, float sectionGap) { if (!Plugin.TryGetCachedWheelState(out var state)) { _util.Label("Wheel not detected (Logitech SDK not ready).", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); return; } Plugin.AxisId steeringAxis = Plugin.GetSteeringAxis(); Plugin.AxisId throttleAxis = Plugin.GetThrottleAxis(); Plugin.AxisId brakeAxis = Plugin.GetBrakeAxis(); Plugin.AxisId clutchAxis = Plugin.GetClutchAxis(); if (_util.CycleButtonRaw("Steering", steeringAxis.ToString(), center, y)) { Plugin.SetSteeringAxis(NextAxis(steeringAxis)); _calStep = CalStep.None; } y += line; if (_util.CycleButtonRaw("Throttle", throttleAxis.ToString(), center, y)) { Plugin.SetThrottleAxis(NextAxis(throttleAxis)); _calStep = CalStep.None; } y += line; if (_util.CycleButtonRaw("Brake", brakeAxis.ToString(), center, y)) { Plugin.SetBrakeAxis(NextAxis(brakeAxis)); _calStep = CalStep.None; } y += line; if (_util.CycleButtonRaw("Clutch", clutchAxis.ToString(), center, y)) { Plugin.SetClutchAxis(NextAxis(clutchAxis)); _calStep = CalStep.None; } y += line + sectionGap; int axisValue = Plugin.GetAxisValue(state, Plugin.GetSteeringAxis()); int axisValue2 = Plugin.GetAxisValue(state, Plugin.GetThrottleAxis()); int axisValue3 = Plugin.GetAxisValue(state, Plugin.GetBrakeAxis()); int axisValue4 = Plugin.GetAxisValue(state, Plugin.GetClutchAxis()); _util.Label($"steer={axisValue} thr={axisValue2}", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line - 2f; _util.Label($"brk={axisValue3} clu={axisValue4}", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); } private void DrawBindingsButtonsPage(Rect p, float center, ref float y, float line, Plugin.ButtonBindAction[] actions) { _ = ((Rect)(ref p)).x; _ = ((Rect)(ref p)).width / 2f; if (_util.CycleButtonRaw("Modifier", Plugin.GetBindingLabel(Plugin.GetModifierBinding()), center, y)) { _bindingCaptureModifier = true; _page = Page.BindingCapture; Plugin.LogDebug("Bindings: start capture for modifier"); return; } y += line + 3f; int num = 8; for (int i = 0; i < actions.Length && i < num; i++) { Plugin.ButtonBindAction buttonBindAction = actions[i]; string singleBindingDisplay = GetSingleBindingDisplay(buttonBindAction); string actionLabel = Plugin.GetActionLabel(buttonBindAction); if (_util.CycleButtonRaw(actionLabel, singleBindingDisplay, center, y)) { _bindingCaptureModifier = false; _bindingCaptureAction = buttonBindAction; _bindingDupConfirmActive = false; _page = Page.BindingCapture; Plugin.LogDebug("Bindings: start capture for " + buttonBindAction); break; } y += line; } } private static string GetSingleBindingDisplay(Plugin.ButtonBindAction action) { Plugin.BindingInput binding = Plugin.GetBinding(Plugin.BindingLayer.Modified, action); if (binding.Kind != 0) { return Plugin.GetChordLabel(binding, modified: true); } return Plugin.GetChordLabel(Plugin.GetBinding(Plugin.BindingLayer.Normal, action), modified: false); } private void ApplyPendingBinding(bool replaceDuplicates) { if (!_bindingDupConfirmActive) { return; } if (replaceDuplicates && _bindingDupConflicts != null) { foreach (BindingConflict bindingDupConflict in _bindingDupConflicts) { Plugin.SetBinding(bindingDupConflict.Layer, bindingDupConflict.Action, new Plugin.BindingInput { Kind = Plugin.BindingKind.None, Code = 0 }); } Plugin.LogDebug("Bindings: removed duplicate bindings"); } ApplyBindingNow(_bindingDupPendingCaptured, _bindingDupPendingLayer); _bindingDupConfirmActive = false; _bindingCaptureModifier = false; _page = Page.Bindings; } private void ApplyBindingNow(Plugin.BindingInput captured, Plugin.BindingLayer targetLayer) { Plugin.SetBinding(targetLayer, _bindingCaptureAction, captured); Plugin.SetBinding((targetLayer == Plugin.BindingLayer.Normal) ? Plugin.BindingLayer.Modified : Plugin.BindingLayer.Normal, _bindingCaptureAction, new Plugin.BindingInput { Kind = Plugin.BindingKind.None, Code = 0 }); Plugin.LogDebug("Bindings: set " + _bindingCaptureAction.ToString() + " -> " + Plugin.GetChordLabel(captured, targetLayer == Plugin.BindingLayer.Modified)); } private static bool SameBinding(Plugin.BindingInput a, Plugin.BindingInput b) { if (a.Kind == b.Kind) { return a.Code == b.Code; } return false; } private static bool TryFindDuplicateBindings(Plugin.BindingInput captured, Plugin.ButtonBindAction targetAction, Plugin.BindingLayer targetLayer, out List<BindingConflict> conflicts) { conflicts = null; Plugin.ButtonBindAction[] allBindableActions = AllBindableActions; foreach (Plugin.ButtonBindAction buttonBindAction in allBindableActions) { Plugin.BindingLayer[] array = new Plugin.BindingLayer[2] { Plugin.BindingLayer.Normal, Plugin.BindingLayer.Modified }; foreach (Plugin.BindingLayer bindingLayer in array) { if (buttonBindAction == targetAction && bindingLayer == targetLayer) { continue; } Plugin.BindingInput binding = Plugin.GetBinding(bindingLayer, buttonBindAction); if (binding.Kind != 0 && SameBinding(binding, captured)) { if (conflicts == null) { conflicts = new List<BindingConflict>(); } conflicts.Add(new BindingConflict { Layer = bindingLayer, Action = buttonBindAction }); } } } if (conflicts != null) { return conflicts.Count > 0; } return false; } private static string GetDupConflictsText(List<BindingConflict> conflicts) { if (conflicts == null || conflicts.Count == 0) { return string.Empty; } BindingConflict bindingConflict = conflicts[0]; return ((bindingConflict.Layer == Plugin.BindingLayer.Modified) ? "M+" : string.Empty) + Plugin.GetActionLabel(bindingConflict.Action); } private void DrawBindingCapture(Rect p, float center, ref float y, float line, float sectionGap) { _util.Label("Bindings", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, y); y += line; string name = ((!_bindingCaptureModifier) ? Plugin.GetActionLabel(_bindingCaptureAction) : "Modifier (hold)"); float num = ((Rect)(ref p)).y + ((Rect)(ref p)).height / 2f - 18f; _util.Label("Press a wheel button for:", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, num); _util.Label(name, ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, num + line); bool flag = false; Plugin.BindingInput modifierBinding = Plugin.GetModifierBinding(); if (!_bindingCaptureModifier && modifierBinding.Kind != 0) { flag = Plugin.IsBindingDownForCurrentFrame(modifierBinding); _util.Label("Hold Modifier to bind M+", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, num + line * 2f); _util.Label(flag ? "Mode: M+" : "Mode: Normal", ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f, num + line * 3f); } float x = ((Rect)(ref p)).x + ((Rect)(ref p)).width / 2f; float y2 = ((Rect)(ref p)).y + ((Rect)(ref p)).height - 30f; float y3 = ((Rec