#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "service_guard.h"
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include "kernel/shmem.h"
#include "kernel/mutex.h"
#include "kernel/rwlock.h"
#include "services/applet.h"
#include "services/hid.h"
#include "runtime/hosversion.h"
#include "runtime/diag.h"
#include "../runtime/alloc.h"

static Service g_hidSrv;
static Service g_hidIAppletResource;
static Service g_hidIActiveVibrationDeviceList;
static SharedMemory g_hidSharedmem;
static Mutex g_hidVibrationMutex;

static u8* g_sevenSixAxisSensorBuffer;
static TransferMemory g_sevenSixAxisSensorTmem0;
static TransferMemory g_sevenSixAxisSensorTmem1;

static Result _hidCreateAppletResource(Service* srv, Service* srv_out);
static Result _hidGetSharedMemoryHandle(Service* srv, Handle* handle_out);

static Result _hidActivateTouchScreen(void);
static Result _hidActivateMouse(void);
static Result _hidActivateKeyboard(void);
static Result _hidActivateNpad(void);
static Result _hidActivateGesture(void);

static Result _hidGetVibrationDeviceHandles(HidVibrationDeviceHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style);

static Result _hidCreateActiveVibrationDeviceList(Service* srv_out);

static Result _hidActivateVibrationDevice(Service* srv, HidVibrationDeviceHandle handle);

static u8 _hidGetSixAxisSensorHandleNpadStyleIndex(HidSixAxisSensorHandle handle);
static Result _hidGetSixAxisSensorHandles(HidSixAxisSensorHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style);

static Result _hidGetPalmaOperationResult(HidPalmaConnectionHandle handle);

NX_GENERATE_SERVICE_GUARD(hid);

Result _hidInitialize(void) {
    Result rc=0;
    Handle sharedmem_handle;

    rc = smGetService(&g_hidSrv, "hid");
    if (R_FAILED(rc))
        return rc;

    rc = _hidCreateAppletResource(&g_hidSrv, &g_hidIAppletResource);

    if (R_SUCCEEDED(rc))
        rc = _hidGetSharedMemoryHandle(&g_hidIAppletResource, &sharedmem_handle);

    if (R_SUCCEEDED(rc)) {
        shmemLoadRemote(&g_hidSharedmem, sharedmem_handle, 0x40000, Perm_R);
        rc = shmemMap(&g_hidSharedmem);
    }

    return rc;
}

void _hidCleanup(void) {
    if (g_sevenSixAxisSensorBuffer != NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));

    serviceClose(&g_hidIActiveVibrationDeviceList);
    shmemClose(&g_hidSharedmem);
    serviceClose(&g_hidIAppletResource);
    serviceClose(&g_hidSrv);
}

Service* hidGetServiceSession(void) {
    return &g_hidSrv;
}

void* hidGetSharedmemAddr(void) {
    return shmemGetAddr(&g_hidSharedmem);
}

static HidNpadInternalState* _hidGetNpadInternalState(HidNpadIdType id) {
    HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr();
    if (sharedmem == NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    if (id <= HidNpadIdType_No8)
        return &sharedmem->npad.entries[id].internal_state;
    else if (id == HidNpadIdType_Handheld)
        return &sharedmem->npad.entries[8].internal_state;
    else if (id == HidNpadIdType_Other)
        return &sharedmem->npad.entries[9].internal_state;
    else
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadInput));
}

u32 hidGetNpadStyleSet(HidNpadIdType id) {
    return atomic_load_explicit(&_hidGetNpadInternalState(id)->style_set, memory_order_acquire);
}

HidNpadJoyAssignmentMode hidGetNpadJoyAssignment(HidNpadIdType id) {
    HidNpadInternalState *npad = _hidGetNpadInternalState(id);

    HidNpadJoyAssignmentMode tmp = atomic_load_explicit(&npad->joy_assignment_mode, memory_order_acquire);
    if (tmp != HidNpadJoyAssignmentMode_Dual && tmp != HidNpadJoyAssignmentMode_Single)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));

    return tmp;
}

Result hidGetNpadControllerColorSingle(HidNpadIdType id, HidNpadControllerColor *color) {
    Result rc = 0;
    HidNpadInternalState *npad = _hidGetNpadInternalState(id);

    HidColorAttribute attribute = npad->full_key_color.attribute;
    if (attribute==HidColorAttribute_NoController) rc = MAKERESULT(202, 604);
    else if (attribute==HidColorAttribute_ReadError) rc = MAKERESULT(202, 603);
    else if (attribute!=HidColorAttribute_Ok) diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));

    if (R_SUCCEEDED(rc))
        *color = npad->full_key_color.full_key;

    return rc;
}

Result hidGetNpadControllerColorSplit(HidNpadIdType id, HidNpadControllerColor *color_left, HidNpadControllerColor *color_right) {
    Result rc = 0;
    HidNpadInternalState *npad = _hidGetNpadInternalState(id);

    HidColorAttribute attribute = npad->joy_color.attribute;
    if (attribute==HidColorAttribute_NoController) rc = MAKERESULT(202, 604);
    else if (attribute==HidColorAttribute_ReadError) rc = MAKERESULT(202, 603);
    else if (attribute!=HidColorAttribute_Ok) diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));

    if (R_SUCCEEDED(rc)) {
        *color_left  = npad->joy_color.left;
        *color_right = npad->joy_color.right;
    }

    return rc;
}

u32 hidGetNpadDeviceType(HidNpadIdType id) {
    return atomic_load_explicit(&_hidGetNpadInternalState(id)->device_type, memory_order_acquire);
}

void hidGetNpadSystemProperties(HidNpadIdType id, HidNpadSystemProperties *out) {
    *out = atomic_load_explicit(&_hidGetNpadInternalState(id)->system_properties, memory_order_acquire);
}

void hidGetNpadSystemButtonProperties(HidNpadIdType id, HidNpadSystemButtonProperties *out) {
    *out = atomic_load_explicit(&_hidGetNpadInternalState(id)->system_button_properties, memory_order_acquire);
}

static void _hidGetNpadPowerInfo(HidNpadInternalState *npad, HidPowerInfo *info, u64 is_charging, u64 is_powered, u32 i) {
    *info = (HidPowerInfo){
        .battery_level = atomic_load_explicit(&npad->battery_level[i], memory_order_acquire),
        .is_charging = (is_charging & BIT(i)) != 0,
        .is_powered = (is_powered & BIT(i)) != 0,
    };
    if (info->battery_level > 4) info->battery_level = 4; // sdknso would Abort when this occurs.
}

void hidGetNpadPowerInfoSingle(HidNpadIdType id, HidPowerInfo *info) {
    HidNpadInternalState *npad = _hidGetNpadInternalState(id);

    HidNpadSystemProperties properties;
    properties = atomic_load_explicit(&npad->system_properties, memory_order_acquire);

    _hidGetNpadPowerInfo(npad, info, properties.is_charging, properties.is_powered, 0);
}

void hidGetNpadPowerInfoSplit(HidNpadIdType id, HidPowerInfo *info_left, HidPowerInfo *info_right) {
    HidNpadInternalState *npad = _hidGetNpadInternalState(id);

    HidNpadSystemProperties properties;
    properties = atomic_load_explicit(&npad->system_properties, memory_order_acquire);

    _hidGetNpadPowerInfo(npad, info_left,  properties.is_charging, properties.is_powered, 1);
    _hidGetNpadPowerInfo(npad, info_right, properties.is_charging, properties.is_powered, 2);
}

u32 hidGetAppletFooterUiAttributesSet(HidNpadIdType id) {
    return atomic_load_explicit(&_hidGetNpadInternalState(id)->applet_footer_ui_attribute, memory_order_acquire);
}

HidAppletFooterUiType hidGetAppletFooterUiTypes(HidNpadIdType id) {
    return atomic_load_explicit(&_hidGetNpadInternalState(id)->applet_footer_ui_type, memory_order_acquire);
}

static size_t _hidGetStates(HidCommonLifoHeader *header, void* in_states, size_t max_states, size_t state_offset, size_t sampling_number_offset, void* states, size_t entrysize, size_t count) {
    s32 total_entries = (s32)atomic_load_explicit(&header->count, memory_order_acquire);
    if (total_entries < 0) total_entries = 0;
    if (total_entries > count) total_entries = count;
    s32 tail = (s32)atomic_load_explicit(&header->tail, memory_order_acquire);

    for (s32 i=0; i<total_entries; i++) {
        s32 entrypos = (((tail + (max_states+1)) - total_entries) + i) % max_states;
        void* state_entry = (void*)((uintptr_t)in_states + entrypos*(state_offset+entrysize));
        void* out_state = (void*)((uintptr_t)states + (total_entries-i-1)*entrysize);
        void* out_state_prev = (void*)((uintptr_t)states + (total_entries-i)*entrysize);

        u64 sampling_number0=0, sampling_number1=0;

        sampling_number0 = atomic_load_explicit((u64*)state_entry, memory_order_acquire);
        memcpy(out_state, (void*)((uintptr_t)state_entry + state_offset), entrysize);
        sampling_number1 = atomic_load_explicit((u64*)state_entry, memory_order_acquire);

        if (sampling_number0 != sampling_number1 || (i>0 && *((u64*)((uintptr_t)out_state+sampling_number_offset)) - *((u64*)((uintptr_t)out_state_prev+sampling_number_offset)) != 1)) {
            s32 tmpcount = (s32)atomic_load_explicit(&header->count, memory_order_acquire);
            tmpcount = total_entries < tmpcount ? tmpcount : total_entries;
            total_entries = tmpcount < count ? tmpcount : count;
            tail = (s32)atomic_load_explicit(&header->tail, memory_order_acquire);

            i=-1;
        }
    }

    return total_entries;
}

void hidInitializeTouchScreen(void) {
    Result rc = _hidActivateTouchScreen();
    if (R_FAILED(rc)) diagAbortWithResult(rc);
}

size_t hidGetTouchScreenStates(HidTouchScreenState *states, size_t count) {
    HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr();
    if (sharedmem == NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    size_t total = _hidGetStates(&sharedmem->touchscreen.lifo.header, sharedmem->touchscreen.lifo.storage, 17, offsetof(HidTouchScreenStateAtomicStorage,state), offsetof(HidTouchScreenState,sampling_number), states, sizeof(HidTouchScreenState), count);
    size_t max_touches = sizeof(states[0].touches)/sizeof(states[0].touches[0]);
    for (size_t i=0; i<total; i++) {
        if (states[i].count > max_touches) states[i].count = max_touches;
    }
    return total;
}

void hidInitializeMouse(void) {
    Result rc = _hidActivateMouse();
    if (R_FAILED(rc)) diagAbortWithResult(rc);
}

size_t hidGetMouseStates(HidMouseState *states, size_t count) {
    HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr();
    if (sharedmem == NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    size_t total = _hidGetStates(&sharedmem->mouse.lifo.header, sharedmem->mouse.lifo.storage, 17, offsetof(HidMouseStateAtomicStorage,state), offsetof(HidMouseState,sampling_number), states, sizeof(HidMouseState), count);
    return total;
}

void hidInitializeKeyboard(void) {
    Result rc = _hidActivateKeyboard();
    if (R_FAILED(rc)) diagAbortWithResult(rc);
}

size_t hidGetKeyboardStates(HidKeyboardState *states, size_t count) {
    HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr();
    if (sharedmem == NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    size_t total = _hidGetStates(&sharedmem->keyboard.lifo.header, sharedmem->keyboard.lifo.storage, 17, offsetof(HidKeyboardStateAtomicStorage,state), offsetof(HidKeyboardState,sampling_number), states, sizeof(HidKeyboardState), count);
    return total;
}

void hidInitializeNpad(void) {
    Result rc = _hidActivateNpad();
    if (R_FAILED(rc)) diagAbortWithResult(rc);
}

static size_t _hidGetNpadStates(HidNpadCommonLifo *lifo, HidNpadCommonState *states, size_t count) {
    if (count > 17) count = 17;
    return _hidGetStates(&lifo->header, lifo->storage, 17, offsetof(HidNpadCommonStateAtomicStorage,state), offsetof(HidNpadCommonState,sampling_number), states, sizeof(HidNpadCommonState), count);
}

size_t hidGetNpadStatesFullKey(HidNpadIdType id, HidNpadFullKeyState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->full_key_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesHandheld(HidNpadIdType id, HidNpadHandheldState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->handheld_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesJoyDual(HidNpadIdType id, HidNpadJoyDualState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->joy_dual_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesJoyLeft(HidNpadIdType id, HidNpadJoyLeftState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->joy_left_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesJoyRight(HidNpadIdType id, HidNpadJoyRightState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->joy_right_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesGc(HidNpadIdType id, HidNpadGcState *states, size_t count) {
    HidNpadCommonState tmp_entries[17];
    HidNpadGcTriggerState tmp_entries_trigger[17];

    HidNpadInternalState *npad = _hidGetNpadInternalState(id);
    size_t total = _hidGetNpadStates(&npad->full_key_lifo, tmp_entries, count);
    size_t total2 = _hidGetStates(&npad->gc_trigger_lifo.header, npad->gc_trigger_lifo.storage, 17, offsetof(HidNpadGcTriggerStateAtomicStorage,state), offsetof(HidNpadGcTriggerState,sampling_number), tmp_entries_trigger, sizeof(HidNpadGcTriggerState), total);
    if (total2 < total) total = total2;

    memset(states, 0, sizeof(HidNpadGcState) * total);

    for (size_t i=0; i<total; i++) {
        states[i].sampling_number = tmp_entries[i].sampling_number;

        // sdknso would handle button-bitmasking with ControlPadRestriction here.

        states[i].buttons = tmp_entries[i].buttons;

        memcpy(&states[i].analog_stick_l, &tmp_entries[i].analog_stick_l, sizeof(tmp_entries[i].analog_stick_l)); // sdknso uses index 0 for the src here.
        memcpy(&states[i].analog_stick_r, &tmp_entries[i].analog_stick_r, sizeof(tmp_entries[i].analog_stick_r)); // sdknso uses index 0 for the src here.
        states[i].attributes = tmp_entries[i].attributes;

        states[i].trigger_l = tmp_entries_trigger[i].trigger_l;
        states[i].trigger_r = tmp_entries_trigger[i].trigger_r;
    }

    return total;
}

size_t hidGetNpadStatesPalma(HidNpadIdType id, HidNpadPalmaState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->palma_lifo, states, count);

    // sdknso doesn't handle ControlPadRestriction with this.

    return total;
}

size_t hidGetNpadStatesLark(HidNpadIdType id, HidNpadLarkState *states, size_t count) {
    HidNpadCommonState tmp_entries[17];

    HidNpadInternalState *npad = _hidGetNpadInternalState(id);
    size_t total = _hidGetNpadStates(&npad->full_key_lifo, tmp_entries, count);

    memset(states, 0, sizeof(HidNpadLarkState) * total);

    HidNpadLarkType lark_type_l_and_main = atomic_load_explicit(&npad->lark_type_l_and_main, memory_order_acquire);
    if (!(lark_type_l_and_main>=HidNpadLarkType_H1 && lark_type_l_and_main<=HidNpadLarkType_NR)) lark_type_l_and_main = HidNpadLarkType_Invalid;

    for (size_t i=0; i<total; i++) {
        states[i].sampling_number = tmp_entries[i].sampling_number;

        // sdknso would handle button-bitmasking with ControlPadRestriction here.

        states[i].buttons = tmp_entries[i].buttons;

        // Leave analog-sticks state at zeros.

        states[i].attributes = tmp_entries[i].attributes;
        states[i].lark_type_l_and_main = lark_type_l_and_main;
    }

    return total;
}

size_t hidGetNpadStatesHandheldLark(HidNpadIdType id, HidNpadHandheldLarkState *states, size_t count) {
    HidNpadCommonState tmp_entries[17];

    HidNpadInternalState *npad = _hidGetNpadInternalState(id);
    size_t total = _hidGetNpadStates(&npad->handheld_lifo, tmp_entries, count);

    memset(states, 0, sizeof(HidNpadHandheldLarkState) * total);

    HidNpadLarkType lark_type_l_and_main = atomic_load_explicit(&npad->lark_type_l_and_main, memory_order_acquire);
    if (!(lark_type_l_and_main>=HidNpadLarkType_H1 && lark_type_l_and_main<=HidNpadLarkType_NR)) lark_type_l_and_main = HidNpadLarkType_Invalid;

    HidNpadLarkType lark_type_r = atomic_load_explicit(&npad->lark_type_r, memory_order_acquire);
    if (!(lark_type_r>=HidNpadLarkType_H1 && lark_type_r<=HidNpadLarkType_NR)) lark_type_r = HidNpadLarkType_Invalid;

    for (size_t i=0; i<total; i++) {
        states[i].sampling_number = tmp_entries[i].sampling_number;

        // sdknso would handle button-bitmasking with ControlPadRestriction here.

        states[i].buttons = tmp_entries[i].buttons;

        memcpy(&states[i].analog_stick_l, &tmp_entries[i].analog_stick_l, sizeof(tmp_entries[i].analog_stick_l)); // sdknso uses index 0 for the src here.
        memcpy(&states[i].analog_stick_r, &tmp_entries[i].analog_stick_r, sizeof(tmp_entries[i].analog_stick_r)); // sdknso uses index 0 for the src here.
        states[i].attributes = tmp_entries[i].attributes;
        states[i].lark_type_l_and_main = lark_type_l_and_main;
        states[i].lark_type_r = lark_type_r;
    }

    return total;
}

size_t hidGetNpadStatesLucia(HidNpadIdType id, HidNpadLuciaState *states, size_t count) {
    HidNpadCommonState tmp_entries[17];

    HidNpadInternalState *npad = _hidGetNpadInternalState(id);
    size_t total = _hidGetNpadStates(&npad->full_key_lifo, tmp_entries, count);

    memset(states, 0, sizeof(HidNpadLuciaState) * total);

    HidNpadLuciaType lucia_type = atomic_load_explicit(&npad->lucia_type, memory_order_acquire);
    if (!(lucia_type>=HidNpadLuciaType_J && lucia_type<=HidNpadLuciaType_U)) lucia_type = HidNpadLuciaType_Invalid;

    for (size_t i=0; i<total; i++) {
        states[i].sampling_number = tmp_entries[i].sampling_number;

        // sdknso would handle button-bitmasking with ControlPadRestriction here.

        states[i].buttons = tmp_entries[i].buttons;

        // Leave analog-sticks state at zeros.

        states[i].attributes = tmp_entries[i].attributes;
        states[i].lucia_type = lucia_type;
    }

    return total;
}

size_t hidGetNpadStatesSystemExt(HidNpadIdType id, HidNpadSystemExtState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->system_ext_lifo, states, count);

    // sdknso would handle button-bitmasking with ControlPadRestriction here.

    return total;
}

size_t hidGetNpadStatesSystem(HidNpadIdType id, HidNpadSystemState *states, size_t count) {
    size_t total = _hidGetNpadStates(&_hidGetNpadInternalState(id)->system_ext_lifo, states, count);

    for (size_t i=0; i<total; i++) {
        u64 buttons = states[i].buttons;
        u64 new_buttons = 0;

        if (buttons & HidNpadButton_AnyLeft) new_buttons |= HidNpadButton_Left;
        if (buttons & HidNpadButton_AnyUp) new_buttons |= HidNpadButton_Up;
        if (buttons & HidNpadButton_AnyRight) new_buttons |= HidNpadButton_Right;
        if (buttons & HidNpadButton_AnyDown) new_buttons |= HidNpadButton_Down;
        if (buttons & (HidNpadButton_L|HidNpadButton_ZL)) new_buttons |= HidNpadButton_L; // sdknso would mask out this button on the else condition for both of these, but it was already clear anyway.
        if (buttons & (HidNpadButton_R|HidNpadButton_ZR)) new_buttons |= HidNpadButton_R;
        buttons = new_buttons | (buttons & (HidNpadButton_A|HidNpadButton_B|HidNpadButton_X|HidNpadButton_Y));

        // sdknso would handle button-bitmasking with ControlPadRestriction here.

        states[i].buttons = buttons;

        memset(&states[i].analog_stick_l, 0, sizeof(states[i].analog_stick_l));
        memset(&states[i].analog_stick_r, 0, sizeof(states[i].analog_stick_r));
    }

    return total;
}

size_t hidGetSixAxisSensorStates(HidSixAxisSensorHandle handle, HidSixAxisSensorState *states, size_t count) {
    HidNpadSixAxisSensorLifo *lifo = NULL;
    HidNpadInternalState *npad = _hidGetNpadInternalState(handle.player_number);
    switch(_hidGetSixAxisSensorHandleNpadStyleIndex(handle)) {
        default:
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));

        case 0: // NpadFullKey/NpadPalma
        case 5: // NpadGc (not actually returned by GetHandles, NpadFullKey is used instead)
            lifo = &npad->full_key_six_axis_sensor_lifo;
        break;

        case 1: // NpadHandheld/NpadHandheldLark
            lifo = &npad->handheld_six_axis_sensor_lifo;
        break;

        case 2: // NpadJoyDual
            if (handle.device_idx==0) lifo = &npad->joy_dual_left_six_axis_sensor_lifo;
            else if (handle.device_idx==1) lifo = &npad->joy_dual_right_six_axis_sensor_lifo;
            else diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
        break;

        case 3: // NpadJoyLeft
            lifo = &npad->joy_left_six_axis_sensor_lifo;
        break;

        case 4: // NpadJoyRight
            lifo = &npad->joy_right_six_axis_sensor_lifo;
        break;

        case 29: // System(Ext) (not actually returned by GetHandles)
        case 30:
        return 0;
    }

    size_t total = _hidGetStates(&lifo->header, lifo->storage, 17, offsetof(HidSixAxisSensorStateAtomicStorage,state), offsetof(HidSixAxisSensorState,sampling_number), states, sizeof(HidSixAxisSensorState), count);
    return total;
}

void hidInitializeGesture(void) {
    Result rc = _hidActivateGesture();
    if (R_FAILED(rc)) diagAbortWithResult(rc);
}

size_t hidGetGestureStates(HidGestureState *states, size_t count) {
    HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr();
    if (sharedmem == NULL)
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    size_t total = _hidGetStates(&sharedmem->gesture.lifo.header, sharedmem->gesture.lifo.storage, 17, offsetof(HidGestureDummyStateAtomicStorage,state), offsetof(HidGestureState,sampling_number), states, sizeof(HidGestureState), count);
    return total;
}

static Result _hidCmdNoIO(Service* srv, u32 cmd_id) {
    return serviceDispatch(srv, cmd_id);
}

static Result _hidCmdGetHandle(Service* srv, Handle* handle_out, u32 cmd_id) {
    return serviceDispatch(srv, cmd_id,
        .out_handle_attrs = { SfOutHandleAttr_HipcCopy },
        .out_handles = handle_out,
    );
}

static Result _hidCmdGetSession(Service* srv_out, u32 cmd_id) {
    return serviceDispatch(&g_hidSrv, cmd_id,
        .out_num_objects = 1,
        .out_objects = srv_out,
    );
}

static Result _hidCmdInAruidNoOut(u32 cmd_id) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchIn(&g_hidSrv, cmd_id, AppletResourceUserId,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU8NoOut(u8 inval, u32 cmd_id) {
    return serviceDispatchIn(&g_hidSrv, cmd_id, inval);
}

static Result _hidCmdInBoolNoOut(bool inval, u32 cmd_id) {
    return _hidCmdInU8NoOut(inval!=0, cmd_id);
}

static Result _hidCmdInU32NoOut(Service* srv, u32 inval, u32 cmd_id) {
    return serviceDispatchIn(srv, cmd_id, inval);
}

static Result _hidCmdInU64NoOut(Service* srv, u64 inval, u32 cmd_id) {
    return serviceDispatchIn(srv, cmd_id, inval);
}

static Result _hidCmdInU16U64NoOut(Service* srv, u16 inval0, u64 inval1, u32 cmd_id) {
    const struct {
        u16 inval0;
        u8 pad[6];
        u64 inval1;
    } in = { inval0, {0}, inval1 };

    return serviceDispatchIn(srv, cmd_id, in);
}

static Result _hidCmdInU32U64NoOut(Service* srv, u32 inval0, u64 inval1, u32 cmd_id) {
    const struct {
        u32 inval0;
        u32 pad;
        u64 inval1;
    } in = { inval0, 0, inval1 };

    return serviceDispatchIn(srv, cmd_id, in);
}

static Result _hidCmdInU64U64NoOut(Service* srv, u64 inval0, u64 inval1, u32 cmd_id) {
    const struct {
        u64 inval0;
        u64 inval1;
    } in = { inval0, inval1 };

    return serviceDispatchIn(srv, cmd_id, in);
}

static Result _hidCmdInU8AruidNoOut(u8 inval, u32 cmd_id) {
    const struct {
        u8 inval;
        u8 pad[7];
        u64 AppletResourceUserId;
    } in = { inval, {0}, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInBoolAruidNoOut(bool flag, u32 cmd_id) {
    return _hidCmdInU8AruidNoOut(flag!=0, cmd_id);
}

static Result _hidCmdInU32AruidNoOut(u32 inval, u32 cmd_id) {
    const struct {
        u32 inval;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { inval, 0, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU64AruidNoOut(u64 inval, u32 cmd_id) {
    const struct {
        u64 AppletResourceUserId;
        u64 inval;
    } in = { appletGetAppletResourceUserId(), inval };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU32AruidU64NoOut(u32 in32, u64 in64, u32 cmd_id) {
    const struct {
        u32 in32;
        u32 pad;
        u64 AppletResourceUserId;
        u64 in64;
    } in = { in32, 0, appletGetAppletResourceUserId(), in64 };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU8U32AruidNoOut(u8 in8, u32 in32, u32 cmd_id) {
    const struct {
        u8 in8;
        u8 pad[3];
        u32 in32;
        u64 AppletResourceUserId;
    } in = { in8, {0}, in32, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInBoolU32AruidNoOut(bool flag, u32 in32, u32 cmd_id) {
    return _hidCmdInU8U32AruidNoOut(flag!=0, in32, cmd_id);
}

static Result _hidCmdInU32U32AruidNoOut(u32 val0, u32 val1, u32 cmd_id) {
    const struct {
        u32 val0, val1;
        u64 AppletResourceUserId;
    } in = { val0, val1, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, cmd_id, in,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU32AruidOutU8(u32 inval, u8 *out, u32 cmd_id) {
    const struct {
        u32 inval;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { inval, 0, appletGetAppletResourceUserId() };

    return serviceDispatchInOut(&g_hidSrv, cmd_id, in, *out,
        .in_send_pid = true,
    );
}

static Result _hidCmdInU32AruidOutBool(u32 inval, bool *out, u32 cmd_id) {
    u8 tmp=0;
    Result rc = _hidCmdInU32AruidOutU8(inval, &tmp, cmd_id);
    if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
    return rc;
}

static Result _hidCmdInU32AruidOutU64(u32 inval, u64 *out, u32 cmd_id) {
    const struct {
        u32 inval;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { inval, 0, appletGetAppletResourceUserId() };

    return serviceDispatchInOut(&g_hidSrv, cmd_id, in, *out,
        .in_send_pid = true,
    );
}

static Result _hidCmdInAruidOutU32(u32 *out, u32 cmd_id) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchInOut(&g_hidSrv, cmd_id, AppletResourceUserId, *out,
        .in_send_pid = true,
    );
}

static Result _hidCmdInAruidOutU64(u64 *out, u32 cmd_id) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchInOut(&g_hidSrv, cmd_id, AppletResourceUserId, *out,
        .in_send_pid = true,
    );
}

static Result _hidCmdNoInOutU8(u8 *out, u32 cmd_id) {
    return serviceDispatchOut(&g_hidSrv, cmd_id, *out);
}

static Result _hidCmdNoInOutBool(bool *out, u32 cmd_id) {
    u8 tmp=0;
    Result rc = _hidCmdNoInOutU8(&tmp, cmd_id);
    if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
    return rc;
}

static Result _hidCmdNoInOutU64(u64 *out, u32 cmd_id) {
    return serviceDispatchOut(&g_hidSrv, cmd_id, *out);
}

static Result _hidCreateAppletResource(Service* srv, Service* srv_out) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchIn(srv, 0, AppletResourceUserId,
        .in_send_pid = true,
        .out_num_objects = 1,
        .out_objects = srv_out,
    );
}

static Result _hidGetSharedMemoryHandle(Service* srv, Handle* handle_out) {
    return _hidCmdGetHandle(srv, handle_out, 0);
}

static Result _hidActivateTouchScreen(void) {
    return _hidCmdInAruidNoOut(11);
}

static Result _hidActivateMouse(void) {
    return _hidCmdInAruidNoOut(21);
}

static Result _hidActivateKeyboard(void) {
    return _hidCmdInAruidNoOut(31);
}

Result hidSendKeyboardLockKeyEvent(u32 events) {
    if (hosversionBefore(6,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU32AruidNoOut(events, 32);
}

Result hidGetSixAxisSensorHandles(HidSixAxisSensorHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style) {
    return _hidGetSixAxisSensorHandles(handles, total_handles, id, style);
}

Result hidStartSixAxisSensor(HidSixAxisSensorHandle handle) {
    return _hidCmdInU32AruidNoOut(handle.type_value, 66);
}

Result hidStopSixAxisSensor(HidSixAxisSensorHandle handle) {
    return _hidCmdInU32AruidNoOut(handle.type_value, 67);
}

Result hidIsSixAxisSensorFusionEnabled(HidSixAxisSensorHandle handle, bool *out) {
    return _hidCmdInU32AruidOutBool(handle.type_value, out, 68);
}

Result hidEnableSixAxisSensorFusion(HidSixAxisSensorHandle handle, bool flag) {
    return _hidCmdInBoolU32AruidNoOut(flag, handle.type_value, 69);
}

Result hidSetSixAxisSensorFusionParameters(HidSixAxisSensorHandle handle, float unk0, float unk1) {
    if (unk0 < 0.0f || unk0 > 1.0f)
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);

    const struct {
        HidSixAxisSensorHandle handle;
        float unk0;
        float unk1;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, unk0, unk1, 0, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, 70, in,
        .in_send_pid = true,
    );
}

Result hidGetSixAxisSensorFusionParameters(HidSixAxisSensorHandle handle, float *unk0, float *unk1) {
    Result rc;

    const struct {
        HidSixAxisSensorHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, 0, appletGetAppletResourceUserId() };

    struct {
        float unk0;
        float unk1;
    } out;

    rc = serviceDispatchInOut(&g_hidSrv, 71, in, out,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && unk0) *unk0 = out.unk0;
    if (R_SUCCEEDED(rc) && unk1) *unk1 = out.unk1;
    return rc;
}

Result hidResetSixAxisSensorFusionParameters(HidSixAxisSensorHandle handle) {
    return _hidCmdInU32AruidNoOut(handle.type_value, 72);
}

Result hidSetGyroscopeZeroDriftMode(HidSixAxisSensorHandle handle, HidGyroscopeZeroDriftMode mode) {
    const struct {
        HidSixAxisSensorHandle handle;
        u32 mode;
        u64 AppletResourceUserId;
    } in = { handle, mode, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, 79, in,
        .in_send_pid = true,
    );
}

Result hidGetGyroscopeZeroDriftMode(HidSixAxisSensorHandle handle, HidGyroscopeZeroDriftMode *mode) {
    Result rc;

    const struct {
        HidSixAxisSensorHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, 0, appletGetAppletResourceUserId() };

    u32 tmp=0;
    rc = serviceDispatchInOut(&g_hidSrv, 80, in, tmp,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && mode) *mode = tmp;
    return rc;
}

Result hidResetGyroscopeZeroDriftMode(HidSixAxisSensorHandle handle) {
    return _hidCmdInU32AruidNoOut(handle.type_value, 81);
}

Result hidIsSixAxisSensorAtRest(HidSixAxisSensorHandle handle, bool *out) {
    return _hidCmdInU32AruidOutBool(handle.type_value, out, 82);
}

Result hidIsFirmwareUpdateAvailableForSixAxisSensor(HidSixAxisSensorHandle handle, bool *out) {
    if (hosversionBefore(6,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU32AruidOutBool(handle.type_value, out, 83);
}

static Result _hidActivateGesture(void) {
    u32 val=1;

    return _hidCmdInU32AruidNoOut(val, 91); // ActivateGesture
}

Result hidSetSupportedNpadStyleSet(u32 style_set) {
    return _hidCmdInU32AruidNoOut(style_set, 100);
}

Result hidGetSupportedNpadStyleSet(u32 *style_set) {
    u32 tmp=0;
    Result rc = _hidCmdInAruidOutU32(&tmp, 101);
    if (R_SUCCEEDED(rc) && style_set) *style_set = tmp;
    return rc;
}

Result hidSetSupportedNpadIdType(const HidNpadIdType *ids, size_t count) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchIn(&g_hidSrv, 102, AppletResourceUserId,
        .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
        .buffers = { { ids, count*sizeof(HidNpadIdType) } },
        .in_send_pid = true,
    );
}

static Result _hidActivateNpad(void) {
    u32 revision=0x0;

    if (hosversionBefore(5,0,0))
        return _hidCmdInAruidNoOut(103); // ActivateNpad

    revision = 0x1; // [5.0.0+]
    if (hosversionAtLeast(6,0,0))
        revision = 0x2; // [6.0.0+]
    if (hosversionAtLeast(8,0,0))
        revision = 0x3; // [8.0.0+]

    return _hidCmdInU32AruidNoOut(revision, 109); // ActivateNpadWithRevision
}

Result hidAcquireNpadStyleSetUpdateEventHandle(HidNpadIdType id, Event* out_event, bool autoclear) {
    Result rc;
    Handle tmp_handle = INVALID_HANDLE;

    const struct {
        u32 id;
        u32 pad;
        u64 AppletResourceUserId;
        u64 event_ptr; // Official sw sets this to a ptr, which the sysmodule doesn't seem to use.
    } in = { id, 0, appletGetAppletResourceUserId(), 0 };

    rc = serviceDispatchIn(&g_hidSrv, 106, in,
        .in_send_pid = true,
        .out_handle_attrs = { SfOutHandleAttr_HipcCopy },
        .out_handles = &tmp_handle,
    );
    if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear);
    return rc;
}

Result hidDisconnectNpad(HidNpadIdType id) {
    return _hidCmdInU32AruidNoOut(id, 107);
}

Result hidGetPlayerLedPattern(HidNpadIdType id, u8 *out) {
    u32 in=id;
    u64 tmp=0;
    Result rc = serviceDispatchIn(&g_hidSrv, 108, in, tmp);
    if (R_SUCCEEDED(rc) && out) *out = tmp;
    return rc;
}

Result hidSetNpadJoyHoldType(HidNpadJoyHoldType type) {
    return _hidCmdInU64AruidNoOut(type, 120);
}

Result hidGetNpadJoyHoldType(HidNpadJoyHoldType *type) {
    u64 tmp=0;
    Result rc = _hidCmdInAruidOutU64(&tmp, 121);
    if (R_SUCCEEDED(rc) && type) *type = tmp;
    return rc;
}

Result hidSetNpadJoyAssignmentModeSingleByDefault(HidNpadIdType id) {
    return _hidCmdInU32AruidNoOut(id, 122);
}

Result hidSetNpadJoyAssignmentModeSingle(HidNpadIdType id, HidNpadJoyDeviceType type) {
    return _hidCmdInU32AruidU64NoOut(id, type, 123);
}

Result hidSetNpadJoyAssignmentModeDual(HidNpadIdType id) {
    return _hidCmdInU32AruidNoOut(id, 124);
}

Result hidMergeSingleJoyAsDualJoy(HidNpadIdType id0, HidNpadIdType id1) {
    return _hidCmdInU32U32AruidNoOut(id0, id1, 125);
}

Result hidStartLrAssignmentMode(void) {
    return _hidCmdInAruidNoOut(126);
}

Result hidStopLrAssignmentMode(void) {
    return _hidCmdInAruidNoOut(127);
}

Result hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode mode) {
    return _hidCmdInU64AruidNoOut(mode, 128);
}

Result hidGetNpadHandheldActivationMode(HidNpadHandheldActivationMode *out) {
    u64 tmp=0;
    Result rc = _hidCmdInAruidOutU64(&tmp, 129);
    if (R_SUCCEEDED(rc) && out) *out = tmp; // Official sw would abort if tmp isn't 0-2.
    return rc;
}

Result hidSwapNpadAssignment(HidNpadIdType id0, HidNpadIdType id1) {
    return _hidCmdInU32U32AruidNoOut(id0, id1, 130);
}

Result hidEnableUnintendedHomeButtonInputProtection(HidNpadIdType id, bool flag) {
    return _hidCmdInBoolU32AruidNoOut(flag, id, 132);
}

Result hidSetNpadJoyAssignmentModeSingleWithDestination(HidNpadIdType id, HidNpadJoyDeviceType type, bool *flag, HidNpadIdType *dest) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        u32 id;
        u32 pad;
        u64 AppletResourceUserId;
        s64 type;
    } in = { id, 0, appletGetAppletResourceUserId(), type };

    struct {
        u8 flag;
        u8 pad[3];
        u32 id;
    } out;

    Result rc = serviceDispatchInOut(&g_hidSrv, 133, in, out,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc)) {
        if (flag) *flag = out.flag & 1;
        if (dest) *dest = out.id;
    }
    return rc;
}

Result hidSetNpadAnalogStickUseCenterClamp(bool flag) {
    if (hosversionBefore(6,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInBoolAruidNoOut(flag, 134);
}

Result hidSetNpadCaptureButtonAssignment(HidNpadStyleTag style, u64 buttons) {
    if (hosversionBefore(8,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU32AruidU64NoOut(style, buttons, 135);
}

Result hidClearNpadCaptureButtonAssignment(void) {
    if (hosversionBefore(8,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInAruidNoOut(136);
}

Result hidInitializeVibrationDevices(HidVibrationDeviceHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style) {
    Result rc=0;
    s32 i;

    rc = _hidGetVibrationDeviceHandles(handles, total_handles, id, style);
    if (R_FAILED(rc)) return rc;

    mutexLock(&g_hidVibrationMutex);
    if (!serviceIsActive(&g_hidIActiveVibrationDeviceList)) {
        rc = _hidCreateActiveVibrationDeviceList(&g_hidIActiveVibrationDeviceList);
    }
    mutexUnlock(&g_hidVibrationMutex);
    if (R_FAILED(rc)) return rc;

    for (i=0; i<total_handles; i++) {
        rc = _hidActivateVibrationDevice(&g_hidIActiveVibrationDeviceList, handles[i]);

        if (R_FAILED(rc))
            break;
    }

    return rc;
}

Result hidGetVibrationDeviceInfo(HidVibrationDeviceHandle handle, HidVibrationDeviceInfo *out) {
    return serviceDispatchInOut(&g_hidSrv, 200, handle, *out);
}

Result hidSendVibrationValue(HidVibrationDeviceHandle handle, const HidVibrationValue *value) {
    const struct {
        HidVibrationDeviceHandle handle;
        HidVibrationValue value;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, *value, 0, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, 201, in,
        .in_send_pid = true,
    );
}

Result hidGetActualVibrationValue(HidVibrationDeviceHandle handle, HidVibrationValue *out) {
    const struct {
        HidVibrationDeviceHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, 0, appletGetAppletResourceUserId() };

    return serviceDispatchInOut(&g_hidSrv, 202, in, *out,
        .in_send_pid = true,
    );
}

static Result _hidCreateActiveVibrationDeviceList(Service* srv_out) {
    return _hidCmdGetSession(srv_out, 203);
}

static Result _hidActivateVibrationDevice(Service* srv, HidVibrationDeviceHandle handle) {
    return _hidCmdInU32NoOut(srv, handle.type_value, 0);
}

Result hidPermitVibration(bool flag) {
    return _hidCmdInBoolNoOut(flag, 204);
}

Result hidIsVibrationPermitted(bool *flag) {
    return _hidCmdNoInOutBool(flag, 205);
}

Result hidSendVibrationValues(const HidVibrationDeviceHandle *handles, const HidVibrationValue *values, s32 count) {
    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchIn(&g_hidSrv, 206, AppletResourceUserId,
        .buffer_attrs = {
            SfBufferAttr_HipcPointer | SfBufferAttr_In,
            SfBufferAttr_HipcPointer | SfBufferAttr_In,
        },
        .buffers = {
            { handles, count*sizeof(HidVibrationDeviceHandle) },
            { values, count*sizeof(HidVibrationValue) },
        },
    );
}

Result hidSendVibrationGcErmCommand(HidVibrationDeviceHandle handle, HidVibrationGcErmCommand cmd) {
    if (hosversionBefore(4,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidVibrationDeviceHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
        u64 cmd;
    } in = { handle, 0, appletGetAppletResourceUserId(), cmd };

    return serviceDispatchIn(&g_hidSrv, 207, in,
        .in_send_pid = true,
    );
}

Result hidGetActualVibrationGcErmCommand(HidVibrationDeviceHandle handle, HidVibrationGcErmCommand *out) {
    if (hosversionBefore(4,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidVibrationDeviceHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, 0, appletGetAppletResourceUserId() };

    u64 tmp=0;
    Result rc = serviceDispatchInOut(&g_hidSrv, 208, in, tmp,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && out) *out = tmp;
    return rc;
}

Result hidBeginPermitVibrationSession(void) {
    if (hosversionBefore(4,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, appletGetAppletResourceUserId(), 209);
}

Result hidEndPermitVibrationSession(void) {
    if (hosversionBefore(4,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdNoIO(&g_hidSrv, 210);
}

Result hidIsVibrationDeviceMounted(HidVibrationDeviceHandle handle, bool *flag) {
    if (hosversionBefore(7,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    Result rc;

    const struct {
        HidVibrationDeviceHandle handle;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { handle, 0, appletGetAppletResourceUserId() };

    u8 tmp=0;
    rc = serviceDispatchInOut(&g_hidSrv, 211, in, tmp,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && flag) *flag = tmp & 1;
    return rc;
}

static HidVibrationDeviceHandle _hidMakeVibrationDeviceHandle(u8 npad_style_index, u8 player_number, u8 device_idx) {
    return (HidVibrationDeviceHandle){.npad_style_index = npad_style_index, .player_number = player_number, .device_idx = device_idx, .pad = 0};
}

static HidSixAxisSensorHandle _hidMakeSixAxisSensorHandle(u8 npad_style_index, u8 player_number, u8 device_idx) {
    return (HidSixAxisSensorHandle){.npad_style_index = npad_style_index, .player_number = player_number, .device_idx = device_idx, .pad = 0};
}

static u8 _hidGetSixAxisSensorHandleNpadStyleIndex(HidSixAxisSensorHandle handle) {
    return handle.npad_style_index - 3;
}

static Result _hidGetVibrationDeviceHandles(HidVibrationDeviceHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style) {
    Result rc=0;
    s32 max_handles=1;
    u32 style_index=0;
    u8 device_idx=0;

    if (total_handles <= 0 || total_handles > 2)
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);

    if (style & HidNpadStyleTag_NpadFullKey) {
        style_index = 3;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadHandheld) {
        style_index = 4;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadJoyDual) {
        style_index = 5;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadJoyLeft) {
        style_index = 6;
    }
    else if (style & HidNpadStyleTag_NpadJoyRight) {
        style_index = 7;
        device_idx = 0x1;
    }
    else if (style & HidNpadStyleTag_NpadGc) {
        style_index = 8;
    }
    else if (style & HidNpadStyleTag_Npad10) {
        style_index = 0xd;
    }
    else if (style & (HidNpadStyleTag_NpadLark | HidNpadStyleTag_NpadLucia)) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else if (style & HidNpadStyleTag_NpadHandheldLark) {
        style_index = 4;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadSystem) {
        style_index = 0x21;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadSystemExt) {
        style_index = 0x20;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadPalma) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);
    }

    handles[0] = _hidMakeVibrationDeviceHandle(style_index, id, device_idx);

    if (total_handles > 1) {
        if (max_handles > 1) {
            handles[1] = _hidMakeVibrationDeviceHandle(style_index, id, 0x1);
        }
        else {
            return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would just return 0 here.
        }
    }

    return rc;
}

static Result _hidGetSixAxisSensorHandles(HidSixAxisSensorHandle *handles, s32 total_handles, HidNpadIdType id, HidNpadStyleTag style) {
    Result rc=0;
    s32 max_handles=1;
    u32 style_index=0;
    u8 device_idx=0;

    if (total_handles <= 0 || total_handles > 2)
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);

    if (style & HidNpadStyleTag_NpadFullKey) {
        style_index = 3;
        device_idx = 0x2;
    }
    else if (style & HidNpadStyleTag_NpadHandheld) {
        style_index = 4;
        device_idx = 0x2;
    }
    else if (style & HidNpadStyleTag_NpadJoyDual) {
        style_index = 5;
        max_handles = 2;
    }
    else if (style & HidNpadStyleTag_NpadJoyLeft) {
        style_index = 6;
    }
    else if (style & HidNpadStyleTag_NpadJoyRight) {
        style_index = 7;
        device_idx = 0x1;
    }
    else if (style & HidNpadStyleTag_NpadGc) {
        style_index = 3;
        device_idx = 0x2;
    }
    else if (style & HidNpadStyleTag_Npad10) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else if (style & (HidNpadStyleTag_NpadLark | HidNpadStyleTag_NpadLucia)) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else if (style & HidNpadStyleTag_NpadHandheldLark) {
        style_index = 4;
        device_idx = 0x2;
    }
    else if (style & HidNpadStyleTag_NpadSystem) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else if (style & HidNpadStyleTag_NpadSystemExt) {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }
    else if (style & HidNpadStyleTag_NpadPalma) {
        style_index = 3;
        device_idx = 0x2;
    }
    else {
        return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would return 0, and return no handles.
    }

    handles[0] = _hidMakeSixAxisSensorHandle(style_index, id, device_idx);

    if (total_handles > 1) {
        if (max_handles > 1) {
            handles[1] = _hidMakeSixAxisSensorHandle(style_index, id, 0x1);
        }
        else {
            return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would just return 0 here.
        }
    }

    return rc;
}

static Result _hidActivateConsoleSixAxisSensor(void) {
    if (hosversionBefore(3,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInAruidNoOut(300);
}

Result hidStartSevenSixAxisSensor(void) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInAruidNoOut(304);
}

Result hidStopSevenSixAxisSensor(void) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInAruidNoOut(305);
}

static Result _hidInitializeSevenSixAxisSensor(TransferMemory *tmem0, TransferMemory *tmem1) {
    const struct {
        u64 AppletResourceUserId;
        u64 size0;
        u64 size1;
    } in = { appletGetAppletResourceUserId(), tmem0->size, tmem1->size };

    return serviceDispatchIn(&g_hidSrv, 306, in,
        .in_send_pid = true,
        .in_num_handles = 2,
        .in_handles = { tmem0->handle, tmem1->handle },
    );
}

Result hidInitializeSevenSixAxisSensor(void) {
    Result rc=0;
    size_t bufsize = 0x80000;

    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
    if (g_sevenSixAxisSensorBuffer != NULL)
        return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);

    if (hosversionBefore(10,0,0)) { // No longer used by sdknso on [10.0.0+].
        rc = _hidActivateConsoleSixAxisSensor();
        if (R_FAILED(rc)) return rc;
    }

    g_sevenSixAxisSensorBuffer = (u8*)__libnx_aligned_alloc(0x1000, bufsize);
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
    memset(g_sevenSixAxisSensorBuffer, 0, bufsize);

    rc = tmemCreateFromMemory(&g_sevenSixAxisSensorTmem0, g_sevenSixAxisSensorBuffer, 0x1000, Perm_R);
    if (R_SUCCEEDED(rc)) rc = tmemCreateFromMemory(&g_sevenSixAxisSensorTmem1, &g_sevenSixAxisSensorBuffer[0x1000], bufsize-0x1000, Perm_None);

    if (R_SUCCEEDED(rc)) rc = _hidInitializeSevenSixAxisSensor(&g_sevenSixAxisSensorTmem0, &g_sevenSixAxisSensorTmem1);

    if (R_FAILED(rc)) {
        tmemClose(&g_sevenSixAxisSensorTmem0);
        tmemClose(&g_sevenSixAxisSensorTmem1);

        __libnx_free(g_sevenSixAxisSensorBuffer);
        g_sevenSixAxisSensorBuffer = NULL;
    }

    return rc;
}

Result hidFinalizeSevenSixAxisSensor(void) {
    Result rc=0;

    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);

    rc = _hidCmdInAruidNoOut(307);

    tmemClose(&g_sevenSixAxisSensorTmem0);
    tmemClose(&g_sevenSixAxisSensorTmem1);

    __libnx_free(g_sevenSixAxisSensorBuffer);
    g_sevenSixAxisSensorBuffer = NULL;

    return rc;
}

Result hidSetSevenSixAxisSensorFusionStrength(float strength) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        float strength;
        u64 AppletResourceUserId;
    } in = { strength, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, 308, in,
        .in_send_pid = true,
    );
}

Result hidGetSevenSixAxisSensorFusionStrength(float *strength) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchInOut(&g_hidSrv, 309, AppletResourceUserId, *strength,
        .in_send_pid = true,
    );
}

Result hidResetSevenSixAxisSensorTimestamp(void) {
    if (hosversionBefore(6,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInAruidNoOut(310);
}

Result hidGetSevenSixAxisSensorStates(HidSevenSixAxisSensorState *states, size_t count, size_t *total_out) {
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);

    if (states == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);

    HidSevenSixAxisSensorStates *states_buf = (HidSevenSixAxisSensorStates*)g_sevenSixAxisSensorBuffer;

    size_t total = _hidGetStates(&states_buf->header, states_buf->storage, 0x21, offsetof(HidSevenSixAxisSensorStateEntry,state), offsetof(HidSevenSixAxisSensorState,sampling_number), states, sizeof(HidSevenSixAxisSensorState), count);
    if (total_out) *total_out = total;

    return 0;
}

Result hidIsSevenSixAxisSensorAtRest(bool *out) {
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);

    HidSharedMemory *shared_mem = (HidSharedMemory*)hidGetSharedmemAddr();
    *out = shared_mem->console_six_axis_sensor.is_seven_six_axis_sensor_at_rest & 1;

    return 0;
}

Result hidGetSensorFusionError(float *out) {
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);

    HidSharedMemory *shared_mem = (HidSharedMemory*)hidGetSharedmemAddr();
    *out = shared_mem->console_six_axis_sensor.verticalization_error;

    return 0;
}

Result hidGetGyroBias(UtilFloat3 *out) {
    if (g_sevenSixAxisSensorBuffer == NULL)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);

    HidSharedMemory *shared_mem = (HidSharedMemory*)hidGetSharedmemAddr();
    *out = shared_mem->console_six_axis_sensor.gyro_bias;

    return 0;
}

Result hidIsUsbFullKeyControllerEnabled(bool *out) {
    if (hosversionBefore(3,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdNoInOutBool(out, 400);
}

Result hidEnableUsbFullKeyController(bool flag) {
    if (hosversionBefore(3,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInBoolNoOut(flag, 401);
}

Result hidIsUsbFullKeyControllerConnected(HidNpadIdType id, bool *out) {
    if (hosversionBefore(3,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u8 tmp=0;
    Result rc = serviceDispatchInOut(&g_hidSrv, 402, id, tmp);
    if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
    return rc;
}

Result hidGetNpadInterfaceType(HidNpadIdType id, u8 *out) {
    if (hosversionBefore(4,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u32 tmp = id;
    return serviceDispatchInOut(&g_hidSrv, 405, tmp, *out);
}

Result hidGetNpadOfHighestBatteryLevel(const HidNpadIdType *ids, size_t count, HidNpadIdType *out) {
    if (hosversionBefore(10,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    u32 tmp=0;
    Result rc = serviceDispatchInOut(&g_hidSrv, 407, AppletResourceUserId, tmp,
        .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
        .buffers = { { ids, count*sizeof(HidNpadIdType) } },
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && out) *out = tmp;
    return rc;
}

Result hidGetPalmaConnectionHandle(HidNpadIdType id, HidPalmaConnectionHandle *out) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u64 tmp=0;
    Result rc = _hidCmdInU32AruidOutU64(id, &tmp, 500);
    if (R_SUCCEEDED(rc) && out) out->handle = tmp;
    return rc;
}

Result hidInitializePalma(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 501);
}

Result hidAcquirePalmaOperationCompleteEvent(HidPalmaConnectionHandle handle, Event* out_event, bool autoclear) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    Result rc;
    Handle tmp_handle = INVALID_HANDLE;

    rc = serviceDispatchIn(&g_hidSrv, 502, handle,
        .out_handle_attrs = { SfOutHandleAttr_HipcCopy },
        .out_handles = &tmp_handle,
    );
    if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear);
    return rc;
}

static Result _hidGetPalmaOperationInfo(HidPalmaConnectionHandle handle, void* buffer, size_t size, u64 *out) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return serviceDispatchInOut(&g_hidSrv, 503, handle, *out,
        .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
        .buffers = { { buffer, size } },
    );
}

Result hidGetPalmaOperationInfo(HidPalmaConnectionHandle handle, HidPalmaOperationInfo *out) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    Result rc=0;
    Result rc2=0;
    u64 tmp=0;
    bool old_ver = hosversionBefore(5,1,0);

    rc = _hidGetPalmaOperationInfo(handle, out->data, sizeof(out->data), &tmp);
    if (R_SUCCEEDED(rc) || old_ver) {
        if (old_ver) rc2 = rc;
        else rc2 = _hidGetPalmaOperationResult(handle);
        out->res = rc2;
        if (tmp > (old_ver ? HidPalmaOperationType_SuspendFeature : HidPalmaOperationType_ResetPlayLog)) rc = MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); // sdknso would Abort here.
        else out->type = tmp;
    }

    return rc;
}

Result hidPlayPalmaActivity(HidPalmaConnectionHandle handle, u16 val) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64U64NoOut(&g_hidSrv, handle.handle, val, 504);
}

Result hidSetPalmaFrModeType(HidPalmaConnectionHandle handle, HidPalmaFrModeType type) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64U64NoOut(&g_hidSrv, handle.handle, type, 505);
}

Result hidReadPalmaStep(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 506);
}

Result hidEnablePalmaStep(HidPalmaConnectionHandle handle, bool flag) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        u8 flag;
        u8 pad[7];
        HidPalmaConnectionHandle handle;
    } in = { flag!=0, {0}, handle };

    return serviceDispatchIn(&g_hidSrv, 507, in);
}

Result hidResetPalmaStep(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 508);
}

Result hidReadPalmaApplicationSection(HidPalmaConnectionHandle handle, s32 inval0, u64 size) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    if (size > sizeof(HidPalmaApplicationSectionAccessBuffer))
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);

    const struct {
        HidPalmaConnectionHandle handle;
        u64 inval0;
        u64 size;
    } in = { handle, inval0, size };

    return serviceDispatchIn(&g_hidSrv, 509, in);
}

Result hidWritePalmaApplicationSection(HidPalmaConnectionHandle handle, s32 inval0, u64 size, const HidPalmaApplicationSectionAccessBuffer *buf) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidPalmaConnectionHandle handle;
        u64 inval0;
        u64 size;
    } in = { handle, inval0, size };

    return serviceDispatchIn(&g_hidSrv, 510, in,
        .buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_In },
        .buffers = { { buf, sizeof(*buf) } },
    );
}

Result hidReadPalmaUniqueCode(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 511);
}

Result hidSetPalmaUniqueCodeInvalid(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 512);
}

Result hidWritePalmaActivityEntry(HidPalmaConnectionHandle handle, u16 unk, const HidPalmaActivityEntry *entry) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidPalmaConnectionHandle handle;
        u64 unk;
        u64 rgb_led_pattern_index;
        u64 wave_set;
        u64 wave_index;
    } in = { handle, unk, entry->rgb_led_pattern_index, entry->wave_set, entry->wave_index };

    return serviceDispatchIn(&g_hidSrv, 513, in);
}

Result hidWritePalmaRgbLedPatternEntry(HidPalmaConnectionHandle handle, u16 unk, const void* buffer, size_t size) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidPalmaConnectionHandle handle;
        u64 unk;
    } in = { handle, unk };

    return serviceDispatchIn(&g_hidSrv, 514, in,
        .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
        .buffers = { { buffer, size } },
    );
}

static Result _hidWritePalmaWaveEntry(HidPalmaConnectionHandle handle, HidPalmaWaveSet wave_set, u16 unk, TransferMemory *tmem, size_t size) {
    const struct {
        HidPalmaConnectionHandle handle;
        u64 wave_set;
        u64 unk;
        u64 tmem_size;
        u64 size;
    } in = { handle, wave_set, unk, tmem->size, size };

    return serviceDispatchIn(&g_hidSrv, 515, in,
        .in_num_handles = 1,
        .in_handles = { tmem->handle },
    );
}

Result hidWritePalmaWaveEntry(HidPalmaConnectionHandle handle, HidPalmaWaveSet wave_set, u16 unk, const void* buffer, size_t tmem_size, size_t size) {
    Result rc=0;
    TransferMemory tmem={0};

    rc = tmemCreateFromMemory(&tmem, (void*)buffer, tmem_size, Perm_R);
    if (R_SUCCEEDED(rc)) rc = _hidWritePalmaWaveEntry(handle, wave_set, unk, &tmem, size);
    tmemClose(&tmem);

    return rc;
}

Result hidSetPalmaDataBaseIdentificationVersion(HidPalmaConnectionHandle handle, s32 version) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU32U64NoOut(&g_hidSrv, version, handle.handle, 516);
}

Result hidGetPalmaDataBaseIdentificationVersion(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 517);
}

Result hidSuspendPalmaFeature(HidPalmaConnectionHandle handle, u32 features) {
    if (hosversionBefore(5,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU32U64NoOut(&g_hidSrv, features, handle.handle, 518);
}

static Result _hidGetPalmaOperationResult(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 519);
}

Result hidReadPalmaPlayLog(HidPalmaConnectionHandle handle, u16 unk) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU16U64NoOut(&g_hidSrv, unk, handle.handle, 520);
}

Result hidResetPalmaPlayLog(HidPalmaConnectionHandle handle, u16 unk) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU16U64NoOut(&g_hidSrv, unk, handle.handle, 521);
}

Result hidSetIsPalmaAllConnectable(bool flag) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInBoolAruidNoOut(flag, 522);
}

Result hidSetIsPalmaPairedConnectable(bool flag) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInBoolAruidNoOut(flag, 523);
}

Result hidPairPalma(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 524);
}

static Result _hidSetPalmaBoostMode(bool flag) {
    if (hosversionBefore(5,1,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInBoolNoOut(flag, 525);
}

Result hidCancelWritePalmaWaveEntry(HidPalmaConnectionHandle handle) {
    if (hosversionBefore(7,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return _hidCmdInU64NoOut(&g_hidSrv, handle.handle, 526);
}

Result hidEnablePalmaBoostMode(bool flag) {
    if (hosversionBefore(8,0,0))
        return _hidSetPalmaBoostMode(flag);

    return _hidCmdInBoolAruidNoOut(flag, 527);
}

Result hidGetPalmaBluetoothAddress(HidPalmaConnectionHandle handle, BtdrvAddress *out) {
    if (hosversionBefore(8,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    return serviceDispatchInOut(&g_hidSrv, 528, handle, *out);
}

Result hidSetDisallowedPalmaConnection(const BtdrvAddress *addrs, s32 count) {
    if (hosversionBefore(8,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    u64 AppletResourceUserId = appletGetAppletResourceUserId();

    return serviceDispatchIn(&g_hidSrv, 529, AppletResourceUserId,
        .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
        .buffers = { { addrs, count*sizeof(BtdrvAddress) } },
        .in_send_pid = true,
    );
}

Result hidSetNpadCommunicationMode(HidNpadCommunicationMode mode) {
    return _hidCmdInU64AruidNoOut(mode, 1000);
}

Result hidGetNpadCommunicationMode(HidNpadCommunicationMode *out) {
    u64 tmp=0;
    Result rc = _hidCmdNoInOutU64(&tmp, 1001);
    if (R_SUCCEEDED(rc) && out) *out = tmp; // sdknso would Abort when tmp is not 0-3.
    return rc;
}

Result hidSetTouchScreenConfiguration(const HidTouchScreenConfigurationForNx *config) {
    if (hosversionBefore(9,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    const struct {
        HidTouchScreenConfigurationForNx config;
        u64 AppletResourceUserId;
    } in = { *config, appletGetAppletResourceUserId() };

    return serviceDispatchIn(&g_hidSrv, 1002, in,
        .in_send_pid = true,
    );
}

Result hidIsFirmwareUpdateNeededForNotification(bool *out) {
    if (hosversionBefore(9,0,0))
        return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

    s32 val=1;

    const struct {
        s32 val;
        u32 pad;
        u64 AppletResourceUserId;
    } in = { val, 0, appletGetAppletResourceUserId() };

    u8 tmp=0;
    Result rc = serviceDispatchInOut(&g_hidSrv, 1003, in, tmp,
        .in_send_pid = true,
    );
    if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
    return rc;
}