#pragma once #include "hid_pad_handler.h" #include namespace reports { // NOTE: The 1st half-frame contains slightly older data than the 2nd half-frame #pragma pack(push, 1) struct ps_move_input_report_common { // ID Size Description u8 report_id{}; // 0x00 1 HID Report ID (always 0x01) u8 buttons_1{}; // 0x01 1 Buttons 1 (Start, Select) u8 buttons_2{}; // 0x02 1 Buttons 2 (X, Square, Circle, Triangle) u8 buttons_3{}; // 0x03 1+ Buttons 3 (PS, Move, T) and EXT u8 sequence_number{}; // 0x04 1- Sequence number u8 trigger_1{}; // 0x05 1 T button values (1st half-frame) u8 trigger_2{}; // 0x06 1 T button values (2nd half-frame) u32 magic{}; // 0x07 4 always 0x7F7F7F7F u8 timestamp_upper{}; // 0x0B 1 Timestamp (upper byte) u8 battery_level{}; // 0x0C 1 Battery level. 0x05 = max, 0xEE = USB charging s16 accel_x_1{}; // 0x0D 2 X-axis accelerometer (1st half-frame) s16 accel_y_1{}; // 0x0F 2 Z-axis accelerometer (1st half-frame) s16 accel_z_1{}; // 0x11 2 Y-axis accelerometer (1st half-frame) s16 accel_x_2{}; // 0x13 2 X-axis accelerometer (2nd half-frame) s16 accel_y_2{}; // 0x15 2 Z-axis accelerometer (2nd half-frame) s16 accel_z_2{}; // 0x17 2 Y-axis accelerometer (2nd half-frame) s16 gyro_x_1{}; // 0x19 2 X-axis gyroscope (1st half-frame) s16 gyro_y_1{}; // 0x1B 2 Z-axis gyroscope (1st half-frame) s16 gyro_z_1{}; // 0x1D 2 Y-axis gyroscope (1st half-frame) s16 gyro_x_2{}; // 0x1F 2 X-axis gyroscope (2nd half-frame) s16 gyro_y_2{}; // 0x21 2 Z-axis gyroscope (2nd half-frame) s16 gyro_z_2{}; // 0x23 2 Y-axis gyroscope (2nd half-frame) u8 temperature{}; // 0x25 1+ Temperature u8 magnetometer_x{}; // 0x26 1+ Temperature + X-axis magnetometer }; #pragma pack(pop) #pragma pack(push, 1) struct ps_move_input_report_ZCM1 { ps_move_input_report_common common{}; // ID Size Description u8 magnetometer_x2{}; // 0x27 1- X-axis magnetometer u8 magnetometer_y{}; // 0x28 1+ Y-axis magnetometer u8 magnetometer_yz{}; // 0x29 1 YZ-axis magnetometer u8 magnetometer_z{}; // 0x2A 1- Z-axis magnetometer u8 timestamp_lower{}; // 0x2B 1 Timestamp (lower byte) std::array ext_device_data{}; // 0x2C 5 External device data }; #pragma pack(pop) #pragma pack(push, 1) struct ps_move_input_report_ZCM2 { ps_move_input_report_common common{}; // ID Size Description u16 timestamp2; // 0x27 2 same as common timestamp u16 unk; // 0x29 2 Unknown u8 timestamp_lower; // 0x2B 1 Timestamp (lower byte) }; #pragma pack(pop) struct ps_move_output_report { u8 type{}; u8 zero{}; u8 r{}; u8 g{}; u8 b{}; u8 zero2{}; u8 rumble{}; u8 padding[2]; }; struct ps_move_feature_report { std::array data{}; // TODO }; // Buffer size for calibration data constexpr u32 PSMOVE_CALIBRATION_SIZE = 49; // Three blocks, minus header (2 bytes) for blocks 2,3 constexpr u32 PSMOVE_ZCM1_CALIBRATION_BLOB_SIZE = PSMOVE_CALIBRATION_SIZE * 3 - 2 * 2; // Three blocks, minus header (2 bytes) for block 2 constexpr u32 PSMOVE_ZCM2_CALIBRATION_BLOB_SIZE = PSMOVE_CALIBRATION_SIZE * 2 - 2 * 1; struct ps_move_calibration_blob { std::array data{}; }; } // namespace reports enum { zero_shift = 0x8000, }; enum class ps_move_model { ZCM1, // PS3 ZCM2, // PS4 }; struct ps_move_calibration { bool is_valid = false; f32 accel_x_factor = 1.0f; f32 accel_y_factor = 1.0f; f32 accel_z_factor = 1.0f; f32 accel_x_offset = 0.0f; f32 accel_y_offset = 0.0f; f32 accel_z_offset = 0.0f; f32 gyro_x_gain = 1.0f; f32 gyro_y_gain = 1.0f; f32 gyro_z_gain = 1.0f; f32 gyro_x_offset = 0.0f; f32 gyro_y_offset = 0.0f; f32 gyro_z_offset = 0.0f; }; class ps_move_device : public HidDevice { public: ps_move_model model = ps_move_model::ZCM1; reports::ps_move_input_report_ZCM1 input_report_ZCM1{}; reports::ps_move_input_report_ZCM2 input_report_ZCM2{}; reports::ps_move_output_report output_report{}; reports::ps_move_output_report last_output_report{}; steady_clock::time_point last_output_report_time; u32 external_device_id = 0; ps_move_calibration calibration{}; const reports::ps_move_input_report_common& input_report_common() const; }; class ps_move_handler final : public hid_pad_handler { enum ps_move_key_codes { none = 0, cross, square, circle, triangle, start, select, ps, move, t, // Available through external sharpshooter firing_mode_1, firing_mode_2, firing_mode_3, reload, // Available through external racing wheel dpad_up, dpad_down, dpad_left, dpad_right, L1, R1, L2, R2, throttle, paddle_left, paddle_right, }; public: ps_move_handler(); ~ps_move_handler(); void SetPadData(const std::string& padId, u8 player_id, u8 large_motor, u8 small_motor, s32 r, s32 g, s32 b, bool player_led, bool battery_led, u32 battery_led_brightness) override; u32 get_battery_level(const std::string& padId) override; void init_config(cfg_pad* cfg) override; private: #ifdef _WIN32 hid_device* connect_move_device(ps_move_device* device, std::string_view path); #endif DataStatus get_data(ps_move_device* device) override; void check_add_device(hid_device* hidDevice, hid_enumerated_device_view path, std::wstring_view wide_serial) override; int send_output_report(ps_move_device* device) override; bool get_is_left_trigger(const std::shared_ptr& device, u64 keyCode) override; bool get_is_right_trigger(const std::shared_ptr& device, u64 keyCode) override; PadHandlerBase::connection update_connection(const std::shared_ptr& device) override; std::unordered_map get_button_values(const std::shared_ptr& device) override; pad_preview_values get_preview_values(const std::unordered_map& data) override; void get_extended_info(const pad_ensemble& binding) override; void apply_pad_data(const pad_ensemble& binding) override; void handle_external_device(const pad_ensemble& binding); };