#include <revolution/wpad.h>
#include <cstring>

#include "__wpad.h"

extern void DEBUGPrint(const char*, ...);

void __a1_20_status_report(u8 chan, u8* data);
void __a1_21_user_data(u8 chan, u8* data);
void __a1_22_ack(u8 chan, u8* data);
void __a1_30_data_type(u8 chan, u8* data);
void __a1_31_data_type(u8 chan, u8* data);
void __a1_32_data_type(u8 chan, u8* data);
void __a1_33_data_type(u8 chan, u8* data);
void __a1_34_data_type(u8 chan, u8* data);
void __a1_35_data_type(u8 chan, u8* data);
void __a1_36_data_type(u8 chan, u8* data);
void __a1_37_data_type(u8 chan, u8* data);
void __a1_3d_data_type(u8 chan, u8* data);
void __a1_3e_data_type(u8 chan, u8* data);
void __a1_3f_data_type(u8 chan, u8* data);
void __a1_unused_report(u8 chan, u8* data);

#define HID_WPAD_BUTTON_MASK (u16)(0x9F1F)
#define HID_WPAD_BUTTON_CLEAR (u16)(0x60E0)
#define HID_NEAREMPTY_MASK (u8)(0x80)
#define HID_DPD_MASK (u8)(0x8)
#define HID_SPEAKER_MASK (u8)(0x4)
#define HID_ATTACH_MASK (u8)(0x2)
#define HID_LOWBAT_MASK (u8)(0x1)
#define HID_LED_MASK (u8)(0xF0)
#define HID_FW_MASK (u8)(0xF0)
#define HID_PRT_MASK (u8)(0xF)

#define HID_IDX_BYTE7 1
#define HID_IDX_BYTE8 2
#define HID_IDX_BYTE9 3
#define HID_IDX_BYTE10 4
#define HID_IDX_BYTE11 5
#define HID_IDX_BYTE12 6
#define HID_IDX_BYTE13 7
#define HID_IDX_BYTE14 8
#define HID_IDX_BYTE15 9
#define HID_IDX_BYTE16 10
#define HID_IDX_BYTE17 11
#define HID_IDX_BYTE18 12
#define HID_IDX_BYTE19 13
#define HID_IDX_BYTE20 14
#define HID_IDX_BYTE21 15
#define HID_IDX_BYTE22 16
#define HID_IDX_BYTE23 17
#define HID_IDX_BYTE24 18
#define HID_IDX_BYTE25 19
#define HID_IDX_BYTE26 20
#define HID_IDX_BYTE27 21

void (*__a1_input_reports_array[])(u8 chan, u8* data) = {
    __a1_20_status_report,  __a1_21_user_data,  __a1_22_ack,        __a1_unused_report,
    __a1_unused_report,     __a1_unused_report, __a1_unused_report, __a1_unused_report,
    __a1_unused_report,     __a1_unused_report, __a1_unused_report, __a1_unused_report,
    __a1_unused_report,     __a1_unused_report, __a1_unused_report, __a1_unused_report,
    __a1_30_data_type,      __a1_31_data_type,  __a1_32_data_type,  __a1_33_data_type,
    __a1_34_data_type,      __a1_35_data_type,  __a1_36_data_type,  __a1_37_data_type,
    __a1_unused_report,     __a1_unused_report, __a1_unused_report, __a1_unused_report,
    __a1_unused_report,     __a1_3d_data_type,  __a1_3e_data_type,  __a1_3f_data_type
};

static u8 checkInvalidData[21] = {
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff,
};
static u8 checkBuffer[21];

WPADExtensionCallback initExtension(s32 chan, WPADControlBlock*);

void abortInitExtension(s32 chan, s32 result) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    u32 type;

    if (result != WPAD_ERR_OK) {
        WPADiClearQueue(&p_wpd->extCmdQueue);

        if (result == WPAD_ERR_NO_CONTROLLER) {
            type = WPAD_DEV_NONE;
        } else if (p_wpd->info.attach) {
            type = WPAD_DEV_NOT_SUPPORTED;
        } else {
            type = WPAD_DEV_CORE;
        }

        p_wpd->devType = (u8)type;
        if (p_wpd->extensionCallback) {
            p_wpd->extensionCallback(chan, (s32)type);
        }
    }
}

#define PI 3.141592f

// Copied from Petari - this is likely close to the correct implementation,
// but loop unrolling is not happening so it ends up completely mismatching.
// Not sure if it's supposed to be manually unrolled in code? But then the
// bit-twiddling instructions end up mismatching.
static inline void __parse_dpd_data(s32 chan, WPADStatus** p_status, u8 fmt, u8* p_data, u8 len) {
    u8 i;
    u8 x;
    u8 y;
    u8 xy;

    if (fmt == WPAD_DPD_STANDARD) {
        for (i = 0; i < WPAD_DPD_MAX_OBJECTS; ++i) {
            if (i * 3 + 2 < len) {
                x = *(p_data + i * 3);
                y = *(p_data + i * 3 + 1);
                xy = *(p_data + i * 3 + 2);

                (*p_status)->obj[i].x = (s16)((s16)((s16)(((u16)x) & 0xFF) | (u16)(((u16)xy & 0x30) << 4)));
                (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)y) & 0xFF) | (u16)(((u16)xy & 0xC0) << 2)));
                (*p_status)->obj[i].size = (u16)(((xy & 0xF) & 0xFF));
                (*p_status)->obj[i].size = (u8)((f32)((*p_status)->obj[i].size) * (f32)((*p_status)->obj[i].size) * PI);
                if ((*p_status)->obj[i].size == 0 || (*p_status)->obj[i].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) ||
                    (*p_status)->obj[i].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1)) {
                    (*p_status)->obj[i].x = (s16)0;
                    (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
                    (*p_status)->obj[i].size = 0;
                }
                (*p_status)->obj[i].traceId = i;
            } else {
                (*p_status)->obj[i].x = (s16)0;
                (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
                (*p_status)->obj[i].size = 0;
                (*p_status)->obj[i].traceId = i;
            }
        }
    } else if (fmt == WPAD_DPD_BASIC) {
        for (i = 0; i < WPAD_DPD_MAX_OBJECTS; ++i) {
            x = *(p_data + i * 3 - (i / 2));
            y = *(p_data + i * 3 - (i / 2) + 1);
            xy = *(p_data + (i / 2) * 5 + 2);

            if ((i % 2) == 0) {
                (*p_status)->obj[i].x = (s16)((s16)((s16)(((u16)x) & 0xFF) | (u16)(((u16)(xy & 0x30)) << 4)));
                (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)y) & 0xFF) | (u16)(((u16)(xy & 0xC0)) << 2)));
            } else {
                (*p_status)->obj[i].x = (s16)((s16)((s16)(((u16)x) & 0xFF) | (u16)(((u16)(xy & (u8)(0x30 >> 4))) << 8)));
                (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)y) & 0xFF) | (u16)(((u16)(xy & (u8)(0xC0 >> 4))) << 6)));
            }
            if ((*p_status)->obj[i].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) || (*p_status)->obj[i].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1)) {
                (*p_status)->obj[i].x = (s16)0;
                (*p_status)->obj[i].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
                (*p_status)->obj[i].size = 0;
            } else {
                (*p_status)->obj[i].size = 12;
            }
            (*p_status)->obj[i].traceId = i;
        }
    }
}

void getDevConfig(s32 chan, s32 result, WPADStatus* p_status, u8* data) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    u8* p_buf = p_wpd->wmReadDataPtr;
    u8 checkSum;
    u8 i;
    int j;
    int index;
    DPDObject obj[WPAD_DPD_MAX_OBJECTS];
    const s16 dummyObjX[] = {0x7F, 0x7F, 0x380, 0x380};
    const s16 dummyObjY[] = {0x5D, 0x2A2, 0x2A2, 0x5D};

    for (i = 0; i < WPAD_DPD_MAX_OBJECTS; i++) {
        p_wpd->devConf.obj[i].x = dummyObjX[i];
        p_wpd->devConf.obj[i].y = dummyObjY[i];
        p_wpd->devConf.obj[i].size = (u16)p_wpd->dpdDummyObjSize;
        p_wpd->devConf.obj[i].traceId = i;
    }

    p_wpd->devConf.acc_0g.x = 530;
    p_wpd->devConf.acc_0g.y = 530;
    p_wpd->devConf.acc_0g.z = 530;
    p_wpd->devConf.acc_1g.x = 636;
    p_wpd->devConf.acc_1g.y = 636;
    p_wpd->devConf.acc_1g.z = 636;
    p_wpd->devConf.volume = 0;
    p_wpd->devConf.motor = 0;

    if (result != WPAD_ERR_OK) {
        return;
    }

    if (!p_wpd->oldFw) {
        for (i = 0; i < 2; i++) {
            checkSum = 0;
            index = i * (11);
            for (j = index; j < index + 10; j++) {
                checkSum += p_buf[j];
            }
            checkSum = (u8)(checkSum + 0x55);

            if (checkSum == p_buf[index + 10]) {
                DEBUGPrint("Dpd Setting is ok.\n");

                __parse_dpd_data(chan, &p_status, WPAD_DPD_BASIC, p_buf, index + 11);

                obj[0].x = (s16)((s16)((u16)p_buf[index] & 0xFF) |
                                 (u16)((u16)(p_buf[index + 2] & 0x30) << 4));
                obj[0].y =
                    (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(p_buf[index + 1])) & 0xFF) | (u16)(((u16)(p_buf[index + 2] & 0xC0)) << 2)));
                obj[0].size = p_wpd->dpdDummyObjSize;
                obj[0].traceId = 0;

                obj[1].x = (s16)((s16)((u16)p_buf[index + 3] & 0xFF) |
                                 (u16)((u16)(p_buf[index + 2] & (u8)(0x30 >> 4)) << 8));
                obj[1].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1 -
                                 (s16)((s16)(((u16)(p_buf[index + 4])) & 0xFF) | (u16)(((u16)(p_buf[index + 2] & (u8)(0xC0 >> 4))) << 6)));
                obj[1].size = p_wpd->dpdDummyObjSize;
                obj[1].traceId = 1;

                obj[2].x = (s16)((s16)((u16)p_buf[index + 5] & 0xFF) |
                                 (u16)((u16)(p_buf[index + 7] & 0x30) << 4));
                obj[2].y =
                    (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(p_buf[index + 6])) & 0xFF) | (u16)(((u16)(p_buf[index + 7] & 0xC0)) << 2)));
                obj[2].size = p_wpd->dpdDummyObjSize;
                obj[2].traceId = 2;

                obj[3].x = (s16)((s16)((u16)p_buf[index + 8] & 0xFF) |
                                 (u16)((u16)(p_buf[index + 7] & (u8)(0x30 >> 4)) << 8));
                obj[3].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1 -
                                 (s16)((s16)(((u16)(p_buf[index + 9])) & 0xFF) | (u16)(((u16)(p_buf[index + 7] & (u8)(0xC0 >> 4))) << 6)));
                obj[3].size = p_wpd->dpdDummyObjSize;
                obj[3].traceId = 3;

                break;
            } else {
                DEBUGPrint("Dpd Setting is broken.\n");
            }
        }
    } else {
        DEBUGPrint("Old Firmware uses default values because it has no Dpd Setting.\n");
    }

    for (i = 0; i < 2; i++) {
        checkSum = 0;

        index = i * (10);
        if (!p_wpd->oldFw) {
            index += 22;
        }

        for (j = index; j < index + 9; j++) {
            checkSum += p_buf[j];
        }
        checkSum = (u8)(checkSum + 0x55);
        if (checkSum == p_buf[index + 9]) {
            DEBUGPrint("Acc is ok.\n");
            p_wpd->devConf.acc_0g.x = (s16)((u16)(((u16)(p_buf[index + 0]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3]) >> 4) & 3));
            p_wpd->devConf.acc_0g.y = (s16)((u16)(((u16)(p_buf[index + 1]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3]) >> 2) & 3));
            p_wpd->devConf.acc_0g.z = (s16)((u16)(((u16)(p_buf[index + 2]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3])) & 3));
            p_wpd->devConf.acc_1g.x = (s16)((u16)(((u16)(p_buf[index + 4]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7]) >> 4) & 3));
            p_wpd->devConf.acc_1g.y = (s16)((u16)(((u16)(p_buf[index + 5]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7]) >> 2) & 3));
            p_wpd->devConf.acc_1g.z = (s16)((u16)(((u16)(p_buf[index + 6]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7])) & 3));
            p_wpd->devConf.volume = (u8)(p_buf[index + 8] & (u8)0x7F);
            p_wpd->devConf.motor = (u8)(p_buf[index + 8] & (u8)0x80);
            break;
        } else {
            DEBUGPrint("Acc is broken.\n");
        }
    }
    DEBUGPrint("0G:  accX = %d,  accY = %d,  accZ = %d\n", p_wpd->devConf.acc_0g.x, p_wpd->devConf.acc_0g.y, p_wpd->devConf.acc_0g.z);
    DEBUGPrint("1G:  accX = %d,  accY = %d,  accZ = %d\n", p_wpd->devConf.acc_1g.x, p_wpd->devConf.acc_1g.y, p_wpd->devConf.acc_1g.z);
    DEBUGPrint("Volume: %d,   Motor: %d\n", p_wpd->devConf.volume, p_wpd->devConf.motor);
}

void getExtConfig(s32 chan, s32 result) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    u8* p_buf = p_wpd->wmReadDataPtr;
    int i, j, k;
    int index = -1;
    int checksum;

    if (result != WPAD_ERR_OK) {
        p_wpd->devType = WPAD_DEV_NOT_SUPPORTED;
    } else if (p_wpd->devType == WPAD_DEV_FS || p_wpd->devType == WPAD_DEV_CLASSIC) {
        WPADiDecode(chan, p_buf, 0x20, 0x20);

        for (i = 0; i < 2; i++) {
            checksum = 0;
            k = i * 16;
            for (j = k; j < k + 14; j++) {
                checksum += p_buf[j];
            }

            if (((u8)(checksum + 0x55) == p_buf[k + 14]) && ((u8)(checksum + 0xaa) == p_buf[k + 15])) {
                index = k;
            }
        }

        switch (p_wpd->devType) {
        case WPAD_DEV_FS:
            if (index < 0) {
                DEBUGPrint("check sum error.\n");
                p_wpd->extConf.fs.acc_0g.x = 512;
                p_wpd->extConf.fs.acc_0g.y = 512;
                p_wpd->extConf.fs.acc_0g.z = 512;
                p_wpd->extConf.fs.acc_1g.x = 716;
                p_wpd->extConf.fs.acc_1g.y = 716;
                p_wpd->extConf.fs.acc_1g.z = 716;
            } else {
                p_wpd->extConf.fs.acc_0g.x = (s16)((u16)(((u16)(p_buf[index + 0]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3]) >> 4) & 3));
                p_wpd->extConf.fs.acc_0g.y = (s16)((u16)(((u16)(p_buf[index + 1]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3]) >> 2) & 3));
                p_wpd->extConf.fs.acc_0g.z = (s16)((u16)(((u16)(p_buf[index + 2]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 3])) & 3));
                p_wpd->extConf.fs.acc_1g.x = (s16)((u16)(((u16)(p_buf[index + 4]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7]) >> 4) & 3));
                p_wpd->extConf.fs.acc_1g.y = (s16)((u16)(((u16)(p_buf[index + 5]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7]) >> 2) & 3));
                p_wpd->extConf.fs.acc_1g.z = (s16)((u16)(((u16)(p_buf[index + 6]) << 2) & 0xFFFC) | (u16)(((u16)(p_buf[index + 7])) & 3));
                p_wpd->extConf.fs.stick.x_max = (s8)p_buf[index + 8];
                p_wpd->extConf.fs.stick.x_min = (s8)p_buf[index + 9];
                p_wpd->extConf.fs.stick.x = (s8)p_buf[index + 10];
                p_wpd->extConf.fs.stick.y_max = (s8)p_buf[index + 11];
                p_wpd->extConf.fs.stick.y_min = (s8)p_buf[index + 12];
                p_wpd->extConf.fs.stick.y = (s8)p_buf[index + 13];

                DEBUGPrint("0G:  fsaccX = %d,  fsaccY = %d,  fsaccZ = %d\n", p_wpd->extConf.fs.acc_0g.x, p_wpd->extConf.fs.acc_0g.y,
                           p_wpd->extConf.fs.acc_0g.z);
                DEBUGPrint("1G:  fsaccX = %d,  fsaccY = %d,  fsaccZ = %d\n", p_wpd->extConf.fs.acc_1g.x, p_wpd->extConf.fs.acc_1g.y,
                           p_wpd->extConf.fs.acc_1g.z);
                DEBUGPrint("FS:  X = %d,  X max = %d,  X min = %d\n", p_wpd->extConf.fs.stick.x, p_wpd->extConf.fs.stick.x_max,
                           p_wpd->extConf.fs.stick.x_min);
                DEBUGPrint("FS:  Y = %d,  Y max = %d,  Y min = %d\n", p_wpd->extConf.fs.stick.y, p_wpd->extConf.fs.stick.y_max,
                           p_wpd->extConf.fs.stick.y_min);
            }
            break;

        case WPAD_DEV_CLASSIC:
            p_wpd->extConf.cl.lstk.x_max = (s8)p_buf[index + 0];
            p_wpd->extConf.cl.lstk.x_min = (s8)p_buf[index + 1];
            p_wpd->extConf.cl.lstk.x = (s8)p_buf[index + 2];
            p_wpd->extConf.cl.lstk.y_max = (s8)p_buf[index + 3];
            p_wpd->extConf.cl.lstk.y_min = (s8)p_buf[index + 4];
            p_wpd->extConf.cl.lstk.y = (s8)p_buf[index + 5];
            p_wpd->extConf.cl.rstk.x_max = (s8)p_buf[index + 6];
            p_wpd->extConf.cl.rstk.x_min = (s8)p_buf[index + 7];
            p_wpd->extConf.cl.rstk.x = (s8)p_buf[index + 8];
            p_wpd->extConf.cl.rstk.y_max = (s8)p_buf[index + 9];
            p_wpd->extConf.cl.rstk.y_min = (s8)p_buf[index + 10];
            p_wpd->extConf.cl.rstk.y = (s8)p_buf[index + 11];
            p_wpd->extConf.cl.triggerL = p_buf[index + 12];
            p_wpd->extConf.cl.triggerR = p_buf[index + 13];

            DEBUGPrint("CL:  X = %d,  X max = %d,  X min = %d\n", p_wpd->extConf.cl.lstk.x, p_wpd->extConf.cl.lstk.x_max,
                       p_wpd->extConf.cl.lstk.x_min);
            DEBUGPrint("CL:  Y = %d,  Y max = %d,  Y min = %d\n", p_wpd->extConf.cl.lstk.y, p_wpd->extConf.cl.lstk.y_max,
                       p_wpd->extConf.cl.lstk.y_min);
            DEBUGPrint("CR:  X = %d,  X max = %d,  X min = %d\n", p_wpd->extConf.cl.rstk.x, p_wpd->extConf.cl.rstk.x_max,
                       p_wpd->extConf.cl.rstk.x_min);
            DEBUGPrint("CR:  Y = %d,  Y max = %d,  Y min = %d\n", p_wpd->extConf.cl.rstk.y, p_wpd->extConf.cl.rstk.y_max,
                       p_wpd->extConf.cl.rstk.y_min);
            DEBUGPrint("LR:  L = %d,  R = %d\n", p_wpd->extConf.cl.triggerL, p_wpd->extConf.cl.triggerR);
            break;
        }
        p_wpd->calibrated = 0;
    }

    if (p_wpd->cmdBlkCallback) {
        p_wpd->cmdBlkCallback(chan, p_wpd->devType);
        p_wpd->cmdBlkCallback = NULL;
    }
}

static BOOL isExtEncMain(u8 type) {
    if (type == WPAD_DEV_FS || type == WPAD_DEV_CLASSIC) {
        return TRUE;
    } else {
        return FALSE;
    }
}

extern BOOL __OSInIPL;

void getExtType(s32 chan, s32 result) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    WPADCmdQueue* cmdq = &p_wpd->extCmdQueue;
    u8* p_buf = (u8*)p_wpd->wmReadDataPtr;

    if (result == WPAD_ERR_OK) {
        WPADiDecode(chan, p_buf, 2, 0xfe);

        p_wpd->devMode = p_buf[0];

        switch (p_buf[1]) {
        case 0x00:
            p_wpd->devType = WPAD_DEV_FS;
            break;
        case 0x01:
            p_wpd->devType = WPAD_DEV_CLASSIC;
            break;
        default:
            p_wpd->devType = WPAD_DEV_FUTURE;
            break;
        }

        //_devCmpt[chan] = 0;
        //_devMode[chan] = p_buf[4];
        if (p_wpd->devType == WPAD_DEV_CLASSIC) {
            if (p_wpd->devMode < 1 || p_wpd->devMode > 3) {
                p_wpd->devType = WPAD_DEV_NOT_SUPPORTED;
            }
        }

        DEBUGPrint("type : %d\n", p_wpd->devType);
        DEBUGPrint("mode : %d\n", p_wpd->devMode);
    } else {
        p_wpd->devType = WPAD_DEV_NOT_SUPPORTED;
        p_wpd->devMode = 0;
    }
}

void getGameInfo(s32 chan, s32 result, u8 index) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    WPADMEMGameInfo* p_info = p_wpd->wmReadDataPtr;
    u8* p_buf = p_wpd->wmReadDataPtr;
    int i;
    u8 sum = 0;

    if (result == WPAD_ERR_OK) {
        for (i = 0; i < 47; i++) {
            sum += p_buf[i];
        }
        sum += 0x55;

        if (p_info->checkSum == sum) {
            memcpy(&p_wpd->gameInfo, p_buf, sizeof(WPADMEMGameInfo));
            p_wpd->gameInfoErr[index] = WPAD_ERR_OK;
        } else {
            p_wpd->gameInfoErr[index] = WPAD_ERR_INVALID;
        }
    }
}

WPADExtensionCallback initExtension(s32 chan, WPADControlBlock* p_wpd) {
    WPADCmdQueue* cmdQueue = &p_wpd->extCmdQueue;
    WPADExtensionCallback extCallback = p_wpd->extensionCallback;

    WPADiSendSetReportType(cmdQueue, p_wpd->dataFormat, NULL);
    WPADiSendWriteDataCmd(cmdQueue, 0xAA, WM_REG_EXTENSION_F0, abortInitExtension);

    WPADiCreateKey(chan);
    WPADiSendWriteData(cmdQueue, p_wpd->key, 6, WM_REG_EXTENSION_40, abortInitExtension);
    WPADiSendWriteData(cmdQueue, p_wpd->key + 6, 6, WM_REG_EXTENSION_46, abortInitExtension);
    WPADiSendWriteData(cmdQueue, p_wpd->key + 12, 4, WM_REG_EXTENSION_4C, abortInitExtension);

    WPADiSendReadData(cmdQueue, p_wpd->wmReadDataBuf, 2, WM_REG_EXTENSION_DEV_MODE, abortInitExtension);
    WPADiSendReadData(cmdQueue, p_wpd->wmReadDataBuf, 0x20, WM_REG_EXTENSION_CONFIG, extCallback);

    return extCallback;
}

s32 WPADiHIDParser(u8 chan, u8* data) {
    int enable;
    s32 ret = 0;

    if ((data[0] >= 0x20) && (data[0] <= 0x3F)) {
        enable = OSDisableInterrupts();
        (*(__a1_input_reports_array[data[0] - 0x20]))(chan, data);
        OSRestoreInterrupts(enable);
        WPADiExcludeButton(chan);
        WPADiCopyOut(chan);
    } else {
        ret = -1;
    }

    return ret;
}

void __a1_20_status_report(u8 chan, u8* data) {
    WPADControlBlock* p_wpd;
    BOOL enable;
    BOOL attach;
    WPADStatus* p_status;
    WPADExtensionCallback extcb;
    u8* p_clear;
    int i;
    WPADCmdQueue* cmdQueue;

    DEBUGPrint("Received report 20\n");

    enable = OSDisableInterrupts();
    p_wpd = _wpdcb[chan];

    if (!p_wpd->handshakeFinished) {
        OSRestoreInterrupts(enable);
        return;
    }

    p_status = (WPADStatus*)&p_wpd->rxBufs[p_wpd->rxBufIndex];
    p_clear = (u8*)p_status;
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    attach = p_wpd->info.attach;

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) &
                             HID_WPAD_BUTTON_MASK);
    p_wpd->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_wpd->info.dpd = (data[HID_IDX_BYTE9] & HID_DPD_MASK) >> 3;
    p_wpd->info.speaker = (data[HID_IDX_BYTE9] & HID_SPEAKER_MASK) >> 2;

    p_wpd->info.attach = (data[HID_IDX_BYTE9] & HID_ATTACH_MASK) >> 1;
    p_wpd->info.lowBat = data[HID_IDX_BYTE9] & HID_LOWBAT_MASK;
    p_wpd->info.led = (u8)((data[HID_IDX_BYTE9] & HID_LED_MASK) >> 4);
    p_wpd->info.protocol = (u8)((data[HID_IDX_BYTE11] & HID_PRT_MASK) >> 4);
    p_wpd->info.firmware = (u8)(data[HID_IDX_BYTE11] & HID_FW_MASK);

    if (data[HID_IDX_BYTE12] >= 0x55) {
        p_wpd->info.battery = WPAD_BATTERY_LEVEL_MAX;
    } else if (data[HID_IDX_BYTE12] >= 0x44) {
        p_wpd->info.battery = WPAD_BATTERY_LEVEL_HIGH;
    } else if (data[HID_IDX_BYTE12] >= 0x33) {
        p_wpd->info.battery = WPAD_BATTERY_LEVEL_MEDIUM;
    } else if (data[HID_IDX_BYTE12] >= 0x3) {
        p_wpd->info.battery = WPAD_BATTERY_LEVEL_LOW;
    } else {
        p_wpd->info.battery = WPAD_BATTERY_LEVEL_CRITICAL;
    }

    if (p_wpd->info.attach) {
        if (!attach) {
            DEBUGPrint("initialize attachment\n");

            extcb = initExtension(chan, p_wpd);

            cmdQueue = &p_wpd->extCmdQueue;

            p_wpd->calibrated = 0;
            p_wpd->devType = WPAD_DEV_INITIALIZING;
            p_wpd->devMode = 0;

            if (extcb) {
                extcb(chan, WPAD_DEV_INITIALIZING);
            }
        } else {
            DEBUGPrint("already initialized\n");
        }
    } else {
        p_wpd->devType = WPAD_DEV_CORE;
        p_wpd->devMode = 0;

        if (attach) {
            WPADiClearQueue(&p_wpd->extCmdQueue);
            WPADiSendSetReportType(&p_wpd->stdCmdQueue, p_wpd->dataFormat, NULL);

            if (p_wpd->extensionCallback) {
                p_wpd->extensionCallback(chan, WPAD_DEV_CORE);
            }
        } else {
            DEBUGPrint("already disconnected\n");
        }
    }

    if (p_wpd->infoOut) {
        memcpy(p_wpd->infoOut, &p_wpd->info, sizeof(WPADInfo));
        p_wpd->infoOut = NULL;
    }

    if (p_wpd->cmdBlkCallback && p_wpd->statusReqBusy) {
        p_wpd->cmdBlkCallback(chan, WPAD_ERR_OK);
        p_wpd->cmdBlkCallback = NULL;
    }

    p_status->err = WPAD_ERR_BUSY;
    p_status->dev = p_wpd->devType;
    p_wpd->statusReqBusy = 0;
    p_wpd->rxBufIndex = (u8)((p_wpd->rxBufIndex) ? 0 : 1);

    OSRestoreInterrupts(enable);
}

void __a1_21_user_data(u8 chan, u8* data) {
    WPADControlBlock* p_wpd;
    BOOL enable;
    WPADStatus* p_status;
    u8* p_clear;
    u16 addr;
    u16 base;
    u16 i2c;
    u8 err;
    u8 len;
    s16 offset;
    int i;
    s32 status;

    enable = OSDisableInterrupts();
    p_wpd = _wpdcb[chan];
    p_status = (WPADStatus*)(p_wpd->rxBufs + p_wpd->rxBufIndex);
    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) &
                             HID_WPAD_BUTTON_MASK);

    err = (u8)(data[HID_IDX_BYTE9] & 0x0f);
    len = (u8)(((u8)(data[HID_IDX_BYTE9] >> 4) & 0x0f) + 1);
    addr = (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE10]) << 8) & (u16)0xFF00) | (u16)((u16)(data[HID_IDX_BYTE11]) & (u16)0xFF));

    if (err != 0) {
        DEBUGPrint("read error happens!\n");
        p_wpd->wmReadErr = -1;

        if (p_wpd->cmdBlkCallback) {
            p_wpd->cmdBlkCallback(chan, WPAD_ERR_TRANSFER);
            p_wpd->cmdBlkCallback = NULL;
        }
        p_wpd->status = WPAD_ERR_OK;
    }

    base = (u16)(p_wpd->wmReadAddr & 0xFFFF);
    i2c = (u16)((p_wpd->wmReadAddr & 0xFFFF0000) >> 16);
    offset = (s16)(addr - base);

    if (addr >= base && addr <= base + p_wpd->wmReadLength) {
        memcpy((u8*)p_wpd->wmReadDataPtr + offset, data + HID_IDX_BYTE12, len);

        if (base + p_wpd->wmReadLength == addr + len) {
            DEBUGPrint("base addr: %08x\n", p_wpd->wmReadAddr);
            DEBUGPrint("length   : %d\n", p_wpd->wmReadLength);
            status = p_wpd->wmReadErr < 0 ? WPAD_CETRANSFER : WPAD_CESUCCESS;

            if ((p_wpd->wmReadAddr == 0 && p_wpd->oldFw == FALSE) ||
                (p_wpd->wmReadAddr == WM_ADDR_MEM_176C && p_wpd->oldFw == TRUE)) {
                getDevConfig(chan, status, p_status, data);
            }
            if (p_wpd->wmReadAddr == WM_REG_EXTENSION_CONFIG) {
                getExtConfig(chan, status);
            }
            if (p_wpd->wmReadAddr == WM_REG_EXTENSION_DEV_MODE) {
                getExtType(chan, status);
            }
            if (p_wpd->wmReadAddr == 0x2A) {
                getGameInfo(chan, status, 0);
            }
            if (p_wpd->wmReadAddr == 0x2A + sizeof(WPADMEMGameInfo)) {
                getGameInfo(chan, status, 1);
            }

            if (p_wpd->cmdBlkCallback) {
                p_wpd->cmdBlkCallback(chan, status);
                p_wpd->cmdBlkCallback = NULL;
            }
            p_wpd->status = WPAD_ERR_OK;
        }
    } else {
        DEBUGPrint("received data is out of range!\n");
    }

    p_status->err = WPAD_ERR_BUSY;
    p_status->dev = p_wpd->devType;

    p_wpd->rxBufIndex = (u8)((p_wpd->rxBufIndex) ? 0 : 1);

    OSRestoreInterrupts(enable);
}

void __a1_22_ack(u8 chan, u8* data) {
    WPADControlBlock* p_wpd = _wpdcb[chan];
    u8 type;
    u8 err;
    BOOL enable;
    WPADStatus* p_status;
    u8* p_clear;
    s32 status;
    int i;

    enable = OSDisableInterrupts();

    DEBUGPrint("Received ack!\n");

    p_wpd = _wpdcb[chan];
    p_status = (WPADStatus*)(p_wpd->rxBufs + p_wpd->rxBufIndex);
    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    type = data[HID_IDX_BYTE9];
    err = data[HID_IDX_BYTE10];

    DEBUGPrint("ack --> report ID = %02x, error code = %d\n", type, err);

    status = err == 0 ? WPAD_CESUCCESS : WPAD_CETRANSFER;
    p_status->err = WPAD_ERR_BUSY;
    p_status->dev = p_wpd->devType;

    if (err != 0) {
        DEBUGPrint("ack error --> report ID = %d, error code = %d\n", type, err);
    }

    if (p_wpd->lastReportId == type) {
        if (p_wpd->cmdBlkCallback) {
            p_wpd->cmdBlkCallback(chan, status);
            p_wpd->cmdBlkCallback = NULL;
        }
        p_wpd->status = WPAD_ERR_OK;
    } else {
        DEBUGPrint("invalid ack!\n");
    }
    p_wpd->rxBufIndex = (u8)((p_wpd->rxBufIndex) ? 0 : 1);

    OSRestoreInterrupts(enable);
}

void __a1_30_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    u8* p_clear;

    int old;
    int i;

    old = OSDisableInterrupts();
    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)data[HID_IDX_BYTE7] & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) &
                             HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN) {
        p_status->err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}

void __a1_31_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    u8* p_clear;
    int old, i;

    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)data[HID_IDX_BYTE7] & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) &
                             HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC) {
        p_status->err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_status->accX = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE9] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE7] >> 5) & (s16)0x0003)) -
                           p_cb->devConf.acc_0g.x);
    p_status->accY = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE10] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE8] >> 4) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.y);
    p_status->accZ = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE11] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE8] >> 5) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.z);
    old = OSDisableInterrupts();
    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}

void __a1_32_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    WPADFSStatus* p_fsStat;
    WPADCLStatus* p_clStat;
    u8* p_clear;
    int old, i;
    s16 x, y;
    old = OSDisableInterrupts();
    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_FS_BTN || p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN) {
        p_status->err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);

    memcpy(checkBuffer, data + HID_IDX_BYTE9, 8);
    WPADiDecode(chan, data + HID_IDX_BYTE9, 8, 0);

    if (p_cb->info.attach) {
        if (p_cb->devType == WPAD_DEV_FS) {
            p_fsStat = (WPADFSStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

            p_fsStat->fsStickX = (s8)(data[HID_IDX_BYTE9]);
            p_fsStat->fsStickY = (s8)(data[HID_IDX_BYTE10]);
            p_fsStat->fsAccX =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE14] >> 2) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.x);
            p_fsStat->fsAccY =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE14] >> 4) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.y);
            p_fsStat->fsAccZ =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE13]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE14] >> 6) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.z);

            p_fsStat->base.button |= (u16)(((u16)(~data[HID_IDX_BYTE14]) << 13) & (u16)0x6000);

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.fs.stick.x = p_fsStat->fsStickX;
                p_cb->extConf.fs.stick.y = p_fsStat->fsStickY;
            }

            x = (s16)((u8)p_fsStat->fsStickX - (u8)p_cb->extConf.fs.stick.x);
            y = (s16)((u8)p_fsStat->fsStickY - (u8)p_cb->extConf.fs.stick.y);

            if (x < -128) {
                p_fsStat->fsStickX = -128;
            } else if (x > 127) {
                p_fsStat->fsStickX = 127;
            } else {
                p_fsStat->fsStickX = (s8)x;
            }

            if (y < -128) {
                p_fsStat->fsStickY = -128;
            } else if (y > 127) {
                p_fsStat->fsStickY = 127;
            } else {
                p_fsStat->fsStickY = (s8)y;
            }
        } else if (p_cb->devType == WPAD_DEV_CLASSIC) {
            switch (p_cb->devMode) {
            case 1:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) & (s16)0x003F) << 4));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE10]) & (s16)0x003F) << 4));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) >> 3) & (s16)0x0018) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE10]) >> 5) & (s16)0x0006) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE11]) >> 7) & (s16)0x0001))
                                                  << 5) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) & (s16)0x001F) << 5) - (s16)(1024 / 2));
                p_clStat->clTriggerL =
                    (u8)((u8)((u8)((u8)(data[HID_IDX_BYTE11] >> 2) & (u8)0x18) | (u8)((u8)(data[HID_IDX_BYTE12] >> 5) & (u8)0x07)) << 3);
                p_clStat->clTriggerR = (u8)((u8)(data[HID_IDX_BYTE12] & (u8)0x1F) << 3);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE13]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE14]) & (u16)0xFF) ^ 0xFFFF);
                break;

            case 2:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
                p_clStat->clLStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE13])) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE10]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE13]) >> 2) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE13]) >> 4) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE13]) >> 6) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE14]);
                p_clStat->clTriggerR = (u8)(data[HID_IDX_BYTE15]);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE16]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE17]) & (u16)0xFF) ^ 0xFFFF);
                break;

            case 3:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE10]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE13]);
                p_clStat->clTriggerR = (u8)(data[HID_IDX_BYTE14]);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE15]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE16]) & (u16)0xFF) ^ 0xFFFF);
                break;
            }

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.cl.lstk.x = p_clStat->clLStickX;
                p_cb->extConf.cl.lstk.y = p_clStat->clLStickY;
                p_cb->extConf.cl.rstk.x = p_clStat->clRStickX;
                p_cb->extConf.cl.rstk.y = p_clStat->clRStickY;
                p_cb->extConf.cl.triggerL = p_clStat->clTriggerL;
                p_cb->extConf.cl.triggerR = p_clStat->clTriggerR;
            }

            x = (s16)(p_clStat->clLStickX - p_cb->extConf.cl.lstk.x);
            y = (s16)(p_clStat->clLStickY - p_cb->extConf.cl.lstk.y);

            if (x < -0x400) {
                p_clStat->clLStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clLStickX = 0x3FF;
            } else {
                p_clStat->clLStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clLStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clLStickY = 0x3FF;
            } else {
                p_clStat->clLStickY = y;
            }

            x = (s16)(p_clStat->clRStickX - p_cb->extConf.cl.rstk.x);
            y = (s16)(p_clStat->clRStickY - p_cb->extConf.cl.rstk.y);

            if (x < -0x400) {
                p_clStat->clRStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clRStickX = 0x3FF;
            } else {
                p_clStat->clRStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clRStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clRStickY = 0x3FF;
            } else {
                p_clStat->clRStickY = y;
            }

            x = (s16)((u8)p_clStat->clTriggerL - (u8)p_cb->extConf.cl.triggerL);
            y = (s16)((u8)p_clStat->clTriggerR - (u8)p_cb->extConf.cl.triggerR);

            if (x < 0) {
                p_clStat->clTriggerL = 0;
            } else {
                p_clStat->clTriggerL = p_clStat->clTriggerL = (u8)x;
            }

            if (y < 0) {
                p_clStat->clTriggerR = 0;
            } else {
                p_clStat->clTriggerR = p_clStat->clTriggerR = (u8)y;
            }
        }

        if (!memcmp(checkBuffer, checkInvalidData, 7)) {
            if (p_status->err == WPAD_ERR_OK) {
                p_status->err = WPAD_ERR_CORRUPTED;
            }
        }
    }

    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}

void __a1_33_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    u8* p_clear;
    int old, i;

    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
    p_clear = (u8*)(p_status);

    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC_DPD) {
        p_status->err = WPAD_ERR_OK;
    } else {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_status->accX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE7] >> 5)) & (s16)0x0003))) -
                           (s16)(p_cb->devConf.acc_0g.x));
    p_status->accY = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE10]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE8] >> 4)) & (s16)0x0002))) -
                           (s16)(p_cb->devConf.acc_0g.y));
    p_status->accZ = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE8] >> 5)) & (s16)0x0002))) -
                           (s16)(p_cb->devConf.acc_0g.z));

    __parse_dpd_data(chan, &p_status, WPAD_DPD_STANDARD, data, 0);

    old = OSDisableInterrupts();
    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}

void __a1_34_data_type(u8 chan, u8* data) {
    // empty function
}

void __a1_35_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    WPADFSStatus* p_fsStat;
    WPADCLStatus* p_clStat;
    u8* p_clear;
    int old, i;
    s16 x, y;
    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

    p_clear = (u8*)(p_status);
    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC || p_cb->dataFormat == WPAD_FMT_FS_BTN ||
        p_cb->dataFormat == WPAD_FMT_FS_BTN_ACC || p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN || p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN_ACC) {
        p_status->err = p_cb->status;
    } else if (p_cb->status != 0) {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_status->accX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE7] >> 5)) & (s16)0x0003))) -
                           (s16)(p_cb->devConf.acc_0g.x));
    p_status->accY = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE10]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE8] >> 4)) & (s16)0x0002))) -
                           (s16)(p_cb->devConf.acc_0g.y));
    p_status->accZ = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE11]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE8] >> 5)) & (s16)0x0002))) -
                           (s16)(p_cb->devConf.acc_0g.z));

    memcpy(checkBuffer, data + HID_IDX_BYTE12, 16);
    WPADiDecode(chan, data + HID_IDX_BYTE12, 16, 0);

    if (p_cb->info.attach) {
        if (p_cb->devType == WPAD_DEV_FS) {
            p_fsStat = (WPADFSStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

            p_fsStat->fsStickX = (s8)(data[HID_IDX_BYTE12]);
            p_fsStat->fsStickY = (s8)(data[HID_IDX_BYTE13]);
            p_fsStat->fsAccX =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE14]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE17] >> 2) & (s16)(0x0003))) -
                      (s16)(p_cb->extConf.fs.acc_0g.x));
            p_fsStat->fsAccY =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE15]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE17] >> 4) & (s16)(0x0003))) -
                      (s16)(p_cb->extConf.fs.acc_0g.y));
            p_fsStat->fsAccZ =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE16]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE17] >> 6) & (s16)(0x0003))) -
                      (s16)(p_cb->extConf.fs.acc_0g.z));

            p_fsStat->base.button |= (u16)(((u16)(~data[HID_IDX_BYTE17]) << 13) & (u16)0x6000);

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.fs.stick.x = p_fsStat->fsStickX;
                p_cb->extConf.fs.stick.y = p_fsStat->fsStickY;
            }

            x = (s16)((u8)p_fsStat->fsStickX - (u8)p_cb->extConf.fs.stick.x);
            y = (s16)((u8)p_fsStat->fsStickY - (u8)p_cb->extConf.fs.stick.y);

            if (x < -128) {
                p_fsStat->fsStickX = -128;
            } else if (x > 127) {
                p_fsStat->fsStickX = 127;
            } else {
                p_fsStat->fsStickX = (s8)x;
            }

            if (y < -128) {
                p_fsStat->fsStickY = -128;
            } else if (y > 127) {
                p_fsStat->fsStickY = 127;
            } else {
                p_fsStat->fsStickY = (s8)y;
            }
        } else if (p_cb->devType == WPAD_DEV_CLASSIC) {
            switch (p_cb->devMode) {
            case 1:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) & (s16)0x003F) << 4) - (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE13]) & (s16)0x003F) << 4) - (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) >> 3) & (s16)0x0018) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE13]) >> 5) & (s16)0x0006) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE14]) >> 7) & (s16)0x0001))
                                                  << 5) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE14]) & (s16)0x001F) << 5) - (s16)(1024 / 2));
                p_clStat->clTriggerL =
                    (u8)((u8)((u8)((u8)(data[HID_IDX_BYTE14] >> 2) & (u8)0x18) | (u8)((u8)(data[HID_IDX_BYTE15] >> 5) & (u8)0x07)) << 3);
                p_clStat->clTriggerR = (u8)((u8)((u8)(data[HID_IDX_BYTE15]) & (u8)0x1F) << 3);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE16]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE17]) & (u16)0xFF) ^ 0xFFFF);
                break;

            case 2:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
                p_clStat->clLStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE16])) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE13]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE16]) >> 2) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE14]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE16]) >> 4) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE15]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE16]) >> 6) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE17]);
                p_clStat->clTriggerR = (u8)(data[HID_IDX_BYTE18]);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE19]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE20]) & (u16)0xFF) ^ 0xFFFF);
                break;

            case 3:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE12]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE13]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE14]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE15]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE16]);
                p_clStat->clTriggerR = (u8)(data[HID_IDX_BYTE17]);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE18]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE19]) & (u16)0xFF) ^ 0xFFFF);
                break;
            }

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.cl.lstk.x = p_clStat->clLStickX;
                p_cb->extConf.cl.lstk.y = p_clStat->clLStickY;
                p_cb->extConf.cl.rstk.x = p_clStat->clRStickX;
                p_cb->extConf.cl.rstk.y = p_clStat->clRStickY;
                p_cb->extConf.cl.triggerL = p_clStat->clTriggerL;
                p_cb->extConf.cl.triggerR = p_clStat->clTriggerR;
            }

            x = (s16)(p_clStat->clLStickX - p_cb->extConf.cl.lstk.x);
            y = (s16)(p_clStat->clLStickY - p_cb->extConf.cl.lstk.y);

            if (x < -0x400) {
                p_clStat->clLStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clLStickX = 0x3FF;
            } else {
                p_clStat->clLStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clLStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clLStickY = 0x3FF;
            } else {
                p_clStat->clLStickY = y;
            }

            x = (s16)(p_clStat->clRStickX - p_cb->extConf.cl.rstk.x);
            y = (s16)(p_clStat->clRStickY - p_cb->extConf.cl.rstk.y);

            if (x < -0x400) {
                p_clStat->clRStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clRStickX = 0x3FF;
            } else {
                p_clStat->clRStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clRStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clRStickY = 0x3FF;
            } else {
                p_clStat->clRStickY = y;
            }

            x = (s16)(p_clStat->clTriggerL - p_cb->extConf.cl.triggerL);
            y = (s16)(p_clStat->clTriggerR - p_cb->extConf.cl.triggerR);

            if (x < 0) {
                p_clStat->clTriggerL = 0;
            } else {
                p_clStat->clTriggerL = p_clStat->clTriggerL = (u8)x;
            }

            if (y < 0) {
                p_clStat->clTriggerR = 0;
            } else {
                p_clStat->clTriggerR = p_clStat->clTriggerR = (u8)y;
            }
        }

        if (!memcmp(checkBuffer, checkInvalidData, 16)) {
            if (p_status->err == WPAD_ERR_OK) {
                p_status->err = WPAD_ERR_CORRUPTED;
            }
        }
    }

    old = OSDisableInterrupts();
    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}

void __a1_36_data_type(u8 chan, u8* data) {
    // empty function
}

void __a1_37_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatus* p_status;
    WPADFSStatus* p_fsStat;
    WPADCLStatus* p_clStat;
    u8* p_clear;
    int old, i;
    s16 x, y;
    s16 temp_r0;
    p_cb = _wpdcb[chan];
    p_status = (WPADStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
    p_clear = (u8*)(p_status);

    for (i = 0; i < WPAD_RX_DATASIZE; i++) {
        p_clear[i] = 0;
    }

    p_status->button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC_DPD ||
        p_cb->dataFormat == WPAD_FMT_FS_BTN || p_cb->dataFormat == WPAD_FMT_FS_BTN_ACC || p_cb->dataFormat == WPAD_FMT_FS_BTN_ACC_DPD ||
        p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN || p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN_ACC || p_cb->dataFormat == WPAD_FMT_CLASSIC_BTN_ACC_DPD) {
        p_status->err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->err = WPAD_ERR_INVALID;
    }

    p_status->dev = p_cb->devType;
    p_cb->info.nearempty = (u8)((data[HID_IDX_BYTE7] & HID_NEAREMPTY_MASK) >> 7);
    p_status->accX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE9]) << 2) & (s16)0xFFFC) |
                                       (s16)((s16)((u16)(data[HID_IDX_BYTE7] >> 5)) & (s16)0x0003))) -
                           p_cb->devConf.acc_0g.x);
    p_status->accY = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE10] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE8] >> 4) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.y);
    p_status->accZ = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE11] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE8] >> 5) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.z);

    __parse_dpd_data(chan, &p_status, WPAD_DPD_STANDARD, data, 0);

    memcpy(checkBuffer, data + HID_IDX_BYTE22, 6);
    WPADiDecode(chan, data + HID_IDX_BYTE22, 6, 0);

    if (p_cb->info.attach) {
        if (p_cb->devType == WPAD_DEV_FS) {
            p_fsStat = (WPADFSStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

            p_fsStat->fsStickX = (s8)(data[HID_IDX_BYTE22]);
            p_fsStat->fsStickY = (s8)(data[HID_IDX_BYTE23]);
            p_fsStat->fsAccX =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE24]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE27] >> 2) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.x);
            p_fsStat->fsAccY =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE25]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE27] >> 4) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.y);
            p_fsStat->fsAccZ =
                (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE26]) << 2) & (s16)0xFFFC) | (s16)((s16)(data[HID_IDX_BYTE27] >> 6) & (s16)(0x0003))) -
                      p_cb->extConf.fs.acc_0g.z);

            p_fsStat->base.button |= (u16)(((u16)(~data[HID_IDX_BYTE27]) << 13) & (u16)0x6000);

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.fs.stick.x = p_fsStat->fsStickX;
                p_cb->extConf.fs.stick.y = p_fsStat->fsStickY;
            }

            x = (s16)((u8)p_fsStat->fsStickX - (u8)p_cb->extConf.fs.stick.x);
            y = (s16)((u8)p_fsStat->fsStickY - (u8)p_cb->extConf.fs.stick.y);

            if (x < -128) {
                p_fsStat->fsStickX = -128;
            } else if (x > 127) {
                p_fsStat->fsStickX = 127;
            } else {
                p_fsStat->fsStickX = (s8)x;
            }

            if (y < -128) {
                p_fsStat->fsStickY = -128;
            } else if (y > 127) {
                p_fsStat->fsStickY = 127;
            } else {
                p_fsStat->fsStickY = (s8)y;
            }
        } else if (p_cb->devType == WPAD_DEV_CLASSIC) {
            switch (p_cb->devMode) {
            case 1:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE22]) & (s16)0x003F) << 4) - (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE23]) & (s16)0x003F) << 4) - (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE22]) >> 3) & (s16)0x0018) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE23]) >> 5) & (s16)0x0006) |
                                                        (s16)((s16)((s16)(data[HID_IDX_BYTE24]) >> 7) & (s16)0x0001))
                                                  << 5) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE24]) & (s16)0x001F) << 5) - (s16)(1024 / 2));
                p_clStat->clTriggerL =
                    (u8)((u8)((u8)((u8)(data[HID_IDX_BYTE24] >> 2) & (u8)0x18) | (u8)((u8)(data[HID_IDX_BYTE25] >> 5) & (u8)0x07)) << 3);
                p_clStat->clTriggerR = (u8)((u8)((u8)(data[HID_IDX_BYTE25]) & (u8)0x1F) << 3);

                p_clStat->clButton =
                    (u16)((u16)((u16)((u16)(data[HID_IDX_BYTE26]) << 8) & (u16)0xFF00 | (u16)(data[HID_IDX_BYTE27]) & (u16)0xFF) ^ 0xFFFF);
                break;

            case 2:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];
                p_clStat->clLStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE22]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE26])) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE23]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE26]) >> 2) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE24]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE26]) >> 4) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)((s16)(data[HID_IDX_BYTE25]) << 2) & (s16)0xFFFC) |
                                                  (s16)((s16)((s16)(data[HID_IDX_BYTE26]) >> 6) & (s16)0x0003)) -
                                            (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE27]);
                p_clStat->clTriggerR = (u8)0;

                p_clStat->clButton = (u16)0;
                break;

            case 3:
                p_clStat = (WPADCLStatus*)&p_cb->rxBufs[p_cb->rxBufIndex];

                p_clStat->clLStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE22]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickX = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE23]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clLStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE24]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clRStickY = (s16)((s16)((s16)((s16)(data[HID_IDX_BYTE25]) & (s16)0xFF) << 2) - (s16)(1024 / 2));
                p_clStat->clTriggerL = (u8)(data[HID_IDX_BYTE26]);
                p_clStat->clTriggerR = (u8)(data[HID_IDX_BYTE27]);

                p_clStat->clButton = (u16)0;
                break;
            }

            if (!p_cb->calibrated) {
                p_cb->calibrated = 1;
                p_cb->extConf.cl.lstk.x = p_clStat->clLStickX;
                p_cb->extConf.cl.lstk.y = p_clStat->clLStickY;
                p_cb->extConf.cl.rstk.x = p_clStat->clRStickX;
                p_cb->extConf.cl.rstk.y = p_clStat->clRStickY;
                p_cb->extConf.cl.triggerL = p_clStat->clTriggerL;
                p_cb->extConf.cl.triggerR = p_clStat->clTriggerR;
            }

            x = (s16)(p_clStat->clLStickX - p_cb->extConf.cl.lstk.x);
            y = (s16)(p_clStat->clLStickY - p_cb->extConf.cl.lstk.y);

            if (x < -0x400) {
                p_clStat->clLStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clLStickX = 0x3FF;
            } else {
                p_clStat->clLStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clLStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clLStickY = 0x3FF;
            } else {
                p_clStat->clLStickY = y;
            }

            x = (s16)(p_clStat->clRStickX - p_cb->extConf.cl.rstk.x);
            y = (s16)(p_clStat->clRStickY - p_cb->extConf.cl.rstk.y);

            if (x < -0x400) {
                p_clStat->clRStickX = -0x400;
            } else if (x > 0x3FF) {
                p_clStat->clRStickX = 0x3FF;
            } else {
                p_clStat->clRStickX = x;
            }

            if (y < -0x400) {
                p_clStat->clRStickY = -0x400;
            } else if (y > 0x3FF) {
                p_clStat->clRStickY = 0x3FF;
            } else {
                p_clStat->clRStickY = y;
            }

            x = (s16)(p_clStat->clTriggerL - p_cb->extConf.cl.triggerL);
            y = (s16)(p_clStat->clTriggerR - p_cb->extConf.cl.triggerR);

            if (x < 0) {
                p_clStat->clTriggerL = 0;
            } else {
                p_clStat->clTriggerL = p_clStat->clTriggerL = (u8)x;
            }

            if (y < 0) {
                p_clStat->clTriggerR = 0;
            } else {
                p_clStat->clTriggerR = p_clStat->clTriggerR = (u8)y;
            }
        }

        if (!memcmp(checkBuffer, checkInvalidData, 6)) {
            if (p_status->err == WPAD_ERR_OK) {
                p_status->err = WPAD_ERR_CORRUPTED;
            }
        }
    }

    old = OSDisableInterrupts();
    p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
    OSRestoreInterrupts(old);
}
void __a1_3d_data_type(u8 chan, u8* data) {}

u8 _recv_3e[WPAD_MAX_CONTROLLERS] = {0, 0, 0, 0};
u8 _recv_3f[WPAD_MAX_CONTROLLERS] = {0, 0, 0, 0};

void __a1_3e_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatusEx* p_status;
    u8* p_clear;
    int old, i;
    p_cb = _wpdcb[chan];
    p_status = (WPADStatusEx*)&p_cb->rxBufs[p_cb->rxBufIndex];

    if (_recv_3e[chan] == 0 && _recv_3f[chan] == 0) {
        p_clear = (u8*)(p_status);
        for (i = 0; i < WPAD_RX_DATASIZE; i++) {
            p_clear[i] = 0;
        }
    }

    p_status->base.button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) & (u16)HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN ||
        p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC ||
        p_cb->dataFormat == WPAD_FMT_BTN_ACC_DPD_EXTENDED) {
        p_status->base.err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->base.err = WPAD_ERR_INVALID;
    }
    p_status->base.dev = p_cb->devType;
    p_cb->info.nearempty = 0;
    p_status->base.accX = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE9] << 2) & (s16)0xFFFC) |
                                      (s16)((s16)(u16)(data[HID_IDX_BYTE7] >> 6) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.x);
    p_status->base.accZ |= (s16)((s16)((s16)((s16)data[HID_IDX_BYTE8] << 3) & (s16)0xFF00) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE7] << 1) & (s16)0x00C0));

    p_status->base.obj[0].x = (s16)((s16)((u16)data[HID_IDX_BYTE10] & 0xFF) |
                                    (u16)((u16)(data[HID_IDX_BYTE12] & 0x30) << 4));
    p_status->base.obj[0].y =
        (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(data[HID_IDX_BYTE11])) & 0xFF) | (u16)(((u16)(data[HID_IDX_BYTE12] & 0xC0)) << 2)));
    p_status->exp[0].pixel = (u16)(((s16)((s16)(((u16)(data[HID_IDX_BYTE17]) << 8) & 0xFF00) | (u16)(data[HID_IDX_BYTE18] & 0xFF))) * 64);
    p_status->exp[0].radius = (s8)((data[HID_IDX_BYTE12] & 0x0f));
    p_status->exp[0].range_x1 = (s16)(((s8)(data[HID_IDX_BYTE13]) == -1) ? 0 : data[HID_IDX_BYTE13]);
    p_status->exp[0].range_y1 = (s16)(((s8)(data[HID_IDX_BYTE14]) == -1) ? 0 : data[HID_IDX_BYTE14]);
    p_status->exp[0].range_x2 = (s16)(((s8)(data[HID_IDX_BYTE15]) == -1) ? 0 : data[HID_IDX_BYTE15]);
    p_status->exp[0].range_y2 = (s16)(((s8)(data[HID_IDX_BYTE16]) == -1) ? 0 : data[HID_IDX_BYTE16]);
    p_status->exp[0].range_x1 = (s16)(p_status->exp[0].range_x1 * 8);
    p_status->exp[0].range_y1 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[0].range_y1 * 8));
    p_status->exp[0].range_x2 = (s16)(p_status->exp[0].range_x2 * 8);
    p_status->exp[0].range_y2 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[0].range_y2 * 8));
    p_status->base.obj[0].size = (u16)((f32)p_status->exp[0].radius * (f32)p_status->exp[0].radius * PI);
    if (p_status->base.obj[0].size == 0 || p_status->base.obj[0].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) ||
        p_status->base.obj[0].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1) || p_status->exp[0].radius == 0xf) {
        p_status->base.obj[0].x = (s16)0;
        p_status->base.obj[0].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
        p_status->base.obj[0].size = 0;
        p_status->exp[0].pixel = 0;
        p_status->exp[0].radius = 0;
    }
    p_status->base.obj[0].traceId = 0;

    p_status->base.obj[1].x = (s16)((s16)((u16)data[HID_IDX_BYTE19] & 0xFF) |
                               (u16)((u16)(data[HID_IDX_BYTE21] & 0x30) << 4));
    p_status->base.obj[1].y =
        (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(data[HID_IDX_BYTE20])) & 0xFF) | (u16)(((u16)(data[HID_IDX_BYTE21] & 0xC0)) << 2)));
    p_status->exp[1].pixel = (u16)(((s16)((s16)(((u16)(data[HID_IDX_BYTE26]) << 8) & 0xFF00) | (u16)(data[HID_IDX_BYTE27] & 0xFF))) * 64);
    p_status->exp[1].radius = (s8)((data[HID_IDX_BYTE21] & 0x0f));
    p_status->exp[1].range_x1 = (s16)(((s8)(data[HID_IDX_BYTE22]) == -1) ? 0 : data[HID_IDX_BYTE22]);
    p_status->exp[1].range_y1 = (s16)(((s8)(data[HID_IDX_BYTE23]) == -1) ? 0 : data[HID_IDX_BYTE23]);
    p_status->exp[1].range_x2 = (s16)(((s8)(data[HID_IDX_BYTE24]) == -1) ? 0 : data[HID_IDX_BYTE24]);
    p_status->exp[1].range_y2 = (s16)(((s8)(data[HID_IDX_BYTE25]) == -1) ? 0 : data[HID_IDX_BYTE25]);
    p_status->exp[1].range_x1 = (s16)(p_status->exp[1].range_x1 * 8);
    p_status->exp[1].range_y1 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[1].range_y1 * 8));
    p_status->exp[1].range_x2 = (s16)(p_status->exp[1].range_x2 * 8);
    p_status->exp[1].range_y2 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[1].range_y2 * 8));
    p_status->base.obj[1].size = (u16)((f32)p_status->exp[1].radius * (f32)p_status->exp[1].radius * PI);
    if (p_status->base.obj[1].size == 0 || p_status->base.obj[1].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) ||
        p_status->base.obj[1].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1) || p_status->exp[1].radius == 0xf) {
        p_status->base.obj[1].x = (s16)0;
        p_status->base.obj[1].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
        p_status->base.obj[1].size = 0;
        p_status->exp[1].pixel = 0;
        p_status->exp[1].radius = 0;
    }
    p_status->base.obj[1].traceId = 1;

    old = OSDisableInterrupts();
    _recv_3e[chan] = 1;
    if (_recv_3e[chan] && _recv_3f[chan]) {
        p_status->base.accZ -= (s16)(p_cb->devConf.acc_0g.z);
        p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
        _recv_3e[chan] = _recv_3f[chan] = 0;
    }
    OSRestoreInterrupts(old);
}

void __a1_3f_data_type(u8 chan, u8* data) {
    WPADControlBlock* p_cb;
    WPADStatusEx* p_status;
    u8* p_clear;
    int old, i;
    p_cb = _wpdcb[chan];
    p_status = (WPADStatusEx*)&p_cb->rxBufs[p_cb->rxBufIndex];

    if (_recv_3e[chan] == 0 && _recv_3f[chan] == 0) {
        p_clear = (u8*)(p_status);
        for (i = 0; i < WPAD_RX_DATASIZE; i++) {
            p_clear[i] = 0;
        }
    }

    p_status->base.button =
        (u16)((u16)(((u16)((u16)(data[HID_IDX_BYTE7]) & 0xFF) | (u16)((u16)(data[HID_IDX_BYTE8] << 8) & 0xFF00))) &
                             HID_WPAD_BUTTON_MASK);

    if (p_cb->dataFormat == WPAD_FMT_CORE_BTN || p_cb->dataFormat == WPAD_FMT_CORE_BTN_ACC || p_cb->dataFormat == WPAD_FMT_BTN_ACC_DPD_EXTENDED) {
        p_status->base.err = p_cb->status;
    } else if (p_cb->status == 0) {
        p_status->base.err = WPAD_ERR_INVALID;
    }
    p_status->base.dev = p_cb->devType;
    p_cb->info.nearempty = 0;
    p_status->base.accY = (s16)((s16)((s16)((s16)((s16)data[HID_IDX_BYTE9] << 2) & (s16)0xFFFC) |
                                 (s16)((s16)(u16)(data[HID_IDX_BYTE7] >> 6) & (s16)0x0002)) -
                           p_cb->devConf.acc_0g.y);
    p_status->base.accZ |= (s16)((s16)((s16)((s16)data[HID_IDX_BYTE8] >> 1) & (s16)0x0030) |
                            (s16)((s16)(u16)(data[HID_IDX_BYTE7] >> 3) & (s16)0x000C));

    p_status->base.obj[2].x = (s16)((s16)((u16)data[HID_IDX_BYTE10] & 0xFF) |
                               (u16)((u16)(data[HID_IDX_BYTE12] & 0x30) << 4));
    p_status->base.obj[2].y =
        (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(data[HID_IDX_BYTE11])) & 0xFF) | (u16)(((u16)(data[HID_IDX_BYTE12] & 0xC0)) << 2)));
    p_status->exp[2].pixel = (u16)(((s16)((s16)(((u16)(data[HID_IDX_BYTE17]) << 8) & 0xFF00) | (u16)(data[HID_IDX_BYTE18] & 0xFF))) * 64);
    p_status->exp[2].radius = (s8)((data[HID_IDX_BYTE12] & 0x0f));
    p_status->exp[2].range_x1 = (s16)(((s8)(data[HID_IDX_BYTE13]) == -1) ? 0 : data[HID_IDX_BYTE13]);
    p_status->exp[2].range_y1 = (s16)(((s8)(data[HID_IDX_BYTE14]) == -1) ? 0 : data[HID_IDX_BYTE14]);
    p_status->exp[2].range_x2 = (s16)(((s8)(data[HID_IDX_BYTE15]) == -1) ? 0 : data[HID_IDX_BYTE15]);
    p_status->exp[2].range_y2 = (s16)(((s8)(data[HID_IDX_BYTE16]) == -1) ? 0 : data[HID_IDX_BYTE16]);
    p_status->exp[2].range_x1 = (s16)(p_status->exp[2].range_x1 * 8);
    p_status->exp[2].range_y1 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[2].range_y1 * 8));
    p_status->exp[2].range_x2 = (s16)(p_status->exp[2].range_x2 * 8);
    p_status->exp[2].range_y2 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[2].range_y2 * 8));
    p_status->base.obj[2].size = (u16)((f32)p_status->exp[2].radius * (f32)p_status->exp[2].radius * PI);
    if (p_status->base.obj[2].size == 0 || p_status->base.obj[2].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) ||
        p_status->base.obj[2].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1) || p_status->exp[2].radius == 0xf) {
        p_status->base.obj[2].x = (s16)0;
        p_status->base.obj[2].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
        p_status->base.obj[2].size = 0;
        p_status->exp[2].pixel = 0;
        p_status->exp[2].radius = 0;
    }
    p_status->base.obj[2].traceId = 2;

    p_status->base.obj[3].x = (s16)((s16)((u16)data[HID_IDX_BYTE19] & 0xFF) |
                                    (u16)((u16)(data[HID_IDX_BYTE21] & 0x30) << 4));
    p_status->base.obj[3].y =
        (s16)(WPAD_DPD_IMG_RESO_WY - 1 - (s16)((s16)(((u16)(data[HID_IDX_BYTE20])) & 0xFF) | (u16)(((u16)(data[HID_IDX_BYTE21] & 0xC0)) << 2)));
    p_status->exp[3].pixel = (u16)(((s16)((s16)(((u16)(data[HID_IDX_BYTE26]) << 8) & 0xFF00) | (u16)(data[HID_IDX_BYTE27] & 0xFF))) * 64);
    p_status->exp[3].radius = (s8)((data[HID_IDX_BYTE21] & 0x0f));
    p_status->exp[3].range_x1 = (s16)(((s8)(data[HID_IDX_BYTE22]) == -1) ? 0 : data[HID_IDX_BYTE22]);
    p_status->exp[3].range_y1 = (s16)(((s8)(data[HID_IDX_BYTE23]) == -1) ? 0 : data[HID_IDX_BYTE23]);
    p_status->exp[3].range_x2 = (s16)(((s8)(data[HID_IDX_BYTE24]) == -1) ? 0 : data[HID_IDX_BYTE24]);
    p_status->exp[3].range_y2 = (s16)(((s8)(data[HID_IDX_BYTE25]) == -1) ? 0 : data[HID_IDX_BYTE25]);
    p_status->exp[3].range_x1 = (s16)(p_status->exp[3].range_x1 * 8);
    p_status->exp[3].range_y1 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[3].range_y1 * 8));
    p_status->exp[3].range_x2 = (s16)(p_status->exp[3].range_x2 * 8);
    p_status->exp[3].range_y2 = (s16)((s16)(WPAD_DPD_IMG_RESO_WY - 1) - (s16)(p_status->exp[3].range_y2 * 8));
    p_status->base.obj[3].size = (u16)((f32)p_status->exp[3].radius * (f32)p_status->exp[3].radius * PI);
    if (p_status->base.obj[3].size == 0 || p_status->base.obj[3].x == (s16)(WPAD_DPD_IMG_RESO_WX - 1) ||
        p_status->base.obj[3].y == (s16)(WPAD_DPD_IMG_RESO_WY - 1) || p_status->exp[3].radius == 0xf) {
        p_status->base.obj[3].x = (s16)0;
        p_status->base.obj[3].y = (s16)(WPAD_DPD_IMG_RESO_WY - 1);
        p_status->base.obj[3].size = 0;
        p_status->exp[3].pixel = 0;
        p_status->exp[3].radius = 0;
    }

    p_status->base.obj[3].traceId = 3;
    old = OSDisableInterrupts();
    _recv_3f[chan] = 1;
    if (_recv_3e[chan] && _recv_3f[chan]) {
        p_status->base.accZ -= p_cb->devConf.acc_0g.z;
        p_cb->rxBufIndex = (u8)((p_cb->rxBufIndex) ? 0 : 1);
        _recv_3e[chan] = _recv_3f[chan] = 0;
    }
    OSRestoreInterrupts(old);
}

void __a1_unused_report(u8 chan, u8* data) {}
