/***************************************************************************

  Joystick.c

 ***************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <mmsystem.h>
#include <assert.h>
#include "win32.h"
#include "Joystick.h"
#include "misc.h"
#include "mouse.h"
#include "input.h"
#ifdef MAME32JP
#ifdef __GNUC__
#include "mmjoyfix.h"
#endif
#endif

#define MAX_JOY              256
#define MAX_JOY_NAME_LEN     20

#define OSD_ANALOGMAX       ( 127.0)
#define OSD_ANALOGMIN       (-128.0)

#define MAX_PHYSICAL_JOYSTICKS  4

#define MAX_PLAYERS         4

/* Not sure if this is supported via the Multimedia API. */
#ifndef JOYSTICKID3
#define JOYSTICKID3 2
#endif
#ifndef JOYSTICKID4
#define JOYSTICKID4 3
#endif

/***************************************************************************
    function prototypes
 ***************************************************************************/

static int              Joystick_init(options_type *osd_options);
static void             Joystick_exit(void);
static void             Joystick_poll_joysticks(void);
static const struct JoystickInfo *Joystick_get_joy_list(void);
static int              Joystick_is_joy_pressed(int joycode);
static void             Joystick_analogjoy_read(int player, int *analog_x, int *analog_y);
static int              Joystick_standard_analog_read(int player, int axis);
static void             Joystick_get_device_numbers(int *buf);
static void             Joystick_set_device_numbers(int *buf);
static const char       *Joystick_get_device_name(int number);
static BOOL             Joystick_Available(void);
static BOOL             Joystick_OnMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT* pResult);

static DWORD            Joystick_DeadZoneMin(DWORD dwMin, DWORD dwMax, UINT nDeadZone);
static DWORD            Joystick_DeadZoneMax(DWORD dwMin, DWORD dwMax, UINT nDeadZone);
static void             Joystick_InitJoyList(void);

#define SetDeadZone(Joy, Axis, axis)                             \
Joy.##axis##_dzmin = Joystick_DeadZoneMin(Joy.caps.w##Axis##min, \
                                          Joy.caps.w##Axis##max, \
                                          Joy.dead_zone);        \
Joy.##axis##_dzmax = Joystick_DeadZoneMax(Joy.caps.w##Axis##min, \
                                          Joy.caps.w##Axis##max, \
                                          Joy.dead_zone);

/***************************************************************************
    External variables
 ***************************************************************************/

struct OSDJoystick Joystick =
{
    Joystick_init,                  /* init                 */
    Joystick_exit,                  /* exit                 */
    Joystick_get_joy_list,          /* get_joy_list         */
    Joystick_is_joy_pressed,        /* is_joy_pressed       */
    Joystick_poll_joysticks,        /* poll_joysticks       */
    Joystick_analogjoy_read,        /* analogjoy_read       */
    Joystick_standard_analog_read,  /* standard_analog_read */
    Joystick_get_device_numbers,    /* get_device_numbers   */
    Joystick_set_device_numbers,    /* set_device_numbers   */
    Joystick_get_device_name,       /* get_device_name      */
    Joystick_Available,             /* Available            */
    Joystick_OnMessage,             /* OnMessage            */
};

/***************************************************************************
    Internal structures
 ***************************************************************************/

typedef struct
{
    BOOL        use_joystick;
    UINT        joyid;

    JOYINFOEX   info;
    JOYCAPS     caps;

    UINT        dead_zone;
    DWORD       x_dzmin;
    DWORD       x_dzmax;
    DWORD       y_dzmin;
    DWORD       y_dzmax;
    DWORD       z_dzmin;
    DWORD       z_dzmax;
    DWORD       r_dzmin;
    DWORD       r_dzmax;
    DWORD       u_dzmin;
    DWORD       u_dzmax;
    DWORD       v_dzmin;
    DWORD       v_dzmax;

    double      x_scale;
    double      y_scale;
} mmjoystick;

/***************************************************************************
    Internal variables
 ***************************************************************************/

static int         num_joysticks;
static mmjoystick  *joysticks[MAX_PLAYERS];
static mmjoystick  mmjoysticks[MAX_PHYSICAL_JOYSTICKS];

static struct JoystickInfo joylist[MAX_JOY] =
{
    /* will be filled later */
    { 0, 0, 0 } /* end of table */
};

static const UINT g_nJoyID[MAX_PHYSICAL_JOYSTICKS] =
{
    JOYSTICKID1,
    JOYSTICKID2,
    JOYSTICKID3,
    JOYSTICKID4
};

/***************************************************************************
    External OSD functions
 ***************************************************************************/

static int Joystick_init(options_type *osd_options)
{
    int       i;
    MMRESULT  mmResult;

    num_joysticks = 0;

    for (i = 0; i < MAX_PLAYERS; i++)
        joysticks[i] = NULL;

    memset(&mmjoysticks, 0, sizeof(mmjoystick) * MAX_PHYSICAL_JOYSTICKS);

    for (i = 0; i < MAX_PHYSICAL_JOYSTICKS; i++)
    {
        mmjoysticks[i].use_joystick = FALSE;
        mmjoysticks[i].dead_zone    = 75;
        mmjoysticks[i].joyid        = g_nJoyID[i];
        mmjoysticks[i].info.dwSize  = sizeof(JOYINFOEX);
        mmjoysticks[i].info.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNX | JOY_RETURNY;

        mmResult = joyGetPosEx(mmjoysticks[i].joyid, &mmjoysticks[i].info);

        if (mmResult == JOYERR_NOERROR)
        {
            joyGetDevCaps(mmjoysticks[i].joyid, &mmjoysticks[i].caps, sizeof(JOYCAPS));

            SetDeadZone(mmjoysticks[i], X, x);
            SetDeadZone(mmjoysticks[i], Y, y);

            mmjoysticks[i].x_scale = ((OSD_ANALOGMAX - OSD_ANALOGMIN) / (mmjoysticks[i].caps.wXmax - mmjoysticks[i].caps.wXmin));
            mmjoysticks[i].y_scale = ((OSD_ANALOGMAX - OSD_ANALOGMIN) / (mmjoysticks[i].caps.wYmax - mmjoysticks[i].caps.wYmin));

            /* Check for other axis */
            if (mmjoysticks[i].caps.wCaps & JOYCAPS_HASZ)
            {
                mmjoysticks[i].info.dwFlags |= JOY_RETURNZ;
                SetDeadZone(mmjoysticks[i], Z, z);
            }

            if (mmjoysticks[i].caps.wCaps & JOYCAPS_HASR)
            {
                mmjoysticks[i].info.dwFlags |= JOY_RETURNR;
                SetDeadZone(mmjoysticks[i], R, r);
            }

            if (mmjoysticks[i].caps.wCaps & JOYCAPS_HASU)
            {
                mmjoysticks[i].info.dwFlags |= JOY_RETURNU;
                SetDeadZone(mmjoysticks[i], U, u);
            }

            if (mmjoysticks[i].caps.wCaps & JOYCAPS_HASV)
            {
                mmjoysticks[i].info.dwFlags |= JOY_RETURNV;
                SetDeadZone(mmjoysticks[i], V, v);
            }

            if (mmjoysticks[i].caps.wCaps & JOYCAPS_HASPOV
            &&  mmjoysticks[i].caps.wCaps & JOYCAPS_POV4DIR)
            {
                mmjoysticks[i].info.dwFlags |= JOY_RETURNPOV;
            }

            mmjoysticks[i].use_joystick = TRUE;

            num_joysticks++;
        }
    }

    if (osd_options)
    {
        if (osd_options->use_joystick)
        {
            for (i = 0; i < MAX_PLAYERS; i++)
            {
                if (osd_options->joyid[i] > 3)
                    continue;

                if (mmjoysticks[osd_options->joyid[i]].use_joystick == TRUE)
                    joysticks[i] = &mmjoysticks[osd_options->joyid[i]];
            }
        }
    }
    else
    {
        if (mmjoysticks[0].use_joystick == TRUE)
            joysticks[0] = &mmjoysticks[0];
    }

    Joystick_InitJoyList();

    Joystick_poll_joysticks();

    return 0;
}

static void Joystick_exit(void)
{
}

static const struct JoystickInfo *Joystick_get_joy_list(void)
{
    return joylist;
}

static void Joystick_poll_joysticks(void)
{
    int i;

    if (num_joysticks == 0)
        return;

    for (i = 0; i < MAX_PLAYERS; i++)
    {
        if (joysticks[i] != NULL)
            joyGetPosEx(joysticks[i]->joyid, &joysticks[i]->info);
    }
}


int Joystick_is_joy_pressed(int joycode)
{
    static DWORD joyButtonCode[] =
    {
        JOY_BUTTON1,  JOY_BUTTON2,  JOY_BUTTON3,  JOY_BUTTON4,
        JOY_BUTTON5,  JOY_BUTTON6,  JOY_BUTTON7,  JOY_BUTTON8,
        JOY_BUTTON9,  JOY_BUTTON10, JOY_BUTTON11, JOY_BUTTON12,
        JOY_BUTTON13, JOY_BUTTON14, JOY_BUTTON15, JOY_BUTTON16,
        JOY_BUTTON17, JOY_BUTTON18, JOY_BUTTON19, JOY_BUTTON20,
        JOY_BUTTON21, JOY_BUTTON22, JOY_BUTTON23, JOY_BUTTON24,
        JOY_BUTTON25, JOY_BUTTON26, JOY_BUTTON27, JOY_BUTTON28,
        JOY_BUTTON29, JOY_BUTTON30, JOY_BUTTON31, JOY_BUTTON32
    };
    static DWORD joyPOVCode[] =
    {
        JOY_POVFORWARD,
        JOY_POVRIGHT,
        JOY_POVBACKWARD,
        JOY_POVLEFT
    };
    int joy_num, stick, axis, dir;

    joy_num = GET_JOYCODE_JOY(joycode);

    /* do we have as many sticks? */
    if (joy_num == 0 || num_joysticks < joy_num)
        return 0;
    joy_num--;

    if (joysticks[joy_num] == NULL)
        return 0;

    stick = GET_JOYCODE_STICK(joycode);

    if (stick == JOYCODE_STICK_BTN)
    {
        /* buttons */
        int button;

        button = GET_JOYCODE_BUTTON(joycode);
        button--;

        if (button >= joysticks[joy_num]->caps.wNumButtons
        ||  GET_JOYCODE_DIR(joycode) != JOYCODE_DIR_BTN)
            return 0;

        return joysticks[joy_num]->info.dwButtons & joyButtonCode[button];
    }

    if (stick == JOYCODE_STICK_POV)
    {
        /* POV */
        int   button;
        DWORD pov;

        button = GET_JOYCODE_BUTTON(joycode);
        if (button == 0 || 4 < button)
            return 0;
        button--;

        pov = joysticks[joy_num]->info.dwPOV;

        if (pov == JOY_POVCENTERED)
            return 0;

        if (pov == 4500)
        {
            if (joyPOVCode[button] == JOY_POVFORWARD
            ||  joyPOVCode[button] == JOY_POVRIGHT)
                return TRUE;
        }
        else
        if (pov == 13500)
        {
            if (joyPOVCode[button] == JOY_POVBACKWARD
            ||  joyPOVCode[button] == JOY_POVRIGHT)
                return TRUE;
        }
        else
        if (pov == 22500)
        {
            if (joyPOVCode[button] == JOY_POVBACKWARD
            ||  joyPOVCode[button] == JOY_POVLEFT)
                return TRUE;
        }
        else
        if (pov == 31500)
        {
            if (joyPOVCode[button] == JOY_POVFORWARD
            ||  joyPOVCode[button] == JOY_POVLEFT)
                return TRUE;
        }

        return pov == joyPOVCode[button];
    }

    /* sticks */
    axis = GET_JOYCODE_AXIS(joycode);
    dir  = GET_JOYCODE_DIR(joycode);

    if (axis == 0 || joysticks[joy_num]->caps.wNumAxes < axis)
        return 0;
    axis--;

    switch (axis)
    {
    case 0: /* X */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwXpos <= joysticks[joy_num]->x_dzmin;
        else
            return joysticks[joy_num]->info.dwXpos >= joysticks[joy_num]->x_dzmax;

    case 1: /* Y */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwYpos <= joysticks[joy_num]->y_dzmin;
        else
            return joysticks[joy_num]->info.dwYpos >= joysticks[joy_num]->y_dzmax;

    case 2: /* Z */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwZpos <= joysticks[joy_num]->z_dzmin;
        else
            return joysticks[joy_num]->info.dwZpos >= joysticks[joy_num]->z_dzmax;

    case 3: /* R */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwRpos <= joysticks[joy_num]->r_dzmin;
        else
            return joysticks[joy_num]->info.dwRpos >= joysticks[joy_num]->r_dzmax;

    case 4: /* U */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwUpos <= joysticks[joy_num]->u_dzmin;
        else
            return joysticks[joy_num]->info.dwUpos >= joysticks[joy_num]->u_dzmax;

    case 5: /* V */
        if (dir == JOYCODE_DIR_NEG)
            return joysticks[joy_num]->info.dwVpos <= joysticks[joy_num]->v_dzmin;
        else
            return joysticks[joy_num]->info.dwVpos >= joysticks[joy_num]->v_dzmax;
    }

    return 0;
}

static void Joystick_analogjoy_read(int player, int *analog_x, int *analog_y)
{
    *analog_x = *analog_y = 0;

    if (num_joysticks == 0)
        return;

    if (num_joysticks <= player)
        return;

    if (joysticks[player] == NULL)
        return;

    *analog_x = (int)((joysticks[player]->info.dwXpos - joysticks[player]->caps.wXmin) * (joysticks[player]->x_scale) - 128.0);
    *analog_y = (int)((joysticks[player]->info.dwYpos - joysticks[player]->caps.wYmin) * (joysticks[player]->y_scale) - 128.0);
}

static int Joystick_standard_analog_read(int player, int axis)
{
    return 0;
}

static void Joystick_get_device_numbers(int *buf)
{
    int i, j;

    for (i = 0; i < MAX_PLAYERS; i++)
    {
        buf[i] = -1;

        if (joysticks[i] == NULL)
            continue;

        for (j = 0; j < num_joysticks; j++)
        {
            if (joysticks[i] == &mmjoysticks[j])
            {
                buf[i] = j;
                break;
            }
        }
    }
}

static void Joystick_set_device_numbers(int *buf)
{
    int i;

    for (i = 0; i < MAX_PLAYERS; i++)
    {
        if (buf[i] == -1)
        {
            joysticks[i] = NULL;
            continue;
        }

        if (mmjoysticks[buf[i]].use_joystick == TRUE)
            joysticks[i] = &mmjoysticks[buf[i]];
        else
            joysticks[i] = NULL;
    }

    Joystick_InitJoyList();
}

static const char *Joystick_get_device_name(int number)
{
#ifdef JAPANESE
    static char *not_found = "なし";
#else
    static char *not_found = "Not Found";
#endif

    if (number < 0 || number >= MAX_PHYSICAL_JOYSTICKS)
        return not_found;

    if (mmjoysticks[number].use_joystick == TRUE)
        return mmjoysticks[number].caps.szPname;
    else
        return not_found;
}

static BOOL Joystick_Available(void)
{
    MMRESULT  mmResult;
    JOYINFOEX JoyInfoEx;
    int       i;

    memset(&JoyInfoEx, 0, sizeof(JoyInfoEx));
    JoyInfoEx.dwSize  = sizeof(JOYINFOEX);
    JoyInfoEx.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNX | JOY_RETURNY;

    for (i = 0; i < MAX_PHYSICAL_JOYSTICKS; i++)
    {
        mmResult = joyGetPosEx(g_nJoyID[i], &JoyInfoEx);
        if (mmResult == JOYERR_NOERROR)
            return TRUE;
    }

    return FALSE;
}

static BOOL Joystick_OnMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
   return FALSE;
}

/***************************************************************************
    Internal functions
 ***************************************************************************/

static void Joystick_InitJoyList(void)
{
    static int joyequiv[][2] =
    {
        { JOYCODE(1, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_NEG), JOYCODE_1_LEFT    },
        { JOYCODE(1, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_POS), JOYCODE_1_RIGHT   },
        { JOYCODE(1, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_NEG), JOYCODE_1_UP      },
        { JOYCODE(1, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_POS), JOYCODE_1_DOWN    },

        { JOYCODE(1, JOYCODE_STICK_BTN,  1, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON1 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  2, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON2 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  3, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON3 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  4, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON4 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  5, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON5 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  6, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON6 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  7, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON7 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  8, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON8 },
        { JOYCODE(1, JOYCODE_STICK_BTN,  9, JOYCODE_DIR_BTN), JOYCODE_1_BUTTON9 },

        { JOYCODE(2, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_NEG), JOYCODE_2_LEFT    },
        { JOYCODE(2, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_POS), JOYCODE_2_RIGHT   },
        { JOYCODE(2, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_NEG), JOYCODE_2_UP      },
        { JOYCODE(2, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_POS), JOYCODE_2_DOWN    },

        { JOYCODE(2, JOYCODE_STICK_BTN,  1, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON1 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  2, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON2 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  3, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON3 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  4, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON4 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  5, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON5 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  6, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON6 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  7, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON7 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  8, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON8 },
        { JOYCODE(2, JOYCODE_STICK_BTN,  9, JOYCODE_DIR_BTN), JOYCODE_2_BUTTON9 },


        { JOYCODE(3, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_NEG), JOYCODE_3_LEFT    },
        { JOYCODE(3, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_POS), JOYCODE_3_RIGHT   },
        { JOYCODE(3, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_NEG), JOYCODE_3_UP      },
        { JOYCODE(3, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_POS), JOYCODE_3_DOWN    },

        { JOYCODE(3, JOYCODE_STICK_BTN,  1, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON1 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  2, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON2 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  3, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON3 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  4, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON4 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  5, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON5 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  6, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON6 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  7, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON7 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  8, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON8 },
        { JOYCODE(3, JOYCODE_STICK_BTN,  9, JOYCODE_DIR_BTN), JOYCODE_3_BUTTON9 },


        { JOYCODE(4, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_NEG), JOYCODE_4_LEFT    },
        { JOYCODE(4, JOYCODE_STICK_AXIS, 1, JOYCODE_DIR_POS), JOYCODE_4_RIGHT   },
        { JOYCODE(4, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_NEG), JOYCODE_4_UP      },
        { JOYCODE(4, JOYCODE_STICK_AXIS, 2, JOYCODE_DIR_POS), JOYCODE_4_DOWN    },

        { JOYCODE(4, JOYCODE_STICK_BTN,  1, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON1 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  2, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON2 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  3, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON3 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  4, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON4 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  5, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON5 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  6, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON6 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  7, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON7 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  8, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON8 },
        { JOYCODE(4, JOYCODE_STICK_BTN,  9, JOYCODE_DIR_BTN), JOYCODE_4_BUTTON9 },

        { 0, 0 }
    };
    static char joynames[MAX_JOY][MAX_JOY_NAME_LEN+1];  /* will be used to store names */
#ifdef JAPANESE
    static char* JoyAxisName[] = { "X軸", "Y軸", "Z軸",
                                   "R軸", "U軸", "V軸"};
    static char* JoyPOVName[] = { "POV 前",
                                  "POV 右",
                                  "POV 後",
                                  "POV 左" };
#else
    static char* JoyAxisName[] = { "X Axis", "Y Axis", "Z Axis",
                                   "R Axis", "U Axis", "V Axis"};
    static char* JoyPOVName[] = { "POV Forward",
                                  "POV Right",
                                  "POV Backward",
                                  "POV Left" };
#endif
    int tot, i, j;
    char buf[256];

    tot = 0;

    /* first of all, map mouse buttons */
    for (j = 0; j < 5; j++)
    {
#ifdef JAPANESE
        sprintf(buf, "マウス ボタン%d", j + 1);
#else
        sprintf(buf, "Mouse B%d", j + 1);
#endif
        strncpy(joynames[tot], buf, MAX_JOY_NAME_LEN);
        joynames[tot][MAX_JOY_NAME_LEN] = 0;
        joylist[tot].name = joynames[tot];
        joylist[tot].code = MOUSE_BUTTON(j + 1);
        tot++;
    }

    for (i = 0; i < MAX_PLAYERS; i++)
    {
        if (joysticks[i] == NULL)
            continue;

        for (j = 0; j < joysticks[i]->caps.wNumAxes; j++)
        {
            sprintf(buf, "J%d %.7s %s -",
                    i + 1,
                    joysticks[i]->caps.szPname,
                    JoyAxisName[j]);
            strncpy(joynames[tot], buf, MAX_JOY_NAME_LEN);
            joynames[tot][MAX_JOY_NAME_LEN] = 0;
            joylist[tot].name = joynames[tot];
            joylist[tot].code = JOYCODE(i + 1, JOYCODE_STICK_AXIS, j + 1, JOYCODE_DIR_NEG);
            tot++;

            sprintf(buf, "J%d %.7s %s +",
                    i + 1,
                    joysticks[i]->caps.szPname,
                    JoyAxisName[j]);
            strncpy(joynames[tot], buf, MAX_JOY_NAME_LEN);
            joynames[tot][MAX_JOY_NAME_LEN] = 0;
            joylist[tot].name = joynames[tot];
            joylist[tot].code = JOYCODE(i + 1, JOYCODE_STICK_AXIS, j + 1, JOYCODE_DIR_POS);
            tot++;
        }

        for (j = 0; j < joysticks[i]->caps.wNumButtons; j++)
        {
#ifdef JAPANESE
            sprintf(buf, "J%d ボタン %d", i + 1, j);
#else
            sprintf(buf, "J%d Button %d", i + 1, j);
#endif
            strncpy(joynames[tot], buf, MAX_JOY_NAME_LEN);
            joynames[tot][MAX_JOY_NAME_LEN] = 0;
            joylist[tot].name = joynames[tot];
            joylist[tot].code = JOYCODE(i + 1, JOYCODE_STICK_BTN, j + 1, JOYCODE_DIR_BTN);
            tot++;
        }

        if (joysticks[i]->caps.wCaps & JOYCAPS_POV4DIR)
        {
            for (j = 0; j < sizeof(JoyPOVName) / sizeof(JoyPOVName[0]); j++)
            {
                sprintf(buf, "J%d %s", i + 1, JoyPOVName[j]);
                strncpy(joynames[tot], buf, MAX_JOY_NAME_LEN);
                joynames[tot][MAX_JOY_NAME_LEN] = 0;
                joylist[tot].name = joynames[tot];
                joylist[tot].code = JOYCODE(i + 1, JOYCODE_STICK_POV, j + 1, JOYCODE_DIR_BTN);
                tot++;
            }
        }
    }

    /* terminate array */
    joylist[tot].name = 0;
    joylist[tot].code = 0;
    joylist[tot].standardcode = 0;

    /* fill in equivalences */
    for (i = 0; i < tot; i++)
    {
        joylist[i].standardcode = JOYCODE_OTHER;

        j = 0;
        while (joyequiv[j][0] != 0)
        {
            if (joyequiv[j][0] == joylist[i].code)
            {
                joylist[i].standardcode = joyequiv[j][1];
                break;
            }
            j++;
        }
    }
}

static DWORD Joystick_DeadZoneMin(DWORD dwMin, DWORD dwMax, UINT nDeadZone)
{
    return (DWORD)((dwMax - dwMin) * ((100.0 - nDeadZone) / 200.0));
}

static DWORD Joystick_DeadZoneMax(DWORD dwMin, DWORD dwMax, UINT nDeadZone)
{
    return (DWORD)((dwMax - dwMin) * ((100.0 + nDeadZone) / 200.0));
}
