From 8a26ec7458f8a24cf0718cc3d4468652938ce11f Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:13:53 -0400 Subject: [PATCH] refactor(Windows): mark protocol headers C++-only Update the shared Windows protocol headers to explicitly be C++-only and remove legacy C compatibility paths. This drops the `#else` constant/enums and `extern "C"` wrappers, leaving a single C++ definition path with `inline constexpr`, scoped enums, and the existing packed protocol structs. --- .../broker/broker_request_validation.hpp | 7 +- .../windows/broker/libvirtualhid_broker.cpp | 14 +- src/platform/windows/control_protocol.hpp | 9 +- .../windows/driver/libvirtualhid_umdf.cpp | 33 +-- .../shared/lvh_windows_broker_protocol.h | 189 ++++++---------- .../windows/shared/lvh_windows_protocol.h | 201 ++++++------------ .../shared/playstation_feature_protocol.hpp | 2 +- src/platform/windows/windows_backend.cpp | 6 +- .../windows/windows_broker_client.hpp | 2 +- src/platform/windows/windows_license.cpp | 46 ++-- tests/fixtures/windows_backend_test_hooks.cpp | 2 +- tests/unit/test_license.cpp | 12 ++ tests/unit/test_windows_broker_validation.cpp | 6 +- tests/unit/test_windows_driver_protocol.cpp | 2 +- tests/unit/test_windows_protocol.cpp | 16 +- 15 files changed, 223 insertions(+), 324 deletions(-) diff --git a/src/platform/windows/broker/broker_request_validation.hpp b/src/platform/windows/broker/broker_request_validation.hpp index 0d0e43a..155c97f 100644 --- a/src/platform/windows/broker/broker_request_validation.hpp +++ b/src/platform/windows/broker/broker_request_validation.hpp @@ -9,6 +9,7 @@ // standard includes #include +#include #include #include #include @@ -29,14 +30,14 @@ namespace lvh::windows::broker_validation { LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_ADAPTIVE_TRIGGERS; template - bool all_zero(const Value (&values)[Size]) { + bool all_zero(const std::array &values) { return std::ranges::all_of(values, [](const auto value) { return value == Value {}; }); } template - bool valid_c_string(const char (&value)[Size], bool allow_empty = true) { + bool valid_c_string(const std::array &value, bool allow_empty = true) { const auto terminator = std::ranges::find(value, '\0'); if (terminator == std::end(value) || (!allow_empty && terminator == std::begin(value))) { return false; @@ -48,7 +49,7 @@ namespace lvh::windows::broker_validation { } template - bool valid_sized_c_string(const char (&value)[Size], std::uint32_t size) { + bool valid_sized_c_string(const std::array &value, std::uint32_t size) { if (size >= Size || value[size] != '\0') { return false; } diff --git a/src/platform/windows/broker/libvirtualhid_broker.cpp b/src/platform/windows/broker/libvirtualhid_broker.cpp index 628862c..c9a487f 100644 --- a/src/platform/windows/broker/libvirtualhid_broker.cpp +++ b/src/platform/windows/broker/libvirtualhid_broker.cpp @@ -470,10 +470,10 @@ namespace lvh::detail::windows_broker_service { } template - void copy_c_string(char (&target)[Size], std::string_view value) { + void copy_c_string(std::array &target, std::string_view value) { std::ranges::fill(target, '\0'); const auto count = std::min(value.size(), Size - 1U); - std::memcpy(target, value.data(), count); + std::memcpy(target.data(), value.data(), count); } std::string windows_error_message(DWORD error_code) { @@ -1189,7 +1189,7 @@ namespace lvh::detail::windows_broker_service { const LvhWindowsSessionToken &lhs, const LvhWindowsSessionToken &rhs ) { - return std::memcmp(lhs.bytes, rhs.bytes, sizeof(lhs.bytes)) == 0; + return std::ranges::equal(lhs.bytes, rhs.bytes); } LvhWindowsDestroyDeviceRequest make_destroy_device_request( @@ -1690,8 +1690,8 @@ namespace lvh::detail::windows_broker_service { return response; } - const std::string license_key {request.license_key}; - const auto instance_name = request.instance_name[0] == '\0' ? default_instance_name() : std::string {request.instance_name}; + const std::string license_key {request.license_key.data()}; + const auto instance_name = request.instance_name[0] == '\0' ? default_instance_name() : std::string {request.instance_name.data()}; if (license_key.empty()) { response.status = std::to_underlying(LvhWindowsBrokerStatusCode::invalid_argument); copy_c_string(response.message, "License key is required."); @@ -2237,7 +2237,7 @@ namespace lvh::detail::windows_broker_service { std::pair authorize_gamepad_create( LvhWindowsBrokerLicenseStatus &license, - char (&message)[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE] + std::array &message ) { { std::lock_guard lock {mutex_}; @@ -2281,7 +2281,7 @@ namespace lvh::detail::windows_broker_service { if (!license_allowed(*license_state_) || !license_time_is_current_locked()) { fill_license_status_locked(license); - copy_c_string(message, license.message); + copy_c_string(message, license.message.data()); return {LvhWindowsBrokerStatusCode::license_invalid, false}; } diff --git a/src/platform/windows/control_protocol.hpp b/src/platform/windows/control_protocol.hpp index 78d628c..c401022 100644 --- a/src/platform/windows/control_protocol.hpp +++ b/src/platform/windows/control_protocol.hpp @@ -6,6 +6,7 @@ // standard includes #include +#include #include #include #include @@ -94,24 +95,24 @@ namespace lvh::detail::windows { } template - std::uint32_t copy_string(char (&target)[Size], std::string_view source) { + std::uint32_t copy_string(std::array &target, std::string_view source) { std::ranges::fill(target, '\0'); const auto copied = std::min(source.size(), Size - 1U); if (copied > 0U) { - std::memcpy(target, source.data(), copied); + std::memcpy(target.data(), source.data(), copied); } return static_cast(copied); } template - std::uint32_t copy_bytes(std::uint8_t (&target)[Size], const std::vector &source) { + std::uint32_t copy_bytes(std::array &target, const std::vector &source) { std::ranges::fill(target, std::uint8_t {}); const auto copied = std::min(source.size(), Size); if (copied > 0U) { - std::memcpy(target, source.data(), copied); + std::memcpy(target.data(), source.data(), copied); } return static_cast(copied); diff --git a/src/platform/windows/driver/libvirtualhid_umdf.cpp b/src/platform/windows/driver/libvirtualhid_umdf.cpp index eaa1af2..ba2a47d 100644 --- a/src/platform/windows/driver/libvirtualhid_umdf.cpp +++ b/src/platform/windows/driver/libvirtualhid_umdf.cpp @@ -420,8 +420,8 @@ namespace { const auto descriptor_size = record->request.report_sizes.report_descriptor_size; record->report_descriptor.assign( - record->request.report_descriptor, - record->request.report_descriptor + descriptor_size + record->request.report_descriptor.data(), + record->request.report_descriptor.data() + descriptor_size ); record->hardware_ids = lvh::detail::windows::make_hardware_ids(record->request); @@ -484,14 +484,14 @@ namespace { } bool session_token_matches(const DeviceRecord &record, const LvhWindowsSessionToken &session_token) { - return std::memcmp(record.session_token.bytes, session_token.bytes, sizeof(record.session_token.bytes)) == 0; + return std::ranges::equal(record.session_token.bytes, session_token.bytes); } NTSTATUS generate_session_token(LvhWindowsSessionToken &session_token) { const auto status = BCryptGenRandom( nullptr, - session_token.bytes, - static_cast(sizeof(session_token.bytes)), + session_token.bytes.data(), + static_cast(session_token.bytes.size()), BCRYPT_USE_SYSTEM_PREFERRED_RNG ); if (!NT_SUCCESS(status)) { @@ -674,8 +674,8 @@ namespace { const LvhWindowsSubmitInputReportRequest &request ) { const auto report_id = record.request.hardware_ids.report_id; - const auto report_begin = request.report; - const auto report_end = request.report + request.report_size; + const auto report_begin = request.report.data(); + const auto report_end = request.report.data() + request.report_size; if (report_id == 0U) { return {report_begin, report_end}; } @@ -703,7 +703,7 @@ namespace { } if (payload_size > 0U) { - std::memcpy(event.report + report_id_size, packet.reportBuffer, payload_size); + std::memcpy(event.report.data() + report_id_size, packet.reportBuffer, payload_size); } event.report_size = static_cast(report_id_size + payload_size); @@ -714,7 +714,7 @@ namespace { return; } - auto reply = lvh::detail::windows::make_switch_pro_reply({event.report, event.report_size}); + auto reply = lvh::detail::windows::make_switch_pro_reply({event.report.data(), event.report_size}); if (!reply.has_value()) { return; } @@ -778,7 +778,7 @@ namespace { std::lock_guard lock {record.mutex}; return record.generic_pid_feature_state.handle_set_feature( static_cast(report_id), - {event.report, event.report_size} + {event.report.data(), event.report_size} ); } return lvh::detail::windows::is_playstation_gamepad(record.request.gamepad_kind); @@ -792,21 +792,24 @@ namespace { std::lock_guard lock {record.mutex}; static_cast(record.generic_pid_feature_state.handle_output_report( static_cast(event.report[0]), - {event.report, event.report_size} + {event.report.data(), event.report_size} )); } - void set_device_path(std::uint64_t driver_device_id, char (&device_path)[LVH_WINDOWS_MAX_DEVICE_PATH_SIZE]) { + void set_device_path( + std::uint64_t driver_device_id, + std::array &device_path + ) { constexpr auto path_prefix_size = sizeof(LVH_WINDOWS_CONTROL_DEVICE_PATH) - 1U; constexpr auto separator_size = 1U; static_assert(path_prefix_size + separator_size < LVH_WINDOWS_MAX_DEVICE_PATH_SIZE); - std::memcpy(device_path, LVH_WINDOWS_CONTROL_DEVICE_PATH, path_prefix_size); + std::memcpy(device_path.data(), LVH_WINDOWS_CONTROL_DEVICE_PATH, path_prefix_size); device_path[path_prefix_size] = '#'; const auto output = std::to_chars( - device_path + path_prefix_size + separator_size, - device_path + sizeof(device_path) - 1U, + device_path.data() + path_prefix_size + separator_size, + device_path.data() + device_path.size() - 1U, driver_device_id ); if (output.ec == std::errc {}) { diff --git a/src/platform/windows/shared/lvh_windows_broker_protocol.h b/src/platform/windows/shared/lvh_windows_broker_protocol.h index 93a0495..a7238f7 100644 --- a/src/platform/windows/shared/lvh_windows_broker_protocol.h +++ b/src/platform/windows/shared/lvh_windows_broker_protocol.h @@ -1,15 +1,15 @@ /** * @file src/platform/windows/shared/lvh_windows_broker_protocol.h * @brief Stable named-pipe protocol shared by the Windows backend, broker, and control UI. + * @note This internal protocol header is C++-only. */ #pragma once #include "lvh_windows_protocol.h" +#include #include -#ifdef __cplusplus - inline constexpr uint32_t LVH_WINDOWS_BROKER_PROTOCOL_VERSION = 2u; inline constexpr uint32_t LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE = 512u; inline constexpr uint32_t LVH_WINDOWS_BROKER_MAX_LICENSE_KEY_SIZE = 128u; @@ -49,130 +49,81 @@ enum class LvhWindowsBrokerLicenseState : uint32_t { invalid = 4, }; -#else +struct LvhWindowsBrokerRequestHeader { + uint32_t version; + uint32_t size; + uint32_t type; + uint32_t reserved0; +}; + +struct LvhWindowsBrokerLicenseStatus { + uint32_t version; + uint32_t size; + uint32_t state; + uint32_t active_devices; + uint32_t free_active_device_limit; + uint32_t activation_limit; + uint32_t activation_usage; + std::array plan_name; + std::array customer_email; + std::array expires_at; + std::array message; +}; + +struct LvhWindowsBrokerStatusRequest { + LvhWindowsBrokerRequestHeader header; +}; -enum { - LVH_WINDOWS_BROKER_PROTOCOL_VERSION = 2u, - LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE = 512u, - LVH_WINDOWS_BROKER_MAX_LICENSE_KEY_SIZE = 128u, - LVH_WINDOWS_BROKER_MAX_INSTANCE_NAME_SIZE = 128u, - LVH_WINDOWS_BROKER_MAX_PLAN_NAME_SIZE = 128u, - LVH_WINDOWS_BROKER_MAX_CUSTOMER_EMAIL_SIZE = 128u, - LVH_WINDOWS_BROKER_MAX_TIMESTAMP_SIZE = 64u, +struct LvhWindowsBrokerStatusResponse { + uint32_t version; + uint32_t size; + uint32_t status; + uint32_t reserved0; + LvhWindowsBrokerLicenseStatus license; + std::array message; }; -static const char LVH_WINDOWS_BROKER_PIPE_PATH[] = "\\\\.\\pipe\\libvirtualhid-broker"; +struct LvhWindowsBrokerCreateGamepadRequest { + LvhWindowsBrokerRequestHeader header; + uint64_t client_control_handle; + LvhWindowsCreateGamepadRequest gamepad; +}; -enum LvhWindowsBrokerRequestType { - LVH_WINDOWS_BROKER_REQUEST_STATUS = 1, - LVH_WINDOWS_BROKER_REQUEST_CREATE_GAMEPAD = 2, - LVH_WINDOWS_BROKER_REQUEST_DESTROY_DEVICE = 3, - LVH_WINDOWS_BROKER_REQUEST_ACTIVATE_LICENSE = 4, - LVH_WINDOWS_BROKER_REQUEST_VALIDATE_LICENSE = 5, - LVH_WINDOWS_BROKER_REQUEST_DEACTIVATE_LICENSE = 6, +struct LvhWindowsBrokerCreateGamepadResponse { + uint32_t version; + uint32_t size; + uint32_t status; + uint32_t reserved0; + LvhWindowsCreateGamepadResponse gamepad; + LvhWindowsBrokerLicenseStatus license; + std::array message; }; -enum LvhWindowsBrokerStatusCode { - LVH_WINDOWS_BROKER_STATUS_SUCCESS = 0, - LVH_WINDOWS_BROKER_STATUS_INVALID_ARGUMENT = 1, - LVH_WINDOWS_BROKER_STATUS_UNSUPPORTED_PROFILE = 2, - LVH_WINDOWS_BROKER_STATUS_DEVICE_NOT_FOUND = 3, - LVH_WINDOWS_BROKER_STATUS_BACKEND_UNAVAILABLE = 4, - LVH_WINDOWS_BROKER_STATUS_BACKEND_FAILURE = 5, - LVH_WINDOWS_BROKER_STATUS_LICENSE_REQUIRED = 6, - LVH_WINDOWS_BROKER_STATUS_LICENSE_INVALID = 7, - LVH_WINDOWS_BROKER_STATUS_ACTIVATION_LIMIT_REACHED = 8, - LVH_WINDOWS_BROKER_STATUS_NETWORK_UNAVAILABLE = 9, +struct LvhWindowsBrokerDestroyDeviceRequest { + LvhWindowsBrokerRequestHeader header; + LvhWindowsDestroyDeviceRequest device; }; -enum LvhWindowsBrokerLicenseState { - LVH_WINDOWS_BROKER_LICENSE_FREE = 0, - LVH_WINDOWS_BROKER_LICENSE_LICENSED = 1, - LVH_WINDOWS_BROKER_LICENSE_EXPIRED = 2, - LVH_WINDOWS_BROKER_LICENSE_DISABLED = 3, - LVH_WINDOWS_BROKER_LICENSE_INVALID = 4, +struct LvhWindowsBrokerDestroyDeviceResponse { + uint32_t version; + uint32_t size; + uint32_t status; + uint32_t reserved0; + LvhWindowsBrokerLicenseStatus license; + std::array message; }; -#endif - -extern "C" { - - struct LvhWindowsBrokerRequestHeader { - uint32_t version; - uint32_t size; - uint32_t type; - uint32_t reserved0; - }; - - struct LvhWindowsBrokerLicenseStatus { - uint32_t version; - uint32_t size; - uint32_t state; - uint32_t active_devices; - uint32_t free_active_device_limit; - uint32_t activation_limit; - uint32_t activation_usage; - char plan_name[LVH_WINDOWS_BROKER_MAX_PLAN_NAME_SIZE]; - char customer_email[LVH_WINDOWS_BROKER_MAX_CUSTOMER_EMAIL_SIZE]; - char expires_at[LVH_WINDOWS_BROKER_MAX_TIMESTAMP_SIZE]; - char message[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE]; - }; - - struct LvhWindowsBrokerStatusRequest { - LvhWindowsBrokerRequestHeader header; - }; - - struct LvhWindowsBrokerStatusResponse { - uint32_t version; - uint32_t size; - uint32_t status; - uint32_t reserved0; - LvhWindowsBrokerLicenseStatus license; - char message[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE]; - }; - - struct LvhWindowsBrokerCreateGamepadRequest { - LvhWindowsBrokerRequestHeader header; - uint64_t client_control_handle; - LvhWindowsCreateGamepadRequest gamepad; - }; - - struct LvhWindowsBrokerCreateGamepadResponse { - uint32_t version; - uint32_t size; - uint32_t status; - uint32_t reserved0; - LvhWindowsCreateGamepadResponse gamepad; - LvhWindowsBrokerLicenseStatus license; - char message[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE]; - }; - - struct LvhWindowsBrokerDestroyDeviceRequest { - LvhWindowsBrokerRequestHeader header; - LvhWindowsDestroyDeviceRequest device; - }; - - struct LvhWindowsBrokerDestroyDeviceResponse { - uint32_t version; - uint32_t size; - uint32_t status; - uint32_t reserved0; - LvhWindowsBrokerLicenseStatus license; - char message[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE]; - }; - - struct LvhWindowsBrokerLicenseRequest { - LvhWindowsBrokerRequestHeader header; - char license_key[LVH_WINDOWS_BROKER_MAX_LICENSE_KEY_SIZE]; - char instance_name[LVH_WINDOWS_BROKER_MAX_INSTANCE_NAME_SIZE]; - }; - - struct LvhWindowsBrokerLicenseResponse { - uint32_t version; - uint32_t size; - uint32_t status; - uint32_t reserved0; - LvhWindowsBrokerLicenseStatus license; - char message[LVH_WINDOWS_BROKER_MAX_MESSAGE_SIZE]; - }; -} +struct LvhWindowsBrokerLicenseRequest { + LvhWindowsBrokerRequestHeader header; + std::array license_key; + std::array instance_name; +}; + +struct LvhWindowsBrokerLicenseResponse { + uint32_t version; + uint32_t size; + uint32_t status; + uint32_t reserved0; + LvhWindowsBrokerLicenseStatus license; + std::array message; +}; diff --git a/src/platform/windows/shared/lvh_windows_protocol.h b/src/platform/windows/shared/lvh_windows_protocol.h index 0ebbdc1..f2a826b 100644 --- a/src/platform/windows/shared/lvh_windows_protocol.h +++ b/src/platform/windows/shared/lvh_windows_protocol.h @@ -1,13 +1,13 @@ /** * @file src/platform/windows/shared/lvh_windows_protocol.h * @brief Stable control protocol shared by the Windows client backend and UMDF driver. + * @note This internal protocol header is C++-only. */ #pragma once +#include #include -#ifdef __cplusplus - inline constexpr uint32_t LVH_WINDOWS_CONTROL_PROTOCOL_VERSION = 2u; inline constexpr char LVH_WINDOWS_CONTROL_DEVICE_PATH[] = R"(\\.\LibVirtualHid)"; inline constexpr char LVH_WINDOWS_GLOBAL_CONTROL_DEVICE_PATH[] = R"(\\.\Global\LibVirtualHid)"; @@ -145,150 +145,83 @@ inline constexpr uint32_t LVH_WINDOWS_GAMEPAD_SWITCH_PRO = lvh_windows_protocol_ inline constexpr uint32_t LVH_WINDOWS_GAMEPAD_DUALSHOCK4 = lvh_windows_protocol_detail::gamepad_dualshock4; -#else +#pragma pack(push, 1) -enum { - LVH_WINDOWS_CONTROL_PROTOCOL_VERSION = 2u, - LVH_WINDOWS_MAX_REPORT_DESCRIPTOR_SIZE = 2048u, - LVH_WINDOWS_MAX_INPUT_REPORT_SIZE = 256u, - LVH_WINDOWS_MAX_OUTPUT_REPORT_SIZE = 256u, - LVH_WINDOWS_MAX_DEVICE_PATH_SIZE = 260u, - LVH_WINDOWS_MAX_DEVICE_NAME_SIZE = 128u, - LVH_WINDOWS_MAX_MANUFACTURER_SIZE = 128u, - LVH_WINDOWS_MAX_STABLE_ID_SIZE = 128u, - LVH_WINDOWS_SESSION_TOKEN_SIZE = 32u, - LVH_WINDOWS_FILE_DEVICE_LIBVIRTUALHID = 0x8000u, - LVH_WINDOWS_METHOD_BUFFERED = 0u, - LVH_WINDOWS_FILE_READ_ACCESS = 1u, - LVH_WINDOWS_FILE_WRITE_ACCESS = 2u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_RUMBLE = 0x00000001u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_MOTION = 0x00000002u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_TOUCHPAD = 0x00000004u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_RGB_LED = 0x00000008u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_BATTERY = 0x00000010u, - LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_ADAPTIVE_TRIGGERS = 0x00000020u, +struct LvhWindowsGamepadHardwareIds { + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_version; + uint8_t report_id; + std::array reserved0; }; -static const char LVH_WINDOWS_CONTROL_DEVICE_PATH[] = "\\\\.\\LibVirtualHid"; -static const char LVH_WINDOWS_GLOBAL_CONTROL_DEVICE_PATH[] = "\\\\.\\Global\\LibVirtualHid"; - -static const uint32_t LVH_WINDOWS_IOCTL_CREATE_GAMEPAD = 0x8000E000u; -static const uint32_t LVH_WINDOWS_IOCTL_DESTROY_DEVICE = 0x8000E004u; -static const uint32_t LVH_WINDOWS_IOCTL_SUBMIT_INPUT_REPORT = 0x8000E008u; -static const uint32_t LVH_WINDOWS_IOCTL_READ_OUTPUT_REPORT = 0x8000600Cu; -static const uint32_t LVH_WINDOWS_IOCTL_RESET_DEVICES = 0x8000E010u; - -enum LvhWindowsProtocolStatus { - LVH_WINDOWS_STATUS_SUCCESS = 0, - LVH_WINDOWS_STATUS_INVALID_ARGUMENT = 1, - LVH_WINDOWS_STATUS_UNSUPPORTED_PROFILE = 2, - LVH_WINDOWS_STATUS_DEVICE_NOT_FOUND = 3, - LVH_WINDOWS_STATUS_BACKEND_FAILURE = 4, +struct LvhWindowsGamepadReportSizes { + uint32_t input_report_size; + uint32_t output_report_size; + uint32_t report_descriptor_size; + uint32_t name_size; + uint32_t manufacturer_size; + uint32_t stable_id_size; }; -enum LvhWindowsBusType { - LVH_WINDOWS_BUS_UNKNOWN = 0, - LVH_WINDOWS_BUS_USB = 1, - LVH_WINDOWS_BUS_BLUETOOTH = 2, +struct LvhWindowsSessionToken { + std::array bytes; }; -enum LvhWindowsGamepadProfileKind { - LVH_WINDOWS_GAMEPAD_GENERIC = 0, - LVH_WINDOWS_GAMEPAD_XBOX_360 = 1, - LVH_WINDOWS_GAMEPAD_XBOX_ONE = 2, - LVH_WINDOWS_GAMEPAD_XBOX_SERIES = 3, - LVH_WINDOWS_GAMEPAD_DUALSENSE = 4, - LVH_WINDOWS_GAMEPAD_SWITCH_PRO = 5, - LVH_WINDOWS_GAMEPAD_DUALSHOCK4 = 6, +struct LvhWindowsCreateGamepadRequest { + uint32_t version; + uint32_t size; + uint64_t client_device_id; + uint32_t bus_type; + uint32_t gamepad_kind; + uint32_t flags; + LvhWindowsGamepadHardwareIds hardware_ids; + LvhWindowsGamepadReportSizes report_sizes; + std::array report_descriptor; + std::array name; + std::array manufacturer; + std::array stable_id; }; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#pragma pack(push, 1) - - struct LvhWindowsGamepadHardwareIds { - uint16_t vendor_id; - uint16_t product_id; - uint16_t device_version; - uint8_t report_id; - uint8_t reserved0[7]; - }; - - struct LvhWindowsGamepadReportSizes { - uint32_t input_report_size; - uint32_t output_report_size; - uint32_t report_descriptor_size; - uint32_t name_size; - uint32_t manufacturer_size; - uint32_t stable_id_size; - }; - - struct LvhWindowsSessionToken { - uint8_t bytes[LVH_WINDOWS_SESSION_TOKEN_SIZE]; - }; - - struct LvhWindowsCreateGamepadRequest { - uint32_t version; - uint32_t size; - uint64_t client_device_id; - uint32_t bus_type; - uint32_t gamepad_kind; - uint32_t flags; - LvhWindowsGamepadHardwareIds hardware_ids; - LvhWindowsGamepadReportSizes report_sizes; - uint8_t report_descriptor[LVH_WINDOWS_MAX_REPORT_DESCRIPTOR_SIZE]; - char name[LVH_WINDOWS_MAX_DEVICE_NAME_SIZE]; - char manufacturer[LVH_WINDOWS_MAX_MANUFACTURER_SIZE]; - char stable_id[LVH_WINDOWS_MAX_STABLE_ID_SIZE]; - }; - - struct LvhWindowsCreateGamepadResponse { - uint32_t version; - uint32_t size; - uint32_t status; - uint32_t reserved0; - uint64_t driver_device_id; - LvhWindowsSessionToken session_token; - char device_path[LVH_WINDOWS_MAX_DEVICE_PATH_SIZE]; - }; +struct LvhWindowsCreateGamepadResponse { + uint32_t version; + uint32_t size; + uint32_t status; + uint32_t reserved0; + uint64_t driver_device_id; + LvhWindowsSessionToken session_token; + std::array device_path; +}; - struct LvhWindowsDestroyDeviceRequest { - uint32_t version; - uint32_t size; - uint64_t driver_device_id; - LvhWindowsSessionToken session_token; - }; +struct LvhWindowsDestroyDeviceRequest { + uint32_t version; + uint32_t size; + uint64_t driver_device_id; + LvhWindowsSessionToken session_token; +}; - struct LvhWindowsResetDevicesRequest { - uint32_t version; - uint32_t size; - }; +struct LvhWindowsResetDevicesRequest { + uint32_t version; + uint32_t size; +}; - struct LvhWindowsSubmitInputReportRequest { - uint32_t version; - uint32_t size; - uint64_t driver_device_id; - LvhWindowsSessionToken session_token; - uint32_t report_size; - uint32_t reserved0; - uint8_t report[LVH_WINDOWS_MAX_INPUT_REPORT_SIZE]; - }; +struct LvhWindowsSubmitInputReportRequest { + uint32_t version; + uint32_t size; + uint64_t driver_device_id; + LvhWindowsSessionToken session_token; + uint32_t report_size; + uint32_t reserved0; + std::array report; +}; - struct LvhWindowsOutputReportEvent { - uint32_t version; - uint32_t size; - uint64_t driver_device_id; - uint32_t report_size; - uint32_t reserved0; - uint8_t report[LVH_WINDOWS_MAX_OUTPUT_REPORT_SIZE]; - }; +struct LvhWindowsOutputReportEvent { + uint32_t version; + uint32_t size; + uint64_t driver_device_id; + uint32_t report_size; + uint32_t reserved0; + std::array report; +}; #pragma pack(pop) - -#ifdef __cplusplus -} -#endif diff --git a/src/platform/windows/shared/playstation_feature_protocol.hpp b/src/platform/windows/shared/playstation_feature_protocol.hpp index 24c28da..c774e7b 100644 --- a/src/platform/windows/shared/playstation_feature_protocol.hpp +++ b/src/platform/windows/shared/playstation_feature_protocol.hpp @@ -80,7 +80,7 @@ namespace lvh::detail::windows { inline std::array request_mac_address(const LvhWindowsCreateGamepadRequest &request) { const auto size = std::min(request.report_sizes.stable_id_size, sizeof(request.stable_id)); - const auto stable_id = std::string_view {request.stable_id, size}; + const auto stable_id = std::string_view {request.stable_id.data(), size}; return parse_mac_address(stable_id).value_or(generated_mac_address(request.client_device_id)); } diff --git a/src/platform/windows/windows_backend.cpp b/src/platform/windows/windows_backend.cpp index 89c776f..ba45163 100644 --- a/src/platform/windows/windows_backend.cpp +++ b/src/platform/windows/windows_backend.cpp @@ -938,7 +938,7 @@ namespace lvh::detail { response.driver_device_id, response.session_token, options.profile, - response.device_path[0] == '\0' ? command_channel_->path() : std::string {response.device_path} + response.device_path[0] == '\0' ? command_channel_->path() : std::string {response.device_path.data()} ); { @@ -1080,8 +1080,8 @@ namespace lvh::detail { std::unique_lock dispatch_lock {state->output_dispatch_mutex_}; std::vector report( - event.report, - event.report + std::min(event.report_size, static_cast(LVH_WINDOWS_MAX_OUTPUT_REPORT_SIZE)) + event.report.data(), + event.report.data() + std::min(event.report_size, static_cast(LVH_WINDOWS_MAX_OUTPUT_REPORT_SIZE)) ); DeviceProfile profile; diff --git a/src/platform/windows/windows_broker_client.hpp b/src/platform/windows/windows_broker_client.hpp index 991b488..7994f55 100644 --- a/src/platform/windows/windows_broker_client.hpp +++ b/src/platform/windows/windows_broker_client.hpp @@ -71,7 +71,7 @@ namespace lvh::detail::windows_broker { return OperationStatus::failure(ErrorCode::backend_failure, "Windows broker returned a truncated or invalid response"); } - return response_status(response.status, response.message); + return response_status(response.status, response.message.data()); } } // namespace lvh::detail::windows_broker diff --git a/src/platform/windows/windows_license.cpp b/src/platform/windows/windows_license.cpp index e29084f..f3fa93c 100644 --- a/src/platform/windows/windows_license.cpp +++ b/src/platform/windows/windows_license.cpp @@ -12,6 +12,7 @@ // standard includes #include +#include #include #include #include @@ -43,10 +44,10 @@ namespace lvh { .active_devices = status.active_devices, .activation_limit = status.activation_limit, .activation_usage = status.activation_usage, - .plan_name = status.plan_name, - .customer_email = status.customer_email, - .expires_at = status.expires_at, - .message = status.message, + .plan_name = status.plan_name.data(), + .customer_email = status.customer_email.data(), + .expires_at = status.expires_at.data(), + .message = status.message.data(), .purchase_url = std::string {windows::broker_config::buy_url}, .manage_account_url = std::string {windows::broker_config::manage_account_url}, }; @@ -62,9 +63,22 @@ namespace lvh { } template - void copy_c_string(char (&target)[Size], std::string_view value) { + void copy_c_string(std::array &target, std::string_view value) { std::ranges::fill(target, '\0'); - std::memcpy(target, value.data(), value.size()); + std::memcpy(target.data(), value.data(), value.size()); + } + + template + LicenseResult license_result_from(const OperationStatus &status, const Response &response) { + if (response.version != LVH_WINDOWS_BROKER_PROTOCOL_VERSION || response.size != sizeof(response)) { + return {status, unavailable_license_status(status.message())}; + } + + auto license = license_status_from(response.license); + if (!std::string_view {response.message.data()}.empty()) { + license.message = response.message.data(); + } + return {status, std::move(license)}; } template @@ -85,15 +99,7 @@ namespace lvh { LvhWindowsBrokerLicenseResponse response {}; const auto status = detail::windows_broker::call(request, response, "Call the Windows license service"); - if (response.version != LVH_WINDOWS_BROKER_PROTOCOL_VERSION || response.size != sizeof(response)) { - return {status, unavailable_license_status(status.message())}; - } - - auto license = license_status_from(response.license); - if (!std::string_view {response.message}.empty()) { - license.message = response.message; - } - return {status, std::move(license)}; + return license_result_from(status, response); } } // namespace @@ -104,15 +110,7 @@ namespace lvh { LvhWindowsBrokerStatusResponse response {}; const auto status = detail::windows_broker::call(request, response, "Query the Windows license service"); - if (response.version != LVH_WINDOWS_BROKER_PROTOCOL_VERSION || response.size != sizeof(response)) { - return {status, unavailable_license_status(status.message())}; - } - - auto license = license_status_from(response.license); - if (!std::string_view {response.message}.empty()) { - license.message = response.message; - } - return {status, std::move(license)}; + return license_result_from(status, response); } LicenseResult activate_license(std::string_view license_key, std::string_view instance_name) { diff --git a/tests/fixtures/windows_backend_test_hooks.cpp b/tests/fixtures/windows_backend_test_hooks.cpp index 52fb1ee..55f44f3 100644 --- a/tests/fixtures/windows_backend_test_hooks.cpp +++ b/tests/fixtures/windows_backend_test_hooks.cpp @@ -540,7 +540,7 @@ namespace lvh::detail { event.size = sizeof(event); event.driver_device_id = driver_id; event.report_size = static_cast(report.size()); - std::ranges::copy(report, event.report); + std::ranges::copy(report, event.report.begin()); event_state->enqueue_output_event(event); }; diff --git a/tests/unit/test_license.cpp b/tests/unit/test_license.cpp index 82066f4..97fa2f1 100644 --- a/tests/unit/test_license.cpp +++ b/tests/unit/test_license.cpp @@ -80,7 +80,19 @@ TEST(WindowsBrokerClientTest, BuildsVersionedRequestHeader) { EXPECT_EQ(header.type, static_cast(LvhWindowsBrokerRequestType::validate_license)); EXPECT_EQ(header.reserved0, 0U); EXPECT_EQ(LVH_WINDOWS_BROKER_PROTOCOL_VERSION, 2U); +} + +TEST(WindowsBrokerClientTest, PreservesFixedWireLayout) { + EXPECT_EQ(sizeof(LvhWindowsBrokerRequestHeader), 16U); + EXPECT_EQ(sizeof(LvhWindowsBrokerLicenseStatus), 860U); + EXPECT_EQ(sizeof(LvhWindowsBrokerStatusRequest), 16U); + EXPECT_EQ(sizeof(LvhWindowsBrokerStatusResponse), 1388U); EXPECT_EQ(sizeof(LvhWindowsBrokerCreateGamepadRequest), 2528U); + EXPECT_EQ(sizeof(LvhWindowsBrokerCreateGamepadResponse), 1704U); + EXPECT_EQ(sizeof(LvhWindowsBrokerDestroyDeviceRequest), 64U); + EXPECT_EQ(sizeof(LvhWindowsBrokerDestroyDeviceResponse), 1388U); + EXPECT_EQ(sizeof(LvhWindowsBrokerLicenseRequest), 272U); + EXPECT_EQ(sizeof(LvhWindowsBrokerLicenseResponse), 1388U); } TEST(WindowsBrokerClientTest, MapsLicenseAndTransportStatuses) { diff --git a/tests/unit/test_windows_broker_validation.cpp b/tests/unit/test_windows_broker_validation.cpp index edf729d..8ce35ba 100644 --- a/tests/unit/test_windows_broker_validation.cpp +++ b/tests/unit/test_windows_broker_validation.cpp @@ -73,8 +73,8 @@ namespace { LvhWindowsBrokerLicenseRequest request {}; request.header = request_header(type, sizeof(request)); if (type == LvhWindowsBrokerRequestType::activate_license) { - std::memcpy(request.license_key, "test-key", sizeof("test-key")); - std::memcpy(request.instance_name, "test-machine", sizeof("test-machine")); + std::memcpy(request.license_key.data(), "test-key", sizeof("test-key")); + std::memcpy(request.instance_name.data(), "test-machine", sizeof("test-machine")); } return request; } @@ -183,7 +183,7 @@ TEST(WindowsBrokerValidationTest, RejectsMalformedCreateFields) { EXPECT_FALSE(lvh::windows::broker_validation::valid_request(request)); request = valid; - std::memcpy(request.gamepad.stable_id, "stable", sizeof("stable")); + std::memcpy(request.gamepad.stable_id.data(), "stable", sizeof("stable")); request.gamepad.report_sizes.stable_id_size = sizeof("stable") - 1U; request.gamepad.stable_id[1] = '\0'; EXPECT_FALSE(lvh::windows::broker_validation::valid_request(request)); diff --git a/tests/unit/test_windows_driver_protocol.cpp b/tests/unit/test_windows_driver_protocol.cpp index ca45c63..0b3bbea 100644 --- a/tests/unit/test_windows_driver_protocol.cpp +++ b/tests/unit/test_windows_driver_protocol.cpp @@ -52,7 +52,7 @@ namespace { request.gamepad_kind = gamepad_kind; request.bus_type = bus_type; request.report_sizes.stable_id_size = static_cast(stable_id.size()); - std::ranges::copy(stable_id, request.stable_id); + std::ranges::copy(stable_id, request.stable_id.begin()); return request; } diff --git a/tests/unit/test_windows_protocol.cpp b/tests/unit/test_windows_protocol.cpp index 51349b0..3f60f00 100644 --- a/tests/unit/test_windows_protocol.cpp +++ b/tests/unit/test_windows_protocol.cpp @@ -151,7 +151,7 @@ TEST(WindowsProtocolTest, CopyHelpersTruncateAndZeroFill) { LVH_WINDOWS_MAX_DEVICE_NAME_SIZE - 1U ); EXPECT_EQ( - std::string_view {create_request.name}, + std::string_view {create_request.name.data()}, std::string_view {oversized_name}.substr(0U, LVH_WINDOWS_MAX_DEVICE_NAME_SIZE - 1U) ); EXPECT_EQ(create_request.name[LVH_WINDOWS_MAX_DEVICE_NAME_SIZE - 1U], '\0'); @@ -191,9 +191,9 @@ TEST(WindowsProtocolTest, PacksGamepadCreateRequest) { EXPECT_EQ(request.report_sizes.output_report_size, options.profile.output_report_size); EXPECT_EQ(request.report_sizes.report_descriptor_size, options.profile.report_descriptor.size()); EXPECT_EQ(request.report_descriptor[0], options.profile.report_descriptor[0]); - EXPECT_STREQ(request.name, options.profile.name.c_str()); - EXPECT_STREQ(request.manufacturer, options.profile.manufacturer.c_str()); - EXPECT_STREQ(request.stable_id, options.metadata.stable_id.c_str()); + EXPECT_STREQ(request.name.data(), options.profile.name.c_str()); + EXPECT_STREQ(request.manufacturer.data(), options.profile.manufacturer.c_str()); + EXPECT_STREQ(request.stable_id.data(), options.metadata.stable_id.c_str()); EXPECT_NE(request.flags & LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_RUMBLE, 0U); EXPECT_NE(request.flags & LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_MOTION, 0U); EXPECT_NE(request.flags & LVH_WINDOWS_GAMEPAD_FLAG_SUPPORTS_TOUCHPAD, 0U); @@ -223,8 +223,8 @@ TEST(WindowsProtocolTest, PresentsXboxSeriesNativeWindowsIdentityAndReportShape) const auto request = lvh::detail::windows::make_create_gamepad_request(8, options); const std::vector descriptor( - request.report_descriptor, - request.report_descriptor + request.report_sizes.report_descriptor_size + request.report_descriptor.data(), + request.report_descriptor.data() + request.report_sizes.report_descriptor_size ); EXPECT_EQ(request.bus_type, LVH_WINDOWS_BUS_USB); @@ -281,8 +281,8 @@ TEST(WindowsProtocolTest, PresentsBuiltInGenericControllerAsDirectInputPidJoysti const auto request = lvh::detail::windows::make_create_gamepad_request(8, options); const std::vector descriptor( - request.report_descriptor, - request.report_descriptor + request.report_sizes.report_descriptor_size + request.report_descriptor.data(), + request.report_descriptor.data() + request.report_sizes.report_descriptor_size ); ASSERT_GE(descriptor.size(), 6U);