code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/set/settings_types.h"
#include "hid_core/hid_result.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/hid_firmware_settings.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/palma/palma.h"
#include "hid_core/resources/touch_screen/touch_screen.h"

namespace Service::HID {

IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                                   std::shared_ptr<HidFirmwareSettings> settings)
    : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
      resource_manager{resource}, firmware_settings{settings} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {31, nullptr, "SendKeyboardLockKeyEvent"},
        {101, nullptr, "AcquireHomeButtonEventHandle"},
        {111, nullptr, "ActivateHomeButton"},
        {121, nullptr, "AcquireSleepButtonEventHandle"},
        {131, nullptr, "ActivateSleepButton"},
        {141, nullptr, "AcquireCaptureButtonEventHandle"},
        {151, nullptr, "ActivateCaptureButton"},
        {161, &IHidSystemServer::GetPlatformConfig, "GetPlatformConfig"},
        {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
        {211, nullptr, "GetNpadsWithNfc"},
        {212, nullptr, "AcquireNfcActivateEventHandle"},
        {213, nullptr, "ActivateNfc"},
        {214, nullptr, "GetXcdHandleForNpadWithNfc"},
        {215, nullptr, "IsNfcActivated"},
        {230, nullptr, "AcquireIrSensorEventHandle"},
        {231, nullptr, "ActivateIrSensor"},
        {232, nullptr, "GetIrSensorState"},
        {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
        {301, nullptr, "ActivateNpadSystem"},
        {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
        {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
        {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
        {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
        {307, nullptr, "GetNpadSystemExtStyle"},
        {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
        {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
        {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
        {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
        {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
        {313, &IHidSystemServer::GetNpadCaptureButtonAssignment, "GetNpadCaptureButtonAssignment"},
        {314, nullptr, "GetAppletFooterUiType"},
        {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
        {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
        {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
        {318, &IHidSystemServer::HasBattery, "HasBattery"},
        {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
        {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
        {322, &IHidSystemServer::SetNpadSystemExtStateEnabled, "SetNpadSystemExtStateEnabled"},
        {323, nullptr, "GetLastActiveUniquePad"},
        {324, nullptr, "GetUniquePadButtonSet"},
        {325, nullptr, "GetUniquePadColor"},
        {326, nullptr, "GetUniquePadAppletDetailedUiType"},
        {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
        {328, nullptr, "AttachAbstractedPadToNpad"},
        {329, nullptr, "DetachAbstractedPadAll"},
        {330, nullptr, "CheckAbstractedPadConnection"},
        {500, nullptr, "SetAppletResourceUserId"},
        {501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
        {502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
        {503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
        {504, &IHidSystemServer::SetAruidValidForVibration, "SetAruidValidForVibration"},
        {505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
        {506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
        {507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
        {510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
        {511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
        {512, &IHidSystemServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
        {513, &IHidSystemServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
        {514, nullptr, "Unknown514"},
        {520, nullptr, "EnableHandheldHids"},
        {521, nullptr, "DisableHandheldHids"},
        {522, nullptr, "SetJoyConRailEnabled"},
        {523, &IHidSystemServer::IsJoyConRailEnabled, "IsJoyConRailEnabled"},
        {524, nullptr, "IsHandheldHidsEnabled"},
        {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"},
        {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
        {541, nullptr, "GetPlayReportControllerUsages"},
        {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
        {543, nullptr, "GetRegisteredDevicesOld"},
        {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
        {545, nullptr, "SendConnectionTrigger"},
        {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
        {547, nullptr, "GetAllowedBluetoothLinksCount"},
        {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
        {549, nullptr, "GetConnectableRegisteredDevices"},
        {700, nullptr, "ActivateUniquePad"},
        {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
        {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
        {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
        {800, nullptr, "ListSixAxisSensorHandles"},
        {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
        {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
        {803, nullptr, "StartSixAxisSensorUserCalibration"},
        {804, nullptr, "CancelSixAxisSensorUserCalibration"},
        {805, nullptr, "GetUniquePadBluetoothAddress"},
        {806, nullptr, "DisconnectUniquePad"},
        {807, nullptr, "GetUniquePadType"},
        {808, nullptr, "GetUniquePadInterface"},
        {809, nullptr, "GetUniquePadSerialNumber"},
        {810, nullptr, "GetUniquePadControllerNumber"},
        {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
        {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
        {821, nullptr, "StartAnalogStickManualCalibration"},
        {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
        {823, nullptr, "CancelAnalogStickManualCalibration"},
        {824, nullptr, "ResetAnalogStickManualCalibration"},
        {825, nullptr, "GetAnalogStickState"},
        {826, nullptr, "GetAnalogStickManualCalibrationStage"},
        {827, nullptr, "IsAnalogStickButtonPressed"},
        {828, nullptr, "IsAnalogStickInReleasePosition"},
        {829, nullptr, "IsAnalogStickInCircumference"},
        {830, nullptr, "SetNotificationLedPattern"},
        {831, nullptr, "SetNotificationLedPatternWithTimeout"},
        {832, nullptr, "PrepareHidsForNotificationWake"},
        {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
        {851, &IHidSystemServer::EnableUsbFullKeyController, "EnableUsbFullKeyController"},
        {852, nullptr, "IsUsbConnected"},
        {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
        {900, nullptr, "ActivateInputDetector"},
        {901, nullptr, "NotifyInputDetector"},
        {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
        {1001, nullptr, "GetFirmwareVersion"},
        {1002, nullptr, "GetAvailableFirmwareVersion"},
        {1003, nullptr, "IsFirmwareUpdateAvailable"},
        {1004, &IHidSystemServer::CheckFirmwareUpdateRequired, "CheckFirmwareUpdateRequired"},
        {1005, nullptr, "StartFirmwareUpdate"},
        {1006, nullptr, "AbortFirmwareUpdate"},
        {1007, nullptr, "GetFirmwareUpdateState"},
        {1008, nullptr, "ActivateAudioControl"},
        {1009, nullptr, "AcquireAudioControlEventHandle"},
        {1010, nullptr, "GetAudioControlStates"},
        {1011, nullptr, "DeactivateAudioControl"},
        {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
        {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
        {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
        {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
        {1100, nullptr, "GetHidbusSystemServiceObject"},
        {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"},
        {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"},
        {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"},
        {1132, &IHidSystemServer::CheckUsbFirmwareUpdateRequired, "CheckUsbFirmwareUpdateRequired"},
        {1133, nullptr, "StartUsbFirmwareUpdate"},
        {1134, nullptr, "GetUsbFirmwareUpdateState"},
        {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
        {1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"},
        {1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"},
        {1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"},
        {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
        {1154, nullptr, "IsFirmwareAvailableForNotification"},
        {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
        {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
        {1157, nullptr, "CancelConnectionTrigger"},
        {1200, nullptr, "IsButtonConfigSupported"},
        {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
        {1202, nullptr, "DeleteButtonConfig"},
        {1203, nullptr, "DeleteButtonConfigEmbedded"},
        {1204, nullptr, "SetButtonConfigEnabled"},
        {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
        {1206, nullptr, "IsButtonConfigEnabled"},
        {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
        {1208, nullptr, "SetButtonConfigEmbedded"},
        {1209, nullptr, "SetButtonConfigFull"},
        {1210, nullptr, "SetButtonConfigLeft"},
        {1211, nullptr, "SetButtonConfigRight"},
        {1212, nullptr, "GetButtonConfigEmbedded"},
        {1213, nullptr, "GetButtonConfigFull"},
        {1214, nullptr, "GetButtonConfigLeft"},
        {1215, nullptr, "GetButtonConfigRight"},
        {1250, nullptr, "IsCustomButtonConfigSupported"},
        {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
        {1252, nullptr, "IsDefaultButtonConfigFull"},
        {1253, nullptr, "IsDefaultButtonConfigLeft"},
        {1254, nullptr, "IsDefaultButtonConfigRight"},
        {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
        {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
        {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
        {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
        {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
        {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
        {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
        {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
        {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
        {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
        {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
        {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
        {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
        {1268, nullptr, "DeleteButtonConfigStorageFull"},
        {1269, nullptr, "DeleteButtonConfigStorageLeft"},
        {1270, nullptr, "DeleteButtonConfigStorageRight"},
        {1271, &IHidSystemServer::IsUsingCustomButtonConfig, "IsUsingCustomButtonConfig"},
        {1272, &IHidSystemServer::IsAnyCustomButtonConfigEnabled, "IsAnyCustomButtonConfigEnabled"},
        {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
        {1274, nullptr, "SetDefaultButtonConfig"},
        {1275, nullptr, "SetAllDefaultButtonConfig"},
        {1276, nullptr, "SetHidButtonConfigEmbedded"},
        {1277, nullptr, "SetHidButtonConfigFull"},
        {1278, nullptr, "SetHidButtonConfigLeft"},
        {1279, nullptr, "SetHidButtonConfigRight"},
        {1280, nullptr, "GetHidButtonConfigEmbedded"},
        {1281, nullptr, "GetHidButtonConfigFull"},
        {1282, nullptr, "GetHidButtonConfigLeft"},
        {1283, nullptr, "GetHidButtonConfigRight"},
        {1284, nullptr, "GetButtonConfigStorageEmbedded"},
        {1285, nullptr, "GetButtonConfigStorageFull"},
        {1286, nullptr, "GetButtonConfigStorageLeft"},
        {1287, nullptr, "GetButtonConfigStorageRight"},
        {1288, nullptr, "SetButtonConfigStorageEmbedded"},
        {1289, nullptr, "SetButtonConfigStorageFull"},
        {1290, nullptr, "DeleteButtonConfigStorageRight"},
        {1291, nullptr, "DeleteButtonConfigStorageRight"},
    };
    // clang-format on

    RegisterHandlers(functions);

    joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
    acquire_device_registered_event =
        service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
    acquire_connection_trigger_timeout_event =
        service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
    unique_pad_connection_event =
        service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
}

IHidSystemServer::~IHidSystemServer() {
    service_context.CloseEvent(joy_detach_event);
    service_context.CloseEvent(acquire_device_registered_event);
    service_context.CloseEvent(acquire_connection_trigger_timeout_event);
    service_context.CloseEvent(unique_pad_connection_event);
};

void IHidSystemServer::GetPlatformConfig(HLERequestContext& ctx) {
    const auto platform_config = firmware_settings->GetPlatformConfig();

    LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushRaw(platform_config);
}

void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, true);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, false);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
    Core::HID::NpadIdType npad_id{};
    const Result result = GetResourceManager()->GetNpad()->GetLastActiveNpad(npad_id);

    LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.PushEnum(npad_id);
}

void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    Core::HID::NpadColor left_color{};
    Core::HID::NpadColor right_color{};
    // TODO: Get colors from Npad

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.PushRaw(left_color);
    rb.PushRaw(right_color);
}

void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    Core::HID::NpadStyleSet supported_styleset{};
    const auto& npad = GetResourceManager()->GetNpad();
    const Result result =
        npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.PushEnum(supported_styleset);
}

void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    const auto& npad = GetResourceManager()->GetNpad();
    const auto result =
        npad->SetSupportedNpadStyleSet(applet_resource_user_id, Core::HID::NpadStyleSet::All);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::GetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};
    const auto capture_button_list_size{ctx.GetWriteBufferNumElements<Core::HID::NpadButton>()};

    LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    std::vector<Core::HID::NpadButton> capture_button_list(capture_button_list_size);
    const auto& npad = GetResourceManager()->GetNpad();
    const u64 list_size =
        npad->GetNpadCaptureButtonAssignment(capture_button_list, applet_resource_user_id);

    if (list_size != 0) {
        ctx.WriteBuffer(capture_button_list);
    }

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push(list_size);
}

void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    const AppletDetailedUiType detailed_ui_type =
        GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushRaw(detailed_ui_type);
}

void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}

void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
    rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}

void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(false);
}

void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    struct LeftRightBattery {
        bool left;
        bool right;
    };

    LeftRightBattery left_right_battery{
        .left = false,
        .right = false,
    };

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushRaw(left_right_battery);
}

void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};

    LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
              npad_id_type); // Spams a lot when controller applet is running

    const std::vector<Core::HID::UniquePadId> unique_pads{};

    if (!unique_pads.empty()) {
        ctx.WriteBuffer(unique_pads);
    }

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(static_cast<u32>(unique_pads.size()));
}

void IHidSystemServer::SetNpadSystemExtStateEnabled(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_BYTES_NOINIT(7);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    const auto result = GetResourceManager()->GetNpad()->SetNpadSystemExtStateEnabled(
        parameters.applet_resource_user_id, parameters.is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}
void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool enable_input;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, enable_input={}, applet_resource_user_id={}",
             parameters.enable_input, parameters.applet_resource_user_id);

    Result result = GetResourceManager()->RegisterAppletResourceUserId(
        parameters.applet_resource_user_id, parameters.enable_input);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    u64 applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    GetResourceManager()->EnableInput(parameters.applet_resource_user_id, parameters.is_enabled);
    GetResourceManager()->GetNpad()->EnableAppletToGetInput(parameters.applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::SetAruidValidForVibration(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    GetResourceManager()->SetAruidValidForVibration(parameters.applet_resource_user_id,
                                                    parameters.is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    GetResourceManager()->EnableTouchScreen(parameters.applet_resource_user_id,
                                            parameters.is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::EnableAppletToGetPadInput(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    GetResourceManager()->EnablePadInput(parameters.applet_resource_user_id, parameters.is_enabled);
    GetResourceManager()->GetNpad()->EnableAppletToGetInput(parameters.applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    struct Parameters {
        bool is_enabled;
        INSERT_PADDING_WORDS_NOINIT(1);
        u64 applet_resource_user_id;
    };
    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");

    const auto parameters{rp.PopRaw<Parameters>()};

    LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
             parameters.is_enabled, parameters.applet_resource_user_id);

    GetResourceManager()->EnableTouchScreen(parameters.applet_resource_user_id,
                                            parameters.is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto master_volume{rp.Pop<f32>()};

    LOG_INFO(Service_HID, "called, volume={}", master_volume);

    const auto result =
        GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
            master_volume);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
    f32 master_volume{};
    const auto result =
        GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
            master_volume);

    LOG_INFO(Service_HID, "called, volume={}", master_volume);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.Push(master_volume);
}

void IHidSystemServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto applet_resource_user_id{rp.Pop<u64>()};

    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);

    const auto result =
        GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
            applet_resource_user_id);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
    LOG_INFO(Service_HID, "called");

    const auto result =
        GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) {
    const bool is_attached = true;

    LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(is_attached);
}

void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
    const bool is_attached = true;

    LOG_DEBUG(Service_HID, "(STUBBED) called, is_attached={}", is_attached);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(is_attached);
}

void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
    LOG_INFO(Service_AM, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(ResultSuccess);
    rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}

void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
    LOG_INFO(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(ResultSuccess);
    rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}

void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    struct RegisterData {
        std::array<u8, 0x68> data;
    };
    static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
    std::vector<RegisterData> registered_devices{};

    if (!registered_devices.empty()) {
        ctx.WriteBuffer(registered_devices);
    }

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<u64>(registered_devices.size());
}

void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
    rb.Push(ResultSuccess);
}

void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
    LOG_DEBUG(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<u64>(0);
}

void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
    LOG_INFO(Service_AM, "called");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(ResultSuccess);
    rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
}

void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
    const bool is_enabled = false;

    LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(is_enabled);
}

void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto is_enabled{rp.Pop<bool>()};

    LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
    const bool button_pressed = false;

    LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
              button_pressed); // Spams a lot when controller applet is open

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(button_pressed);
}

void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::CheckFirmwareUpdateRequired(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::InitializeUsbFirmwareUpdate(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
    LOG_WARNING(Service_HID, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto point1x{rp.Pop<f32>()};
    const auto point1y{rp.Pop<f32>()};
    const auto point2x{rp.Pop<f32>()};
    const auto point2y{rp.Pop<f32>()};

    LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x,
             point2y);

    const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification(
        point1x, point1y, point2x, point2y);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) {
    LOG_INFO(Service_HID, "called");

    Core::HID::FirmwareVersion firmware{};
    const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware);

    IPC::ResponseBuilder rb{ctx, 6};
    rb.Push(result);
    rb.PushRaw(firmware);
}

void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};

    LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode);

    if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
        touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
        touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
    }

    const Result result =
        GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration(
            touchscreen_config);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
    LOG_INFO(Service_HID, "called");

    Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
    const Result result =
        GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration(
            touchscreen_config);

    if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
        touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
        touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
    }

    IPC::ResponseBuilder rb{ctx, 6};
    rb.Push(result);
    rb.PushRaw(touchscreen_config);
}

void IHidSystemServer::SetForceHandheldStyleVibration(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto is_forced{rp.Pop<bool>()};

    LOG_INFO(Service_HID, "called, is_forced={}", is_forced);

    GetResourceManager()->SetForceHandheldStyleVibration(is_forced);
    GetResourceManager()->GetNpad()->UpdateHandheldAbstractState();

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
    const bool is_enabled = false;

    LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(is_enabled);
}

void IHidSystemServer::IsAnyCustomButtonConfigEnabled(HLERequestContext& ctx) {
    const bool is_enabled = false;

    LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(is_enabled);
}

std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
    resource_manager->Initialize();
    return resource_manager;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Kernel {
class KEvent;
}

namespace Service::HID {
class ResourceManager;
class HidFirmwareSettings;

class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
public:
    explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                              std::shared_ptr<HidFirmwareSettings> settings);
    ~IHidSystemServer() override;

private:
    void GetPlatformConfig(HLERequestContext& ctx);
    void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
    void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
    void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
    void GetLastActiveNpad(HLERequestContext& ctx);
    void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
    void GetNpadFullKeyGripColor(HLERequestContext& ctx);
    void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
    void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
    void GetNpadCaptureButtonAssignment(HLERequestContext& ctx);
    void GetAppletDetailedUiType(HLERequestContext& ctx);
    void GetNpadInterfaceType(HLERequestContext& ctx);
    void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
    void HasBattery(HLERequestContext& ctx);
    void HasLeftRightBattery(HLERequestContext& ctx);
    void GetUniquePadsFromNpad(HLERequestContext& ctx);
    void SetNpadSystemExtStateEnabled(HLERequestContext& ctx);
    void RegisterAppletResourceUserId(HLERequestContext& ctx);
    void UnregisterAppletResourceUserId(HLERequestContext& ctx);
    void EnableAppletToGetInput(HLERequestContext& ctx);
    void SetAruidValidForVibration(HLERequestContext& ctx);
    void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
    void EnableAppletToGetPadInput(HLERequestContext& ctx);
    void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
    void SetVibrationMasterVolume(HLERequestContext& ctx);
    void GetVibrationMasterVolume(HLERequestContext& ctx);
    void BeginPermitVibrationSession(HLERequestContext& ctx);
    void EndPermitVibrationSession(HLERequestContext& ctx);
    void IsJoyConRailEnabled(HLERequestContext& ctx);
    void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
    void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
    void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
    void GetRegisteredDevices(HLERequestContext& ctx);
    void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
    void GetUniquePadIds(HLERequestContext& ctx);
    void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
    void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
    void EnableUsbFullKeyController(HLERequestContext& ctx);
    void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
    void InitializeFirmwareUpdate(HLERequestContext& ctx);
    void CheckFirmwareUpdateRequired(HLERequestContext& ctx);
    void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx);
    void InitializeUsbFirmwareUpdate(HLERequestContext& ctx);
    void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
    void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
    void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
    void SetTouchScreenMagnification(HLERequestContext& ctx);
    void GetTouchScreenFirmwareVersion(HLERequestContext& ctx);
    void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
    void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
    void SetForceHandheldStyleVibration(HLERequestContext& ctx);
    void IsUsingCustomButtonConfig(HLERequestContext& ctx);
    void IsAnyCustomButtonConfigEnabled(HLERequestContext& ctx);

    std::shared_ptr<ResourceManager> GetResourceManager();

    Kernel::KEvent* acquire_connection_trigger_timeout_event;
    Kernel::KEvent* acquire_device_registered_event;
    Kernel::KEvent* joy_detach_event;
    Kernel::KEvent* unique_pad_connection_event;
    KernelHelpers::ServiceContext service_context;
    std::shared_ptr<ResourceManager> resource_manager;
    std::shared_ptr<HidFirmwareSettings> firmware_settings;
};

} // namespace Service::HID

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
#include "hid_core/hid_types.h"
#include "hid_core/hidbus/ringcon.h"
#include "hid_core/hidbus/starlink.h"
#include "hid_core/hidbus/stubbed.h"

namespace Service::HID {
// (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};

Hidbus::Hidbus(Core::System& system_)
    : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {

    // clang-format off
    static const FunctionInfo functions[] = {
            {1, C<&Hidbus::GetBusHandle>, "GetBusHandle"},
            {2, C<&Hidbus::IsExternalDeviceConnected>, "IsExternalDeviceConnected"},
            {3, C<&Hidbus::Initialize>, "Initialize"},
            {4, C<&Hidbus::Finalize>, "Finalize"},
            {5, C<&Hidbus::EnableExternalDevice>, "EnableExternalDevice"},
            {6, C<&Hidbus::GetExternalDeviceId>, "GetExternalDeviceId"},
            {7, C<&Hidbus::SendCommandAsync>, "SendCommandAsync"},
            {8, C<&Hidbus::GetSendCommandAsynceResult>, "GetSendCommandAsynceResult"},
            {9, C<&Hidbus::SetEventForSendCommandAsycResult>, "SetEventForSendCommandAsycResult"},
            {10, C<&Hidbus::GetSharedMemoryHandle>, "GetSharedMemoryHandle"},
            {11, C<&Hidbus::EnableJoyPollingReceiveMode>, "EnableJoyPollingReceiveMode"},
            {12, C<&Hidbus::DisableJoyPollingReceiveMode>, "DisableJoyPollingReceiveMode"},
            {13, nullptr, "GetPollingData"},
            {14, C<&Hidbus::SetStatusManagerType>, "SetStatusManagerType"},
    };
    // clang-format on

    RegisterHandlers(functions);

    // Register update callbacks
    hidbus_update_event = Core::Timing::CreateEvent(
        "Hidbus::UpdateCallback",
        [this](s64 time,
               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
            const auto guard = LockService();
            UpdateHidbus(ns_late);
            return std::nullopt;
        });

    system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
                                              hidbus_update_event);
}

Hidbus::~Hidbus() {
    system.CoreTiming().UnscheduleEvent(hidbus_update_event);
}

void Hidbus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
    if (is_hidbus_enabled) {
        for (std::size_t i = 0; i < devices.size(); ++i) {
            if (!devices[i].is_device_initialized) {
                continue;
            }
            auto& device = devices[i].device;
            device->OnUpdate();
            auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
            cur_entry.is_polling_mode = device->IsPollingMode();
            cur_entry.polling_mode = device->GetPollingMode();
            cur_entry.is_enabled = device->IsEnabled();

            u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
            std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
                        sizeof(HidbusStatusManagerEntry));
        }
    }
}

std::optional<std::size_t> Hidbus::GetDeviceIndexFromHandle(BusHandle handle) const {
    for (std::size_t i = 0; i < devices.size(); ++i) {
        const auto& device_handle = devices[i].handle;
        if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
            handle.internal_index == device_handle.internal_index &&
            handle.player_number == device_handle.player_number &&
            handle.bus_type_id == device_handle.bus_type_id &&
            handle.is_valid == device_handle.is_valid) {
            return i;
        }
    }
    return std::nullopt;
}

Result Hidbus::GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
                            Core::HID::NpadIdType npad_id, BusType bus_type,
                            AppletResourceUserId aruid) {
    LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", npad_id,
             bus_type, aruid.pid);

    bool is_handle_found = 0;
    std::size_t handle_index = 0;

    for (std::size_t i = 0; i < devices.size(); i++) {
        const auto& handle = devices[i].handle;
        if (!handle.is_valid) {
            continue;
        }
        if (handle.player_number.As<Core::HID::NpadIdType>() == npad_id &&
            handle.bus_type_id == static_cast<u8>(bus_type)) {
            is_handle_found = true;
            handle_index = i;
            break;
        }
    }

    // Handle not found. Create a new one
    if (!is_handle_found) {
        for (std::size_t i = 0; i < devices.size(); i++) {
            if (devices[i].handle.is_valid) {
                continue;
            }
            devices[i].handle.raw = 0;
            devices[i].handle.abstracted_pad_id.Assign(i);
            devices[i].handle.internal_index.Assign(i);
            devices[i].handle.player_number.Assign(static_cast<u8>(npad_id));
            devices[i].handle.bus_type_id.Assign(static_cast<u8>(bus_type));
            devices[i].handle.is_valid.Assign(true);
            handle_index = i;
            break;
        }
    }

    *out_is_valid = true;
    *out_bus_handle = devices[handle_index].handle;
    R_SUCCEED();
}

Result Hidbus::IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle) {
    LOG_INFO(Service_HID,
             "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
             "player_number={}, is_valid={}",
             bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
             bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    *out_is_connected = devices[device_index.value()].device->IsDeviceActivated();
    R_SUCCEED();
}

Result Hidbus::Initialize(BusHandle bus_handle, AppletResourceUserId aruid) {
    LOG_INFO(Service_HID,
             "called, abstracted_pad_id={} bus_type={} internal_index={} "
             "player_number={} is_valid={}, applet_resource_user_id={}",
             bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
             bus_handle.player_number, bus_handle.is_valid, aruid.pid);

    is_hidbus_enabled = true;

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    const auto entry_index = devices[device_index.value()].handle.internal_index;
    auto& cur_entry = hidbus_status.entries[entry_index];

    if (bus_handle.internal_index == 0 && Settings::values.enable_ring_controller) {
        MakeDevice<RingController>(bus_handle);
        devices[device_index.value()].is_device_initialized = true;
        devices[device_index.value()].device->ActivateDevice();
        cur_entry.is_in_focus = true;
        cur_entry.is_connected = true;
        cur_entry.is_connected_result = ResultSuccess;
        cur_entry.is_enabled = false;
        cur_entry.is_polling_mode = false;
    } else {
        MakeDevice<HidbusStubbed>(bus_handle);
        devices[device_index.value()].is_device_initialized = true;
        cur_entry.is_in_focus = true;
        cur_entry.is_connected = false;
        cur_entry.is_connected_result = ResultSuccess;
        cur_entry.is_enabled = false;
        cur_entry.is_polling_mode = false;
    }

    std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
                sizeof(hidbus_status));
    R_SUCCEED();
}

Result Hidbus::Finalize(BusHandle bus_handle, AppletResourceUserId aruid) {
    LOG_INFO(Service_HID,
             "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
             "player_number={}, is_valid={}, applet_resource_user_id={}",
             bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
             bus_handle.player_number, bus_handle.is_valid, aruid.pid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    const auto entry_index = devices[device_index.value()].handle.internal_index;
    auto& cur_entry = hidbus_status.entries[entry_index];
    auto& device = devices[device_index.value()].device;
    devices[device_index.value()].is_device_initialized = false;
    device->DeactivateDevice();

    cur_entry.is_in_focus = true;
    cur_entry.is_connected = false;
    cur_entry.is_connected_result = ResultSuccess;
    cur_entry.is_enabled = false;
    cur_entry.is_polling_mode = false;
    std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
                sizeof(hidbus_status));
    R_SUCCEED();
}

Result Hidbus::EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
                                    AppletResourceUserId aruid) {
    LOG_DEBUG(Service_HID,
              "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
              "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
              is_enabled, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
              bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid, inval,
              aruid.pid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);
    devices[device_index.value()].device->Enable(is_enabled);
    R_SUCCEED();
}

Result Hidbus::GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle) {
    LOG_DEBUG(Service_HID,
              "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
              "is_valid={}",
              bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
              bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    *out_device_id = devices[device_index.value()].device->GetDeviceId();
    R_SUCCEED();
}

Result Hidbus::SendCommandAsync(BusHandle bus_handle,
                                InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
    LOG_DEBUG(Service_HID,
              "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
              "player_number={}, is_valid={}",
              buffer_data.size(), bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
              bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    devices[device_index.value()].device->SetCommand(buffer_data);
    R_SUCCEED();
};

Result Hidbus::GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
                                          OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data) {
    LOG_DEBUG(Service_HID,
              "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
              "is_valid={}",
              bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
              bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    *out_data_size = devices[device_index.value()].device->GetReply(out_buffer_data);
    R_SUCCEED();
};

Result Hidbus::SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
                                                BusHandle bus_handle) {
    LOG_INFO(Service_HID,
             "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
             "is_valid={}",
             bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
             bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    *out_event = &devices[device_index.value()].device->GetSendCommandAsycEvent();
    R_SUCCEED();
};

Result Hidbus::GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) {
    LOG_DEBUG(Service_HID, "called");

    *out_shared_memory = &system.Kernel().GetHidBusSharedMem();
    R_SUCCEED();
}

Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
                                           BusHandle bus_handle,
                                           InCopyHandle<Kernel::KTransferMemory> t_mem) {
    ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
    ASSERT_MSG(t_mem->GetSize() == t_mem_size, "t_mem has incorrect size");

    LOG_INFO(Service_HID,
             "called, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
             "internal_index={}, player_number={}, is_valid={}",
             polling_mode, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
             bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    auto& device = devices[device_index.value()].device;
    device->SetPollingMode(polling_mode);
    device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
    R_SUCCEED();
}

Result Hidbus::DisableJoyPollingReceiveMode(BusHandle bus_handle) {
    LOG_INFO(Service_HID,
             "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
             "is_valid={}",
             bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
             bus_handle.player_number, bus_handle.is_valid);

    const auto device_index = GetDeviceIndexFromHandle(bus_handle);

    R_UNLESS(device_index.has_value(), ResultUnknown);

    auto& device = devices[device_index.value()].device;
    device->DisablePollingMode();
    R_SUCCEED();
}

Result Hidbus::SetStatusManagerType(StatusManagerType manager_type) {
    LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
    R_SUCCEED();
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <functional>

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/hidbus/hidbus_base.h"

namespace Core::Timing {
struct EventType;
} // namespace Core::Timing

namespace Core {
class System;
} // namespace Core

namespace Service::HID {

class Hidbus final : public ServiceFramework<Hidbus> {
public:
    explicit Hidbus(Core::System& system_);
    ~Hidbus() override;

private:
    static const std::size_t max_number_of_handles = 0x13;

    enum class HidBusDeviceId : std::size_t {
        RingController = 0x20,
        FamicomRight = 0x21,
        Starlink = 0x28,
    };

    // This is nn::hidbus::detail::StatusManagerType
    enum class StatusManagerType : u32 {
        None,
        Type16,
        Type32,
    };

    // This is nn::hidbus::BusType
    enum class BusType : u64 {
        LeftJoyRail,
        RightJoyRail,
        InternalBus, // Lark microphone

        MaxBusType,
    };

    // This is nn::hidbus::BusHandle
    struct BusHandle {
        union {
            u64 raw{};

            BitField<0, 32, u64> abstracted_pad_id;
            BitField<32, 8, u64> internal_index;
            BitField<40, 8, u64> player_number;
            BitField<48, 8, u64> bus_type_id;
            BitField<56, 1, u64> is_valid;
        };
    };
    static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");

    // This is nn::hidbus::JoyPollingReceivedData
    struct JoyPollingReceivedData {
        std::array<u8, 0x30> data;
        u64 out_size;
        u64 sampling_number;
    };
    static_assert(sizeof(JoyPollingReceivedData) == 0x40,
                  "JoyPollingReceivedData is an invalid size");

    struct HidbusStatusManagerEntry {
        u8 is_connected{};
        INSERT_PADDING_BYTES(0x3);
        Result is_connected_result{0};
        u8 is_enabled{};
        u8 is_in_focus{};
        u8 is_polling_mode{};
        u8 reserved{};
        JoyPollingMode polling_mode{};
        INSERT_PADDING_BYTES(0x70); // Unknown
    };
    static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
                  "HidbusStatusManagerEntry is an invalid size");

    struct HidbusStatusManager {
        std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
        INSERT_PADDING_BYTES(0x680); // Unused
    };
    static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");

    struct HidbusDevice {
        bool is_device_initialized{};
        BusHandle handle{};
        std::unique_ptr<HidbusBase> device{nullptr};
    };

    Result GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
                        Core::HID::NpadIdType npad_id, BusType bus_type,
                        AppletResourceUserId aruid);

    Result IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle);

    Result Initialize(BusHandle bus_handle, AppletResourceUserId aruid);

    Result Finalize(BusHandle bus_handle, AppletResourceUserId aruid);

    Result EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
                                AppletResourceUserId aruid);

    Result GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle);

    Result SendCommandAsync(BusHandle bus_handle, InBuffer<BufferAttr_HipcAutoSelect> buffer_data);

    Result GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
                                      OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data);

    Result SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
                                            BusHandle bus_handle);

    Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory);

    Result EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
                                       BusHandle bus_handle,
                                       InCopyHandle<Kernel::KTransferMemory> t_mem);

    Result DisableJoyPollingReceiveMode(BusHandle bus_handle);

    Result SetStatusManagerType(StatusManagerType manager_type);

    void UpdateHidbus(std::chrono::nanoseconds ns_late);
    std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;

    template <typename T>
    void MakeDevice(BusHandle handle) {
        const auto device_index = GetDeviceIndexFromHandle(handle);
        if (device_index) {
            devices[device_index.value()].device = std::make_unique<T>(system, service_context);
        }
    }

    bool is_hidbus_enabled{false};
    HidbusStatusManager hidbus_status{};
    std::array<HidbusDevice, max_number_of_handles> devices{};
    std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
    KernelHelpers::ServiceContext service_context;
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <random>

#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/irsensor/clustering_processor.h"
#include "hid_core/irsensor/image_transfer_processor.h"
#include "hid_core/irsensor/ir_led_processor.h"
#include "hid_core/irsensor/moment_processor.h"
#include "hid_core/irsensor/pointing_processor.h"
#include "hid_core/irsensor/tera_plugin_processor.h"

namespace Service::IRS {

IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
        {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
        {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
        {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
        {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
        {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
        {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
        {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
        {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
        {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
        {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
        {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
        {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
        {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
        {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
        {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
        {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
        {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
    };
    // clang-format on

    u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
    RegisterHandlers(functions);
    shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));

    npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
IRS::~IRS() = default;

Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
    R_SUCCEED();
}

Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
    R_SUCCEED();
}

Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
                                          ClientAppletResourceUserId aruid) {
    LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);

    *out_shared_memory = &system.Kernel().GetIrsSharedMem();
    R_SUCCEED();
}

Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                               ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    // TODO: Stop Image processor
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::Active);
    R_SUCCEED();
}

Result IRS::RunMomentProcessor(
    Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
    const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::RunClusteringProcessor(
    Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
    const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::RunImageTransferProcessor(
    Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
    const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
    u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {

    ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");

    LOG_INFO(Service_IRS,
             "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
             "applet_resource_user_id={}",
             camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
             aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::GetImageTransferProcessorState(
    Out<Core::IrSensor::ImageTransferProcessorState> out_state,
    Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
    OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
    LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
              camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);

    R_TRY(IsIrCameraHandleValid(camera_handle));
    R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
             InvalidProcessorState);

    *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);

    R_SUCCEED();
}

Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                                   Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
                                   ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
                "applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
                processor_config.required_mcu_version.major,
                processor_config.required_mcu_version.minor, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessor<TeraPluginProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
                                  Core::HID::NpadIdType npad_id) {
    R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);

    *out_camera_handle = {
        .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
        .npad_type = Core::HID::NpadStyleIndex::None,
    };

    LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
             out_camera_handle->npad_id, out_camera_handle->npad_type);

    R_SUCCEED();
}

Result IRS::RunPointingProcessor(
    Core::IrSensor::IrCameraHandle camera_handle,
    const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
    ClientAppletResourceUserId aruid) {
    LOG_WARNING(
        Service_IRS,
        "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
        camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
        processor_config.required_mcu_version.minor, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessor<PointingProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                                  ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    // TODO: Suspend image processor

    R_SUCCEED();
}

Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
                                 Core::IrSensor::PackedMcuVersion mcu_version,
                                 ClientAppletResourceUserId aruid) {
    LOG_WARNING(
        Service_IRS,
        "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
        camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
        mcu_version.minor);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    // TODO: Check firmware version

    R_SUCCEED();
}

Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
                             Core::IrSensor::PackedFunctionLevel function_level,
                             ClientAppletResourceUserId aruid) {
    LOG_WARNING(
        Service_IRS,
        "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
        camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    // TODO: Set Function level

    R_SUCCEED();
}

Result IRS::RunImageTransferExProcessor(
    Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
    const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
    u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {

    ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");

    LOG_INFO(Service_IRS,
             "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
             "applet_resource_user_id={}",
             camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                              Core::IrSensor::PackedIrLedProcessorConfig processor_config,
                              ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
                "applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
                processor_config.required_mcu_version.major,
                processor_config.required_mcu_version.minor, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
    MakeProcessor<IrLedProcessor>(camera_handle, device);
    auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
    image_transfer_processor.SetConfig(processor_config);
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::IR);

    R_SUCCEED();
}

Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
                                    ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS,
                "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
                camera_handle.npad_type, camera_handle.npad_id, aruid.pid);

    R_TRY(IsIrCameraHandleValid(camera_handle));

    // TODO: Stop image processor async
    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                Common::Input::PollingMode::Active);

    R_SUCCEED();
}

Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
                                              ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
                function_level.function_level, aruid.pid);
    R_SUCCEED();
}

Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
    if (camera_handle.npad_id >
        static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
        return InvalidIrCameraHandle;
    }
    if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
        return InvalidIrCameraHandle;
    }
    return ResultSuccess;
}

Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
    const Core::IrSensor::IrCameraHandle& camera_handle) {
    const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device));
    ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id");
    return shared_memory->device[camera_handle.npad_id];
}

IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {500, nullptr, "SetAppletResourceUserId"},
        {501, nullptr, "RegisterAppletResourceUserId"},
        {502, nullptr, "UnregisterAppletResourceUserId"},
        {503, nullptr, "EnableAppletToGetInput"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IRS_SYS::~IRS_SYS() = default;

} // namespace Service::IRS


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h"

namespace Core::HID {
class EmulatedController;
} // namespace Core::HID

namespace Service::IRS {

class IRS final : public ServiceFramework<IRS> {
public:
    explicit IRS(Core::System& system_);
    ~IRS() override;

private:
    // This is nn::irsensor::detail::AruidFormat
    struct AruidFormat {
        u64 sensor_aruid;
        u64 sensor_aruid_status;
    };
    static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");

    // This is nn::irsensor::detail::StatusManager
    struct StatusManager {
        std::array<Core::IrSensor::DeviceFormat, 9> device;
        std::array<AruidFormat, 5> aruid;
    };
    static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");

    Result ActivateIrsensor(ClientAppletResourceUserId aruid);

    Result DeactivateIrsensor(ClientAppletResourceUserId aruid);

    Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
                                         ClientAppletResourceUserId aruid);
    Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                              ClientAppletResourceUserId aruid);

    Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                              ClientAppletResourceUserId aruid,
                              const Core::IrSensor::PackedMomentProcessorConfig& processor_config);

    Result RunClusteringProcessor(
        Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
        const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);

    Result RunImageTransferProcessor(
        Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
        const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
        u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);

    Result GetImageTransferProcessorState(
        Out<Core::IrSensor::ImageTransferProcessorState> out_state,
        Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
        OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);

    Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                                  Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
                                  ClientAppletResourceUserId aruid);

    Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
                                 Core::HID::NpadIdType npad_id);

    Result RunPointingProcessor(
        Core::IrSensor::IrCameraHandle camera_handle,
        const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
        ClientAppletResourceUserId aruid);

    Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                                 ClientAppletResourceUserId aruid);

    Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
                                Core::IrSensor::PackedMcuVersion mcu_version,
                                ClientAppletResourceUserId aruid);

    Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
                            Core::IrSensor::PackedFunctionLevel function_level,
                            ClientAppletResourceUserId aruid);

    Result RunImageTransferExProcessor(
        Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
        const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
        u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);

    Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
                             Core::IrSensor::PackedIrLedProcessorConfig processor_config,
                             ClientAppletResourceUserId aruid);

    Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
                                   ClientAppletResourceUserId aruid);

    Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
                                             ClientAppletResourceUserId aruid);

    Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;

    Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
        const Core::IrSensor::IrCameraHandle& camera_handle);

    template <typename T>
    void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
                       Core::IrSensor::DeviceFormat& device_state) {
        const auto index = static_cast<std::size_t>(handle.npad_id);
        if (index > sizeof(processors)) {
            LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
            return;
        }
        processors[index] = std::make_unique<T>(device_state);
    }

    template <typename T>
    void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
                                      Core::IrSensor::DeviceFormat& device_state) {
        const auto index = static_cast<std::size_t>(handle.npad_id);
        if (index > sizeof(processors)) {
            LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
            return;
        }

        if constexpr (std::is_constructible_v<T, Core::System&, Core::IrSensor::DeviceFormat&,
                                              std::size_t>) {
            processors[index] = std::make_unique<T>(system, device_state, index);
        } else {
            processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
        }
    }

    template <typename T>
    T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
        const auto index = static_cast<std::size_t>(handle.npad_id);
        if (index > sizeof(processors)) {
            LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
            return static_cast<T&>(*processors[0]);
        }
        return static_cast<T&>(*processors[index]);
    }

    template <typename T>
    const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
        const auto index = static_cast<std::size_t>(handle.npad_id);
        if (index > sizeof(processors)) {
            LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
            return static_cast<T&>(*processors[0]);
        }
        return static_cast<T&>(*processors[index]);
    }

    Core::HID::EmulatedController* npad_device = nullptr;
    StatusManager* shared_memory = nullptr;
    std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};

class IRS_SYS final : public ServiceFramework<IRS_SYS> {
public:
    explicit IRS_SYS(Core::System& system);
    ~IRS_SYS() override;
};

} // namespace Service::IRS


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/hid/xcd.h"

namespace Service::HID {

XCD_SYS::XCD_SYS(Core::System& system_) : ServiceFramework{system_, "xcd:sys"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "GetDataFormat"},
        {1, nullptr, "SetDataFormat"},
        {2, nullptr, "GetMcuState"},
        {3, nullptr, "SetMcuState"},
        {4, nullptr, "GetMcuVersionForNfc"},
        {5, nullptr, "CheckNfcDevicePower"},
        {10, nullptr, "SetNfcEvent"},
        {11, nullptr, "GetNfcInfo"},
        {12, nullptr, "StartNfcDiscovery"},
        {13, nullptr, "StopNfcDiscovery"},
        {14, nullptr, "StartNtagRead"},
        {15, nullptr, "StartNtagWrite"},
        {16, nullptr, "SendNfcRawData"},
        {17, nullptr, "RegisterMifareKey"},
        {18, nullptr, "ClearMifareKey"},
        {19, nullptr, "StartMifareRead"},
        {20, nullptr, "StartMifareWrite"},
        {101, nullptr, "GetAwakeTriggerReasonForLeftRail"},
        {102, nullptr, "GetAwakeTriggerReasonForRightRail"},
        {103, nullptr, "GetAwakeTriggerBatteryLevelTransitionForLeftRail"},
        {104, nullptr, "GetAwakeTriggerBatteryLevelTransitionForRightRail"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

XCD_SYS::~XCD_SYS() = default;

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::HID {

class XCD_SYS final : public ServiceFramework<XCD_SYS> {
public:
    explicit XCD_SYS(Core::System& system_);
    ~XCD_SYS() override;
};

} // namespace Service::HID