//============================================================
//
//  input.c - Win32 implementation of MAME input routines
//
//  Copyright (c) 1996-2006, Nicola Salmoria and the MAME Team.
//  Visit http://mamedev.org for licensing and usage restrictions.
//
//============================================================

// Needed for RAW Input
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#endif

// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <conio.h>
#include <winioctl.h>
#include <ctype.h>
#include <stddef.h>

// undef WINNT for dinput.h to prevent duplicate definition
#undef WINNT
#include <dinput.h>

// MAME headers
#include "osdepend.h"
#include "driver.h"
#include "window.h"
#include "rc.h"
#include "input.h"
#include "debugwin.h"

#define ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP


//============================================================
//  IMPORTS
//============================================================

extern int verbose;
extern int win_physical_width;
extern int win_physical_height;
extern int win_window_mode;


//============================================================
//  PARAMETERS
//============================================================

#define MAX_KEYBOARDS		1
#define MAX_MICE			8
#define MAX_JOYSTICKS		8
#define MAX_LIGHTGUNS		8
#define MAX_DX_LIGHTGUNS	2

#define MAX_KEYS			256

#define MAX_JOY				512
#define MAX_AXES			8
#define MAX_LIGHTGUN_AXIS	2
#define MAX_BUTTONS			32
#define MAX_POV				4

#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
#define MAX_MOUSEAXES		3		//	Mouse is (X,Y,Z)
#endif /* USE_JOY_MOUSE_MOVE */


#define HISTORY_LENGTH		16

enum
{
	ANALOG_TYPE_PADDLE = 0,
	ANALOG_TYPE_ADSTICK,
	ANALOG_TYPE_LIGHTGUN,
	ANALOG_TYPE_PEDAL,
	ANALOG_TYPE_DIAL,
	ANALOG_TYPE_TRACKBALL,
#ifdef MESS
	ANALOG_TYPE_MOUSE,
#endif // MESS
	ANALOG_TYPE_COUNT
};

enum
{
	SELECT_TYPE_KEYBOARD = 0,
	SELECT_TYPE_MOUSE,
	SELECT_TYPE_JOYSTICK,
	SELECT_TYPE_LIGHTGUN
};

enum
{
	AXIS_TYPE_INVALID = 0,
	AXIS_TYPE_DIGITAL,
	AXIS_TYPE_ANALOG
};



//============================================================
//  MACROS
//============================================================

#define STRUCTSIZE(x)		((dinput_version == 0x0300) ? sizeof(x##_DX3) : sizeof(x))

#define ELEMENTS(x)			(sizeof(x) / sizeof((x)[0]))



//============================================================
//  TYPEDEFS
//============================================================

typedef struct _axis_history axis_history;
struct _axis_history
{
	LONG		value;
	INT32		count;
};

typedef struct _raw_mouse raw_mouse;
struct _raw_mouse
{
	// Identifier for the mouse.
	// WM_INPUT passes the device HANDLE as lparam when registering a mousemove
	HANDLE			device_handle;

	// Current mouse axis and button info
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
	DIMOUSESTATE2	mouse_state;
#else /* USE_JOY_MOUSE_MOVE */
	DIMOUSESTATE	mouse_state;
#endif /* USE_JOY_MOUSE_MOVE */

	// Used to determine if the HID is using absolute mode or relative mode
	USHORT			flags;
};


//============================================================
//  GLOBAL VARIABLES
//============================================================




//============================================================
//  LOCAL VARIABLES
//============================================================

// this will be filled in dynamically
static os_code_info	codelist[MAX_KEYS+MAX_JOY];
static int					total_codes;

// DirectInput variables
static LPDIRECTINPUT		dinput;
static int					dinput_version;

// global states
static int					input_paused;
static cycles_t				last_poll;

// Controller override options
static float				a2d_deadzone;
static int					use_mouse;
static int					use_joystick;
static int					use_lightgun;
static int					use_lightgun_dual;
static int					use_lightgun_reload;
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
static int					use_stickpoint;
#endif /* USE_JOY_MOUSE_MOVE */
static int					use_keyboard_leds;
static const char *			ledmode;
static int					steadykey;
static UINT8				analog_type[ANALOG_TYPE_COUNT];
static int					dummy[10];

// keyboard states
static int					keyboard_count;
static LPDIRECTINPUTDEVICE	keyboard_device[MAX_KEYBOARDS];
static LPDIRECTINPUTDEVICE2	keyboard_device2[MAX_KEYBOARDS];
static DIDEVCAPS			keyboard_caps[MAX_KEYBOARDS];
static BYTE					keyboard_state[MAX_KEYBOARDS][MAX_KEYS];

// additional key data
static INT8					oldkey[MAX_KEYS];
static INT8					currkey[MAX_KEYS];

// mouse states
static int					mouse_active;
static int					mouse_count;
static LPDIRECTINPUTDEVICE	mouse_device[MAX_MICE+1];
static LPDIRECTINPUTDEVICE2	mouse_device2[MAX_MICE+1];
static raw_mouse			raw_mouse_device[MAX_MICE];
static DIDEVCAPS			mouse_caps[MAX_MICE+1];
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
static DIMOUSESTATE2			mouse_state[MAX_MICE];
#else /* USE_JOY_MOUSE_MOVE */
static DIMOUSESTATE			mouse_state[MAX_MICE];
#endif /* USE_JOY_MOUSE_MOVE */
static char					mouse_name[MAX_MICE+1][MAX_PATH];
static int					lightgun_count;
static POINT				lightgun_dual_player_pos[4];
static int					lightgun_dual_player_state[4];
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
static DIPROPRANGE			mouse_range[MAX_MICE][MAX_AXES];
#endif /* USE_JOY_MOUSE_MOVE */

// joystick states
static int					joystick_count;
static LPDIRECTINPUTDEVICE	joystick_device[MAX_JOYSTICKS];
static LPDIRECTINPUTDEVICE2	joystick_device2[MAX_JOYSTICKS];
static DIDEVCAPS			joystick_caps[MAX_JOYSTICKS];
static DIJOYSTATE			joystick_state[MAX_JOYSTICKS];
static DIPROPRANGE			joystick_range[MAX_JOYSTICKS][MAX_AXES];
static UINT8				joystick_digital[MAX_JOYSTICKS][MAX_AXES];
static char					joystick_name[MAX_JOYSTICKS][MAX_PATH];
static axis_history			joystick_history[MAX_JOYSTICKS][MAX_AXES][HISTORY_LENGTH];
static UINT8				joystick_type[MAX_JOYSTICKS][MAX_AXES];
#ifdef JOYSTICK_ID
static int					joyid[8];
#endif /* JOYSTICK_ID */

// gun states
static INT32				gun_axis[MAX_DX_LIGHTGUNS][2];

// led states
static int					original_leds;
static HANDLE				hKbdDev;
static int					ledmethod;



//============================================================
//  OPTIONS
//============================================================

// prototypes
static int decode_ledmode(struct rc_option *option, const char *arg, int priority);
static int decode_analog_select(struct rc_option *option, const char *arg, int priority);
static int decode_digital(struct rc_option *option, const char *arg, int priority);

// global input options
struct rc_option input_opts[] =
{
	/* name, shortname, type, dest, deflt, min, max, func, help */
	{ "Input device options", NULL, rc_seperator, NULL, NULL, 0, 0, NULL, NULL },
	{ "mouse", NULL, rc_bool, &use_mouse, "0", 0, 0, NULL, "enable mouse input" },	//fixme: why not win_use_mouse
	{ "joystick", "joy", rc_bool, &use_joystick, "0", 0, 0, NULL, "enable joystick input" },
	{ "lightgun", "gun", rc_bool, &use_lightgun, "0", 0, 0, NULL, "enable lightgun input" },
	{ "dual_lightgun", "dual", rc_bool, &use_lightgun_dual, "0", 0, 0, NULL, "enable dual lightgun input" },
	{ "offscreen_reload", "reload", rc_bool, &use_lightgun_reload, "0", 0, 0, NULL, "offscreen shots reload" },
	{ "steadykey", "steady", rc_bool, &steadykey, "0", 0, 0, NULL, "enable steadykey support" },
	{ "keyboard_leds", "leds", rc_bool, &use_keyboard_leds, "1", 0, 0, NULL, "enable keyboard LED emulation" },
	{ "led_mode", NULL, rc_string, &ledmode, "ps/2", 0, 0, decode_ledmode, "LED mode (ps/2|usb)" },
	{ "a2d_deadzone", "a2d", rc_float, &a2d_deadzone, "0.3", 0.0, 1.0, NULL, "minimal analog value for digital input" },
	{ "ctrlr", NULL, rc_string, &options.controller, "Standard", 0, 0, NULL, "preconfigure for specified controller" },
#ifdef USE_JOY_MOUSE_MOVE
	// Support Stick-type Pointing Device (miko2u@hotmail.com)
 	{ "stickpoint", NULL, rc_bool, &use_stickpoint, "0", 0, 0, NULL, "enable pointing stick input" },
#endif /* USE_JOY_MOUSE_MOVE */
#ifdef JOYSTICK_ID
	{ "joyid1", NULL, rc_int, &joyid[0], "0", 0, 0, NULL, "set joystick ID (Player1)" },
	{ "joyid2", NULL, rc_int, &joyid[1], "1", 0, 0, NULL, "set joystick ID (Player2)" },
	{ "joyid3", NULL, rc_int, &joyid[2], "2", 0, 0, NULL, "set joystick ID (Player3)" },
	{ "joyid4", NULL, rc_int, &joyid[3], "3", 0, 0, NULL, "set joystick ID (Player4)" },
	{ "joyid5", NULL, rc_int, &joyid[4], "4", 0, 0, NULL, "set joystick ID (Player5)" },
	{ "joyid6", NULL, rc_int, &joyid[5], "5", 0, 0, NULL, "set joystick ID (Player6)" },
	{ "joyid7", NULL, rc_int, &joyid[6], "6", 0, 0, NULL, "set joystick ID (Player7)" },
	{ "joyid8", NULL, rc_int, &joyid[7], "7", 0, 0, NULL, "set joystick ID (Player8)" },
#endif /* JOYSTICK_ID */
	{ "paddle_device", "paddle", rc_string, &dummy[0], "keyboard", ANALOG_TYPE_PADDLE, 0, decode_analog_select, "enable (keyboard|mouse|joystick) if a paddle control is present" },
	{ "adstick_device", "adstick", rc_string, &dummy[1], "keyboard", ANALOG_TYPE_ADSTICK, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if an analog joystick control is present" },
	{ "pedal_device", "pedal", rc_string, &dummy[2], "keyboard", ANALOG_TYPE_PEDAL, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if a pedal control is present" },
	{ "dial_device", "dial", rc_string, &dummy[3], "keyboard", ANALOG_TYPE_DIAL, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if a dial control is present" },
	{ "trackball_device", "trackball", rc_string, &dummy[4], "keyboard", ANALOG_TYPE_TRACKBALL, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if a trackball control is present" },
	{ "lightgun_device", NULL, rc_string, &dummy[5], "keyboard", ANALOG_TYPE_LIGHTGUN, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if a lightgun control is present" },
#ifdef MESS
	{ "mouse_device", NULL, rc_string, &dummy[6], "mouse", ANALOG_TYPE_MOUSE, 0, decode_analog_select, "enable (keyboard|mouse|joystick|lightgun) if a mouse control is present" },
#endif // MESS
	{ "digital", NULL, rc_string, &dummy[7], "none", 1, 0, decode_digital, "mark certain joysticks or axes as digital (none|all|j<N>*|j<N>a<M>[,...])" },
	{ NULL,	NULL, rc_end, NULL, NULL, 0, 0,	NULL, NULL }
};



//============================================================
//  PROTOTYPES
//============================================================

static void updatekeyboard(void);
static void update_joystick_axes(void);
static void init_keycodes(void);
static void init_joycodes(void);
static void poll_lightguns(void);
static BOOL is_rm_rdp_mouse(const char *device_string);
static void process_raw_input(PRAWINPUT raw);
static BOOL register_raw_mouse(void);
static BOOL init_raw_mouse(void);
static void win_read_raw_mouse(void);



//============================================================
//  Dynamically linked functions from rawinput
//============================================================

typedef WINUSERAPI INT (WINAPI *pGetRawInputDeviceList)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize);
typedef WINUSERAPI INT (WINAPI *pGetRawInputData)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader);
typedef WINUSERAPI INT (WINAPI *pGetRawInputDeviceInfoA)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize);
typedef WINUSERAPI BOOL (WINAPI *pRegisterRawInputDevices)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);

static pGetRawInputDeviceList _GetRawInputDeviceList;
static pGetRawInputData _GetRawInputData;
static pGetRawInputDeviceInfoA _GetRawInputDeviceInfoA;
static pRegisterRawInputDevices _RegisterRawInputDevices;



//============================================================
//  KEYBOARD/JOYSTICK LIST
//============================================================

// macros for building/mapping keyboard codes
#define KEYCODE(dik, vk, ascii)		((dik) | ((vk) << 8) | ((ascii) << 16))
#define DICODE(keycode)				((keycode) & 0xff)
#define VKCODE(keycode)				(((keycode) >> 8) & 0xff)
#define ASCIICODE(keycode)			(((keycode) >> 16) & 0xff)

// macros for building/mapping joystick codes
#define JOYCODE(joy, type, index)	((index) | ((type) << 8) | ((joy) << 12) | 0x80000000)
#define JOYINDEX(joycode)			((joycode) & 0xff)
#define CODETYPE(joycode)			(((joycode) >> 8) & 0xf)
#define JOYNUM(joycode)				(((joycode) >> 12) & 0xf)

// macros for differentiating the two
#define IS_KEYBOARD_CODE(code)		(((code) & 0x80000000) == 0)
#define IS_JOYSTICK_CODE(code)		(((code) & 0x80000000) != 0)

// joystick types
#define CODETYPE_KEYBOARD			0
#define CODETYPE_AXIS_NEG			1
#define CODETYPE_AXIS_POS			2
#define CODETYPE_POV_UP				3
#define CODETYPE_POV_DOWN			4
#define CODETYPE_POV_LEFT			5
#define CODETYPE_POV_RIGHT			6
#define CODETYPE_BUTTON				7
#define CODETYPE_JOYAXIS			8
#define CODETYPE_MOUSEAXIS			9
#define CODETYPE_MOUSEBUTTON		10
#define CODETYPE_GUNAXIS			11
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
#define CODETYPE_MOUSEAXIS_NEG		12
#define CODETYPE_MOUSEAXIS_POS		13
#endif /* USE_JOY_MOUSE_MOVE */

// master keyboard translation table
const int win_key_trans_table[][4] =
{
	// MAME key             dinput key          virtual key     ascii
	{ KEYCODE_ESC, 			DIK_ESCAPE,			VK_ESCAPE,	 	27 },
	{ KEYCODE_1, 			DIK_1,				'1',			'1' },
	{ KEYCODE_2, 			DIK_2,				'2',			'2' },
	{ KEYCODE_3, 			DIK_3,				'3',			'3' },
	{ KEYCODE_4, 			DIK_4,				'4',			'4' },
	{ KEYCODE_5, 			DIK_5,				'5',			'5' },
	{ KEYCODE_6, 			DIK_6,				'6',			'6' },
	{ KEYCODE_7, 			DIK_7,				'7',			'7' },
	{ KEYCODE_8, 			DIK_8,				'8',			'8' },
	{ KEYCODE_9, 			DIK_9,				'9',			'9' },
	{ KEYCODE_0, 			DIK_0,				'0',			'0' },
	{ KEYCODE_MINUS, 		DIK_MINUS, 			0xbd,			'-' },
	{ KEYCODE_EQUALS, 		DIK_EQUALS,		 	0xbb,			'=' },
	{ KEYCODE_BACKSPACE,	DIK_BACK, 			VK_BACK, 		8 },
	{ KEYCODE_TAB, 			DIK_TAB, 			VK_TAB, 		9 },
	{ KEYCODE_Q, 			DIK_Q,				'Q',			'Q' },
	{ KEYCODE_W, 			DIK_W,				'W',			'W' },
	{ KEYCODE_E, 			DIK_E,				'E',			'E' },
	{ KEYCODE_R, 			DIK_R,				'R',			'R' },
	{ KEYCODE_T, 			DIK_T,				'T',			'T' },
	{ KEYCODE_Y, 			DIK_Y,				'Y',			'Y' },
	{ KEYCODE_U, 			DIK_U,				'U',			'U' },
	{ KEYCODE_I, 			DIK_I,				'I',			'I' },
	{ KEYCODE_O, 			DIK_O,				'O',			'O' },
	{ KEYCODE_P, 			DIK_P,				'P',			'P' },
	{ KEYCODE_OPENBRACE,	DIK_LBRACKET, 		0xdb,			'[' },
	{ KEYCODE_CLOSEBRACE,	DIK_RBRACKET, 		0xdd,			']' },
	{ KEYCODE_ENTER, 		DIK_RETURN, 		VK_RETURN, 		13 },
	{ KEYCODE_LCONTROL, 	DIK_LCONTROL, 		VK_CONTROL, 	0 },
	{ KEYCODE_A, 			DIK_A,				'A',			'A' },
	{ KEYCODE_S, 			DIK_S,				'S',			'S' },
	{ KEYCODE_D, 			DIK_D,				'D',			'D' },
	{ KEYCODE_F, 			DIK_F,				'F',			'F' },
	{ KEYCODE_G, 			DIK_G,				'G',			'G' },
	{ KEYCODE_H, 			DIK_H,				'H',			'H' },
	{ KEYCODE_J, 			DIK_J,				'J',			'J' },
	{ KEYCODE_K, 			DIK_K,				'K',			'K' },
	{ KEYCODE_L, 			DIK_L,				'L',			'L' },
	{ KEYCODE_COLON, 		DIK_SEMICOLON,		0xba,			';' },
	{ KEYCODE_QUOTE, 		DIK_APOSTROPHE,		0xde,			'\'' },
	{ KEYCODE_TILDE, 		DIK_GRAVE, 			0xc0,			'`' },
	{ KEYCODE_LSHIFT, 		DIK_LSHIFT, 		VK_SHIFT, 		0 },
	{ KEYCODE_BACKSLASH,	DIK_BACKSLASH, 		0xdc,			'\\' },
	{ KEYCODE_Z, 			DIK_Z,				'Z',			'Z' },
	{ KEYCODE_X, 			DIK_X,				'X',			'X' },
	{ KEYCODE_C, 			DIK_C,				'C',			'C' },
	{ KEYCODE_V, 			DIK_V,				'V',			'V' },
	{ KEYCODE_B, 			DIK_B,				'B',			'B' },
	{ KEYCODE_N, 			DIK_N,				'N',			'N' },
	{ KEYCODE_M, 			DIK_M,				'M',			'M' },
	{ KEYCODE_COMMA, 		DIK_COMMA,			0xbc,			',' },
	{ KEYCODE_STOP, 		DIK_PERIOD, 		0xbe,			'.' },
	{ KEYCODE_SLASH, 		DIK_SLASH, 			0xbf,			'/' },
	{ KEYCODE_RSHIFT, 		DIK_RSHIFT, 		VK_SHIFT, 		0 },
	{ KEYCODE_ASTERISK, 	DIK_MULTIPLY, 		VK_MULTIPLY,	'*' },
	{ KEYCODE_LALT, 		DIK_LMENU, 			VK_MENU, 		0 },
	{ KEYCODE_SPACE, 		DIK_SPACE, 			VK_SPACE,		' ' },
	{ KEYCODE_CAPSLOCK, 	DIK_CAPITAL, 		VK_CAPITAL, 	0 },
	{ KEYCODE_F1, 			DIK_F1,				VK_F1, 			0 },
	{ KEYCODE_F2, 			DIK_F2,				VK_F2, 			0 },
	{ KEYCODE_F3, 			DIK_F3,				VK_F3, 			0 },
	{ KEYCODE_F4, 			DIK_F4,				VK_F4, 			0 },
	{ KEYCODE_F5, 			DIK_F5,				VK_F5, 			0 },
	{ KEYCODE_F6, 			DIK_F6,				VK_F6, 			0 },
	{ KEYCODE_F7, 			DIK_F7,				VK_F7, 			0 },
	{ KEYCODE_F8, 			DIK_F8,				VK_F8, 			0 },
	{ KEYCODE_F9, 			DIK_F9,				VK_F9, 			0 },
	{ KEYCODE_F10, 			DIK_F10,			VK_F10, 		0 },
	{ KEYCODE_NUMLOCK, 		DIK_NUMLOCK,		VK_NUMLOCK, 	0 },
	{ KEYCODE_SCRLOCK, 		DIK_SCROLL,			VK_SCROLL, 		0 },
	{ KEYCODE_7_PAD, 		DIK_NUMPAD7,		VK_NUMPAD7, 	0 },
	{ KEYCODE_8_PAD, 		DIK_NUMPAD8,		VK_NUMPAD8, 	0 },
	{ KEYCODE_9_PAD, 		DIK_NUMPAD9,		VK_NUMPAD9, 	0 },
	{ KEYCODE_MINUS_PAD,	DIK_SUBTRACT,		VK_SUBTRACT, 	0 },
	{ KEYCODE_4_PAD, 		DIK_NUMPAD4,		VK_NUMPAD4, 	0 },
	{ KEYCODE_5_PAD, 		DIK_NUMPAD5,		VK_NUMPAD5, 	0 },
	{ KEYCODE_6_PAD, 		DIK_NUMPAD6,		VK_NUMPAD6, 	0 },
	{ KEYCODE_PLUS_PAD, 	DIK_ADD,			VK_ADD, 		0 },
	{ KEYCODE_1_PAD, 		DIK_NUMPAD1,		VK_NUMPAD1, 	0 },
	{ KEYCODE_2_PAD, 		DIK_NUMPAD2,		VK_NUMPAD2, 	0 },
	{ KEYCODE_3_PAD, 		DIK_NUMPAD3,		VK_NUMPAD3, 	0 },
	{ KEYCODE_0_PAD, 		DIK_NUMPAD0,		VK_NUMPAD0, 	0 },
	{ KEYCODE_DEL_PAD, 		DIK_DECIMAL,		VK_DECIMAL, 	0 },
	{ KEYCODE_F11, 			DIK_F11,			VK_F11, 		0 },
	{ KEYCODE_F12, 			DIK_F12,			VK_F12, 		0 },
	{ KEYCODE_F13, 			DIK_F13,			VK_F13, 		0 },
	{ KEYCODE_F14, 			DIK_F14,			VK_F14, 		0 },
	{ KEYCODE_F15, 			DIK_F15,			VK_F15, 		0 },
	{ KEYCODE_ENTER_PAD,	DIK_NUMPADENTER,	VK_RETURN, 		0 },
	{ KEYCODE_RCONTROL, 	DIK_RCONTROL,		VK_CONTROL, 	0 },
	{ KEYCODE_SLASH_PAD,	DIK_DIVIDE,			VK_DIVIDE, 		0 },
	{ KEYCODE_PRTSCR, 		DIK_SYSRQ, 			0, 				0 },
	{ KEYCODE_RALT, 		DIK_RMENU,			VK_MENU, 		0 },
	{ KEYCODE_HOME, 		DIK_HOME,			VK_HOME, 		0 },
	{ KEYCODE_UP, 			DIK_UP,				VK_UP, 			0 },
	{ KEYCODE_PGUP, 		DIK_PRIOR,			VK_PRIOR, 		0 },
	{ KEYCODE_LEFT, 		DIK_LEFT,			VK_LEFT, 		0 },
	{ KEYCODE_RIGHT, 		DIK_RIGHT,			VK_RIGHT, 		0 },
	{ KEYCODE_END, 			DIK_END,			VK_END, 		0 },
	{ KEYCODE_DOWN, 		DIK_DOWN,			VK_DOWN, 		0 },
	{ KEYCODE_PGDN, 		DIK_NEXT,			VK_NEXT, 		0 },
	{ KEYCODE_INSERT, 		DIK_INSERT,			VK_INSERT, 		0 },
	{ KEYCODE_DEL, 			DIK_DELETE,			VK_DELETE, 		0 },
	{ KEYCODE_LWIN, 		DIK_LWIN,			VK_LWIN, 		0 },
	{ KEYCODE_RWIN, 		DIK_RWIN,			VK_RWIN, 		0 },
	{ KEYCODE_MENU, 		DIK_APPS,			VK_APPS, 		0 },
	{ KEYCODE_KANA,			DIK_KANA,			0x15,	 		0 },
	{ KEYCODE_CONVERT,		DIK_CONVERT,		0x1c,			0 },
	{ KEYCODE_NONCONVERT,	DIK_NOCONVERT,		0x1d,			0 },
	{ -1 }
};


// master joystick translation table
static int joy_trans_table[][2] =
{
	// internal code                    MAME code
	{ JOYCODE(0, CODETYPE_AXIS_NEG, 0),	JOYCODE_1_LEFT },
	{ JOYCODE(0, CODETYPE_AXIS_POS, 0),	JOYCODE_1_RIGHT },
	{ JOYCODE(0, CODETYPE_AXIS_NEG, 1),	JOYCODE_1_UP },
	{ JOYCODE(0, CODETYPE_AXIS_POS, 1),	JOYCODE_1_DOWN },
	{ JOYCODE(0, CODETYPE_BUTTON, 0),	JOYCODE_1_BUTTON1 },
	{ JOYCODE(0, CODETYPE_BUTTON, 1),	JOYCODE_1_BUTTON2 },
	{ JOYCODE(0, CODETYPE_BUTTON, 2),	JOYCODE_1_BUTTON3 },
	{ JOYCODE(0, CODETYPE_BUTTON, 3),	JOYCODE_1_BUTTON4 },
	{ JOYCODE(0, CODETYPE_BUTTON, 4),	JOYCODE_1_BUTTON5 },
	{ JOYCODE(0, CODETYPE_BUTTON, 5),	JOYCODE_1_BUTTON6 },
	{ JOYCODE(0, CODETYPE_BUTTON, 6),	JOYCODE_1_BUTTON7 },
	{ JOYCODE(0, CODETYPE_BUTTON, 7),	JOYCODE_1_BUTTON8 },
	{ JOYCODE(0, CODETYPE_BUTTON, 8),	JOYCODE_1_BUTTON9 },
	{ JOYCODE(0, CODETYPE_BUTTON, 9),	JOYCODE_1_BUTTON10 },
	{ JOYCODE(0, CODETYPE_BUTTON, 10),	JOYCODE_1_BUTTON11 },
	{ JOYCODE(0, CODETYPE_BUTTON, 11),	JOYCODE_1_BUTTON12 },
	{ JOYCODE(0, CODETYPE_BUTTON, 12),	JOYCODE_1_BUTTON13 },
	{ JOYCODE(0, CODETYPE_BUTTON, 13),	JOYCODE_1_BUTTON14 },
	{ JOYCODE(0, CODETYPE_BUTTON, 14),	JOYCODE_1_BUTTON15 },
	{ JOYCODE(0, CODETYPE_BUTTON, 15),	JOYCODE_1_BUTTON16 },
	{ JOYCODE(0, CODETYPE_BUTTON, 16),	JOYCODE_1_BUTTON17 },
	{ JOYCODE(0, CODETYPE_BUTTON, 17),	JOYCODE_1_BUTTON18 },
	{ JOYCODE(0, CODETYPE_BUTTON, 18),	JOYCODE_1_BUTTON19 },
	{ JOYCODE(0, CODETYPE_BUTTON, 19),	JOYCODE_1_BUTTON20 },
	{ JOYCODE(0, CODETYPE_BUTTON, 20),	JOYCODE_1_BUTTON21 },
	{ JOYCODE(0, CODETYPE_BUTTON, 21),	JOYCODE_1_BUTTON22 },
	{ JOYCODE(0, CODETYPE_BUTTON, 22),	JOYCODE_1_BUTTON23 },
	{ JOYCODE(0, CODETYPE_BUTTON, 23),	JOYCODE_1_BUTTON24 },
	{ JOYCODE(0, CODETYPE_BUTTON, 24),	JOYCODE_1_BUTTON25 },
	{ JOYCODE(0, CODETYPE_BUTTON, 25),	JOYCODE_1_BUTTON26 },
	{ JOYCODE(0, CODETYPE_BUTTON, 26),	JOYCODE_1_BUTTON27 },
	{ JOYCODE(0, CODETYPE_BUTTON, 27),	JOYCODE_1_BUTTON28 },
	{ JOYCODE(0, CODETYPE_BUTTON, 28),	JOYCODE_1_BUTTON29 },
	{ JOYCODE(0, CODETYPE_BUTTON, 29),	JOYCODE_1_BUTTON30 },
	{ JOYCODE(0, CODETYPE_BUTTON, 30),	JOYCODE_1_BUTTON31 },
	{ JOYCODE(0, CODETYPE_BUTTON, 31),	JOYCODE_1_BUTTON32 },
	{ JOYCODE(0, CODETYPE_JOYAXIS, 0),	JOYCODE_1_ANALOG_X },
	{ JOYCODE(0, CODETYPE_JOYAXIS, 1),	JOYCODE_1_ANALOG_Y },
	{ JOYCODE(0, CODETYPE_JOYAXIS, 2),	JOYCODE_1_ANALOG_Z },

	{ JOYCODE(1, CODETYPE_AXIS_NEG, 0),	JOYCODE_2_LEFT },
	{ JOYCODE(1, CODETYPE_AXIS_POS, 0),	JOYCODE_2_RIGHT },
	{ JOYCODE(1, CODETYPE_AXIS_NEG, 1),	JOYCODE_2_UP },
	{ JOYCODE(1, CODETYPE_AXIS_POS, 1),	JOYCODE_2_DOWN },
	{ JOYCODE(1, CODETYPE_BUTTON, 0),	JOYCODE_2_BUTTON1 },
	{ JOYCODE(1, CODETYPE_BUTTON, 1),	JOYCODE_2_BUTTON2 },
	{ JOYCODE(1, CODETYPE_BUTTON, 2),	JOYCODE_2_BUTTON3 },
	{ JOYCODE(1, CODETYPE_BUTTON, 3),	JOYCODE_2_BUTTON4 },
	{ JOYCODE(1, CODETYPE_BUTTON, 4),	JOYCODE_2_BUTTON5 },
	{ JOYCODE(1, CODETYPE_BUTTON, 5),	JOYCODE_2_BUTTON6 },
	{ JOYCODE(1, CODETYPE_BUTTON, 6),	JOYCODE_2_BUTTON7 },
	{ JOYCODE(1, CODETYPE_BUTTON, 7),	JOYCODE_2_BUTTON8 },
	{ JOYCODE(1, CODETYPE_BUTTON, 8),	JOYCODE_2_BUTTON9 },
	{ JOYCODE(1, CODETYPE_BUTTON, 9),	JOYCODE_2_BUTTON10 },
	{ JOYCODE(1, CODETYPE_BUTTON, 10),	JOYCODE_2_BUTTON11 },
	{ JOYCODE(1, CODETYPE_BUTTON, 11),	JOYCODE_2_BUTTON12 },
	{ JOYCODE(1, CODETYPE_BUTTON, 12),	JOYCODE_2_BUTTON13 },
	{ JOYCODE(1, CODETYPE_BUTTON, 13),	JOYCODE_2_BUTTON14 },
	{ JOYCODE(1, CODETYPE_BUTTON, 14),	JOYCODE_2_BUTTON15 },
	{ JOYCODE(1, CODETYPE_BUTTON, 15),	JOYCODE_2_BUTTON16 },
	{ JOYCODE(1, CODETYPE_BUTTON, 16),	JOYCODE_2_BUTTON17 },
	{ JOYCODE(1, CODETYPE_BUTTON, 17),	JOYCODE_2_BUTTON18 },
	{ JOYCODE(1, CODETYPE_BUTTON, 18),	JOYCODE_2_BUTTON19 },
	{ JOYCODE(1, CODETYPE_BUTTON, 19),	JOYCODE_2_BUTTON20 },
	{ JOYCODE(1, CODETYPE_BUTTON, 20),	JOYCODE_2_BUTTON21 },
	{ JOYCODE(1, CODETYPE_BUTTON, 21),	JOYCODE_2_BUTTON22 },
	{ JOYCODE(1, CODETYPE_BUTTON, 22),	JOYCODE_2_BUTTON23 },
	{ JOYCODE(1, CODETYPE_BUTTON, 23),	JOYCODE_2_BUTTON24 },
	{ JOYCODE(1, CODETYPE_BUTTON, 24),	JOYCODE_2_BUTTON25 },
	{ JOYCODE(1, CODETYPE_BUTTON, 25),	JOYCODE_2_BUTTON26 },
	{ JOYCODE(1, CODETYPE_BUTTON, 26),	JOYCODE_2_BUTTON27 },
	{ JOYCODE(1, CODETYPE_BUTTON, 27),	JOYCODE_2_BUTTON28 },
	{ JOYCODE(1, CODETYPE_BUTTON, 28),	JOYCODE_2_BUTTON29 },
	{ JOYCODE(1, CODETYPE_BUTTON, 29),	JOYCODE_2_BUTTON30 },
	{ JOYCODE(1, CODETYPE_BUTTON, 30),	JOYCODE_2_BUTTON31 },
	{ JOYCODE(1, CODETYPE_BUTTON, 31),	JOYCODE_2_BUTTON32 },
	{ JOYCODE(1, CODETYPE_JOYAXIS, 0),	JOYCODE_2_ANALOG_X },
	{ JOYCODE(1, CODETYPE_JOYAXIS, 1),	JOYCODE_2_ANALOG_Y },
	{ JOYCODE(1, CODETYPE_JOYAXIS, 2),	JOYCODE_2_ANALOG_Z },

	{ JOYCODE(2, CODETYPE_AXIS_NEG, 0),	JOYCODE_3_LEFT },
	{ JOYCODE(2, CODETYPE_AXIS_POS, 0),	JOYCODE_3_RIGHT },
	{ JOYCODE(2, CODETYPE_AXIS_NEG, 1),	JOYCODE_3_UP },
	{ JOYCODE(2, CODETYPE_AXIS_POS, 1),	JOYCODE_3_DOWN },
	{ JOYCODE(2, CODETYPE_BUTTON, 0),	JOYCODE_3_BUTTON1 },
	{ JOYCODE(2, CODETYPE_BUTTON, 1),	JOYCODE_3_BUTTON2 },
	{ JOYCODE(2, CODETYPE_BUTTON, 2),	JOYCODE_3_BUTTON3 },
	{ JOYCODE(2, CODETYPE_BUTTON, 3),	JOYCODE_3_BUTTON4 },
	{ JOYCODE(2, CODETYPE_BUTTON, 4),	JOYCODE_3_BUTTON5 },
	{ JOYCODE(2, CODETYPE_BUTTON, 5),	JOYCODE_3_BUTTON6 },
	{ JOYCODE(2, CODETYPE_BUTTON, 6),	JOYCODE_3_BUTTON7 },
	{ JOYCODE(2, CODETYPE_BUTTON, 7),	JOYCODE_3_BUTTON8 },
	{ JOYCODE(2, CODETYPE_BUTTON, 8),	JOYCODE_3_BUTTON9 },
	{ JOYCODE(2, CODETYPE_BUTTON, 9),	JOYCODE_3_BUTTON10 },
	{ JOYCODE(2, CODETYPE_BUTTON, 10),	JOYCODE_3_BUTTON11 },
	{ JOYCODE(2, CODETYPE_BUTTON, 11),	JOYCODE_3_BUTTON12 },
	{ JOYCODE(2, CODETYPE_BUTTON, 12),	JOYCODE_3_BUTTON13 },
	{ JOYCODE(2, CODETYPE_BUTTON, 13),	JOYCODE_3_BUTTON14 },
	{ JOYCODE(2, CODETYPE_BUTTON, 14),	JOYCODE_3_BUTTON15 },
	{ JOYCODE(2, CODETYPE_BUTTON, 15),	JOYCODE_3_BUTTON16 },
	{ JOYCODE(2, CODETYPE_BUTTON, 16),	JOYCODE_3_BUTTON17 },
	{ JOYCODE(2, CODETYPE_BUTTON, 17),	JOYCODE_3_BUTTON18 },
	{ JOYCODE(2, CODETYPE_BUTTON, 18),	JOYCODE_3_BUTTON19 },
	{ JOYCODE(2, CODETYPE_BUTTON, 19),	JOYCODE_3_BUTTON20 },
	{ JOYCODE(2, CODETYPE_BUTTON, 20),	JOYCODE_3_BUTTON21 },
	{ JOYCODE(2, CODETYPE_BUTTON, 21),	JOYCODE_3_BUTTON22 },
	{ JOYCODE(2, CODETYPE_BUTTON, 22),	JOYCODE_3_BUTTON23 },
	{ JOYCODE(2, CODETYPE_BUTTON, 23),	JOYCODE_3_BUTTON24 },
	{ JOYCODE(2, CODETYPE_BUTTON, 24),	JOYCODE_3_BUTTON25 },
	{ JOYCODE(2, CODETYPE_BUTTON, 25),	JOYCODE_3_BUTTON26 },
	{ JOYCODE(2, CODETYPE_BUTTON, 26),	JOYCODE_3_BUTTON27 },
	{ JOYCODE(2, CODETYPE_BUTTON, 27),	JOYCODE_3_BUTTON28 },
	{ JOYCODE(2, CODETYPE_BUTTON, 28),	JOYCODE_3_BUTTON29 },
	{ JOYCODE(2, CODETYPE_BUTTON, 29),	JOYCODE_3_BUTTON30 },
	{ JOYCODE(2, CODETYPE_BUTTON, 30),	JOYCODE_3_BUTTON31 },
	{ JOYCODE(2, CODETYPE_BUTTON, 31),	JOYCODE_3_BUTTON32 },
	{ JOYCODE(2, CODETYPE_JOYAXIS, 0),	JOYCODE_3_ANALOG_X },
	{ JOYCODE(2, CODETYPE_JOYAXIS, 1),	JOYCODE_3_ANALOG_Y },
	{ JOYCODE(2, CODETYPE_JOYAXIS, 2),	JOYCODE_3_ANALOG_Z },

	{ JOYCODE(3, CODETYPE_AXIS_NEG, 0),	JOYCODE_4_LEFT },
	{ JOYCODE(3, CODETYPE_AXIS_POS, 0),	JOYCODE_4_RIGHT },
	{ JOYCODE(3, CODETYPE_AXIS_NEG, 1),	JOYCODE_4_UP },
	{ JOYCODE(3, CODETYPE_AXIS_POS, 1),	JOYCODE_4_DOWN },
	{ JOYCODE(3, CODETYPE_BUTTON, 0),	JOYCODE_4_BUTTON1 },
	{ JOYCODE(3, CODETYPE_BUTTON, 1),	JOYCODE_4_BUTTON2 },
	{ JOYCODE(3, CODETYPE_BUTTON, 2),	JOYCODE_4_BUTTON3 },
	{ JOYCODE(3, CODETYPE_BUTTON, 3),	JOYCODE_4_BUTTON4 },
	{ JOYCODE(3, CODETYPE_BUTTON, 4),	JOYCODE_4_BUTTON5 },
	{ JOYCODE(3, CODETYPE_BUTTON, 5),	JOYCODE_4_BUTTON6 },
	{ JOYCODE(3, CODETYPE_BUTTON, 6),	JOYCODE_4_BUTTON7 },
	{ JOYCODE(3, CODETYPE_BUTTON, 7),	JOYCODE_4_BUTTON8 },
	{ JOYCODE(3, CODETYPE_BUTTON, 8),	JOYCODE_4_BUTTON9 },
	{ JOYCODE(3, CODETYPE_BUTTON, 9),	JOYCODE_4_BUTTON10 },
	{ JOYCODE(3, CODETYPE_BUTTON, 10),	JOYCODE_4_BUTTON11 },
	{ JOYCODE(3, CODETYPE_BUTTON, 11),	JOYCODE_4_BUTTON12 },
	{ JOYCODE(3, CODETYPE_BUTTON, 12),	JOYCODE_4_BUTTON13 },
	{ JOYCODE(3, CODETYPE_BUTTON, 13),	JOYCODE_4_BUTTON14 },
	{ JOYCODE(3, CODETYPE_BUTTON, 14),	JOYCODE_4_BUTTON15 },
	{ JOYCODE(3, CODETYPE_BUTTON, 15),	JOYCODE_4_BUTTON16 },
	{ JOYCODE(3, CODETYPE_BUTTON, 16),	JOYCODE_4_BUTTON17 },
	{ JOYCODE(3, CODETYPE_BUTTON, 17),	JOYCODE_4_BUTTON18 },
	{ JOYCODE(3, CODETYPE_BUTTON, 18),	JOYCODE_4_BUTTON19 },
	{ JOYCODE(3, CODETYPE_BUTTON, 19),	JOYCODE_4_BUTTON20 },
	{ JOYCODE(3, CODETYPE_BUTTON, 20),	JOYCODE_4_BUTTON21 },
	{ JOYCODE(3, CODETYPE_BUTTON, 21),	JOYCODE_4_BUTTON22 },
	{ JOYCODE(3, CODETYPE_BUTTON, 22),	JOYCODE_4_BUTTON23 },
	{ JOYCODE(3, CODETYPE_BUTTON, 23),	JOYCODE_4_BUTTON24 },
	{ JOYCODE(3, CODETYPE_BUTTON, 24),	JOYCODE_4_BUTTON25 },
	{ JOYCODE(3, CODETYPE_BUTTON, 25),	JOYCODE_4_BUTTON26 },
	{ JOYCODE(3, CODETYPE_BUTTON, 26),	JOYCODE_4_BUTTON27 },
	{ JOYCODE(3, CODETYPE_BUTTON, 27),	JOYCODE_4_BUTTON28 },
	{ JOYCODE(3, CODETYPE_BUTTON, 28),	JOYCODE_4_BUTTON29 },
	{ JOYCODE(3, CODETYPE_BUTTON, 29),	JOYCODE_4_BUTTON30 },
	{ JOYCODE(3, CODETYPE_BUTTON, 30),	JOYCODE_4_BUTTON31 },
	{ JOYCODE(3, CODETYPE_BUTTON, 31),	JOYCODE_4_BUTTON32 },
	{ JOYCODE(3, CODETYPE_JOYAXIS, 0),	JOYCODE_4_ANALOG_X },
	{ JOYCODE(3, CODETYPE_JOYAXIS, 1),	JOYCODE_4_ANALOG_Y },
	{ JOYCODE(3, CODETYPE_JOYAXIS, 2),	JOYCODE_4_ANALOG_Z },

	{ JOYCODE(4, CODETYPE_AXIS_NEG, 0),	JOYCODE_5_LEFT },
	{ JOYCODE(4, CODETYPE_AXIS_POS, 0),	JOYCODE_5_RIGHT },
	{ JOYCODE(4, CODETYPE_AXIS_NEG, 1),	JOYCODE_5_UP },
	{ JOYCODE(4, CODETYPE_AXIS_POS, 1),	JOYCODE_5_DOWN },
	{ JOYCODE(4, CODETYPE_BUTTON, 0),	JOYCODE_5_BUTTON1 },
	{ JOYCODE(4, CODETYPE_BUTTON, 1),	JOYCODE_5_BUTTON2 },
	{ JOYCODE(4, CODETYPE_BUTTON, 2),	JOYCODE_5_BUTTON3 },
	{ JOYCODE(4, CODETYPE_BUTTON, 3),	JOYCODE_5_BUTTON4 },
	{ JOYCODE(4, CODETYPE_BUTTON, 4),	JOYCODE_5_BUTTON5 },
	{ JOYCODE(4, CODETYPE_BUTTON, 5),	JOYCODE_5_BUTTON6 },
	{ JOYCODE(4, CODETYPE_BUTTON, 6),	JOYCODE_5_BUTTON7 },
	{ JOYCODE(4, CODETYPE_BUTTON, 7),	JOYCODE_5_BUTTON8 },
	{ JOYCODE(4, CODETYPE_BUTTON, 8),	JOYCODE_5_BUTTON9 },
	{ JOYCODE(4, CODETYPE_BUTTON, 9),	JOYCODE_5_BUTTON10 },
	{ JOYCODE(4, CODETYPE_BUTTON, 10),	JOYCODE_5_BUTTON11 },
	{ JOYCODE(4, CODETYPE_BUTTON, 11),	JOYCODE_5_BUTTON12 },
	{ JOYCODE(4, CODETYPE_BUTTON, 12),	JOYCODE_5_BUTTON13 },
	{ JOYCODE(4, CODETYPE_BUTTON, 13),	JOYCODE_5_BUTTON14 },
	{ JOYCODE(4, CODETYPE_BUTTON, 14),	JOYCODE_5_BUTTON15 },
	{ JOYCODE(4, CODETYPE_BUTTON, 15),	JOYCODE_5_BUTTON16 },
	{ JOYCODE(4, CODETYPE_BUTTON, 16),	JOYCODE_5_BUTTON17 },
	{ JOYCODE(4, CODETYPE_BUTTON, 17),	JOYCODE_5_BUTTON18 },
	{ JOYCODE(4, CODETYPE_BUTTON, 18),	JOYCODE_5_BUTTON19 },
	{ JOYCODE(4, CODETYPE_BUTTON, 19),	JOYCODE_5_BUTTON20 },
	{ JOYCODE(4, CODETYPE_BUTTON, 20),	JOYCODE_5_BUTTON21 },
	{ JOYCODE(4, CODETYPE_BUTTON, 21),	JOYCODE_5_BUTTON22 },
	{ JOYCODE(4, CODETYPE_BUTTON, 22),	JOYCODE_5_BUTTON23 },
	{ JOYCODE(4, CODETYPE_BUTTON, 23),	JOYCODE_5_BUTTON24 },
	{ JOYCODE(4, CODETYPE_BUTTON, 24),	JOYCODE_5_BUTTON25 },
	{ JOYCODE(4, CODETYPE_BUTTON, 25),	JOYCODE_5_BUTTON26 },
	{ JOYCODE(4, CODETYPE_BUTTON, 26),	JOYCODE_5_BUTTON27 },
	{ JOYCODE(4, CODETYPE_BUTTON, 27),	JOYCODE_5_BUTTON28 },
	{ JOYCODE(4, CODETYPE_BUTTON, 28),	JOYCODE_5_BUTTON29 },
	{ JOYCODE(4, CODETYPE_BUTTON, 29),	JOYCODE_5_BUTTON30 },
	{ JOYCODE(4, CODETYPE_BUTTON, 30),	JOYCODE_5_BUTTON31 },
	{ JOYCODE(4, CODETYPE_BUTTON, 31),	JOYCODE_5_BUTTON32 },
	{ JOYCODE(4, CODETYPE_JOYAXIS, 0),	JOYCODE_5_ANALOG_X },
	{ JOYCODE(4, CODETYPE_JOYAXIS, 1), 	JOYCODE_5_ANALOG_Y },
	{ JOYCODE(4, CODETYPE_JOYAXIS, 2),	JOYCODE_5_ANALOG_Z },

	{ JOYCODE(5, CODETYPE_AXIS_NEG, 0),	JOYCODE_6_LEFT },
	{ JOYCODE(5, CODETYPE_AXIS_POS, 0),	JOYCODE_6_RIGHT },
	{ JOYCODE(5, CODETYPE_AXIS_NEG, 1),	JOYCODE_6_UP },
	{ JOYCODE(5, CODETYPE_AXIS_POS, 1),	JOYCODE_6_DOWN },
	{ JOYCODE(5, CODETYPE_BUTTON, 0),	JOYCODE_6_BUTTON1 },
	{ JOYCODE(5, CODETYPE_BUTTON, 1),	JOYCODE_6_BUTTON2 },
	{ JOYCODE(5, CODETYPE_BUTTON, 2),	JOYCODE_6_BUTTON3 },
	{ JOYCODE(5, CODETYPE_BUTTON, 3),	JOYCODE_6_BUTTON4 },
	{ JOYCODE(5, CODETYPE_BUTTON, 4),	JOYCODE_6_BUTTON5 },
	{ JOYCODE(5, CODETYPE_BUTTON, 5),	JOYCODE_6_BUTTON6 },
	{ JOYCODE(5, CODETYPE_BUTTON, 6),	JOYCODE_6_BUTTON7 },
	{ JOYCODE(5, CODETYPE_BUTTON, 7),	JOYCODE_6_BUTTON8 },
	{ JOYCODE(5, CODETYPE_BUTTON, 8),	JOYCODE_6_BUTTON9 },
	{ JOYCODE(5, CODETYPE_BUTTON, 9),	JOYCODE_6_BUTTON10 },
	{ JOYCODE(5, CODETYPE_BUTTON, 10),	JOYCODE_6_BUTTON11 },
	{ JOYCODE(5, CODETYPE_BUTTON, 11),	JOYCODE_6_BUTTON12 },
	{ JOYCODE(5, CODETYPE_BUTTON, 12),	JOYCODE_6_BUTTON13 },
	{ JOYCODE(5, CODETYPE_BUTTON, 13),	JOYCODE_6_BUTTON14 },
	{ JOYCODE(5, CODETYPE_BUTTON, 14),	JOYCODE_6_BUTTON15 },
	{ JOYCODE(5, CODETYPE_BUTTON, 15),	JOYCODE_6_BUTTON16 },
	{ JOYCODE(5, CODETYPE_BUTTON, 16),	JOYCODE_6_BUTTON17 },
	{ JOYCODE(5, CODETYPE_BUTTON, 17),	JOYCODE_6_BUTTON18 },
	{ JOYCODE(5, CODETYPE_BUTTON, 18),	JOYCODE_6_BUTTON19 },
	{ JOYCODE(5, CODETYPE_BUTTON, 19),	JOYCODE_6_BUTTON20 },
	{ JOYCODE(5, CODETYPE_BUTTON, 20),	JOYCODE_6_BUTTON21 },
	{ JOYCODE(5, CODETYPE_BUTTON, 21),	JOYCODE_6_BUTTON22 },
	{ JOYCODE(5, CODETYPE_BUTTON, 22),	JOYCODE_6_BUTTON23 },
	{ JOYCODE(5, CODETYPE_BUTTON, 23),	JOYCODE_6_BUTTON24 },
	{ JOYCODE(5, CODETYPE_BUTTON, 24),	JOYCODE_6_BUTTON25 },
	{ JOYCODE(5, CODETYPE_BUTTON, 25),	JOYCODE_6_BUTTON26 },
	{ JOYCODE(5, CODETYPE_BUTTON, 26),	JOYCODE_6_BUTTON27 },
	{ JOYCODE(5, CODETYPE_BUTTON, 27),	JOYCODE_6_BUTTON28 },
	{ JOYCODE(5, CODETYPE_BUTTON, 28),	JOYCODE_6_BUTTON29 },
	{ JOYCODE(5, CODETYPE_BUTTON, 29),	JOYCODE_6_BUTTON30 },
	{ JOYCODE(5, CODETYPE_BUTTON, 30),	JOYCODE_6_BUTTON31 },
	{ JOYCODE(5, CODETYPE_BUTTON, 31),	JOYCODE_6_BUTTON32 },
	{ JOYCODE(5, CODETYPE_JOYAXIS, 0),	JOYCODE_6_ANALOG_X },
	{ JOYCODE(5, CODETYPE_JOYAXIS, 1),	JOYCODE_6_ANALOG_Y },
	{ JOYCODE(5, CODETYPE_JOYAXIS, 2),	JOYCODE_6_ANALOG_Z },

	{ JOYCODE(6, CODETYPE_AXIS_NEG, 0),	JOYCODE_7_LEFT },
	{ JOYCODE(6, CODETYPE_AXIS_POS, 0),	JOYCODE_7_RIGHT },
	{ JOYCODE(6, CODETYPE_AXIS_NEG, 1),	JOYCODE_7_UP },
	{ JOYCODE(6, CODETYPE_AXIS_POS, 1),	JOYCODE_7_DOWN },
	{ JOYCODE(6, CODETYPE_BUTTON, 0),	JOYCODE_7_BUTTON1 },
	{ JOYCODE(6, CODETYPE_BUTTON, 1),	JOYCODE_7_BUTTON2 },
	{ JOYCODE(6, CODETYPE_BUTTON, 2),	JOYCODE_7_BUTTON3 },
	{ JOYCODE(6, CODETYPE_BUTTON, 3),	JOYCODE_7_BUTTON4 },
	{ JOYCODE(6, CODETYPE_BUTTON, 4),	JOYCODE_7_BUTTON5 },
	{ JOYCODE(6, CODETYPE_BUTTON, 5),	JOYCODE_7_BUTTON6 },
	{ JOYCODE(6, CODETYPE_BUTTON, 6),	JOYCODE_7_BUTTON7 },
	{ JOYCODE(6, CODETYPE_BUTTON, 7),	JOYCODE_7_BUTTON8 },
	{ JOYCODE(6, CODETYPE_BUTTON, 8),	JOYCODE_7_BUTTON9 },
	{ JOYCODE(6, CODETYPE_BUTTON, 9),	JOYCODE_7_BUTTON10 },
	{ JOYCODE(6, CODETYPE_BUTTON, 10),	JOYCODE_7_BUTTON11 },
	{ JOYCODE(6, CODETYPE_BUTTON, 11),	JOYCODE_7_BUTTON12 },
	{ JOYCODE(6, CODETYPE_BUTTON, 12),	JOYCODE_7_BUTTON13 },
	{ JOYCODE(6, CODETYPE_BUTTON, 13),	JOYCODE_7_BUTTON14 },
	{ JOYCODE(6, CODETYPE_BUTTON, 14),	JOYCODE_7_BUTTON15 },
	{ JOYCODE(6, CODETYPE_BUTTON, 15),	JOYCODE_7_BUTTON16 },
	{ JOYCODE(6, CODETYPE_BUTTON, 16),	JOYCODE_7_BUTTON17 },
	{ JOYCODE(6, CODETYPE_BUTTON, 17),	JOYCODE_7_BUTTON18 },
	{ JOYCODE(6, CODETYPE_BUTTON, 18),	JOYCODE_7_BUTTON19 },
	{ JOYCODE(6, CODETYPE_BUTTON, 19),	JOYCODE_7_BUTTON20 },
	{ JOYCODE(6, CODETYPE_BUTTON, 20),	JOYCODE_7_BUTTON21 },
	{ JOYCODE(6, CODETYPE_BUTTON, 21),	JOYCODE_7_BUTTON22 },
	{ JOYCODE(6, CODETYPE_BUTTON, 22),	JOYCODE_7_BUTTON23 },
	{ JOYCODE(6, CODETYPE_BUTTON, 23),	JOYCODE_7_BUTTON24 },
	{ JOYCODE(6, CODETYPE_BUTTON, 24),	JOYCODE_7_BUTTON25 },
	{ JOYCODE(6, CODETYPE_BUTTON, 25),	JOYCODE_7_BUTTON26 },
	{ JOYCODE(6, CODETYPE_BUTTON, 26),	JOYCODE_7_BUTTON27 },
	{ JOYCODE(6, CODETYPE_BUTTON, 27),	JOYCODE_7_BUTTON28 },
	{ JOYCODE(6, CODETYPE_BUTTON, 28),	JOYCODE_7_BUTTON29 },
	{ JOYCODE(6, CODETYPE_BUTTON, 29),	JOYCODE_7_BUTTON30 },
	{ JOYCODE(6, CODETYPE_BUTTON, 30),	JOYCODE_7_BUTTON31 },
	{ JOYCODE(6, CODETYPE_BUTTON, 31),	JOYCODE_7_BUTTON32 },
	{ JOYCODE(6, CODETYPE_JOYAXIS, 0),	JOYCODE_7_ANALOG_X },
	{ JOYCODE(6, CODETYPE_JOYAXIS, 1),	JOYCODE_7_ANALOG_Y },
	{ JOYCODE(6, CODETYPE_JOYAXIS, 2),	JOYCODE_7_ANALOG_Z },

	{ JOYCODE(7, CODETYPE_AXIS_NEG, 0),	JOYCODE_8_LEFT },
	{ JOYCODE(7, CODETYPE_AXIS_POS, 0),	JOYCODE_8_RIGHT },
	{ JOYCODE(7, CODETYPE_AXIS_NEG, 1),	JOYCODE_8_UP },
	{ JOYCODE(7, CODETYPE_AXIS_POS, 1),	JOYCODE_8_DOWN },
	{ JOYCODE(7, CODETYPE_BUTTON, 0),	JOYCODE_8_BUTTON1 },
	{ JOYCODE(7, CODETYPE_BUTTON, 1),	JOYCODE_8_BUTTON2 },
	{ JOYCODE(7, CODETYPE_BUTTON, 2),	JOYCODE_8_BUTTON3 },
	{ JOYCODE(7, CODETYPE_BUTTON, 3),	JOYCODE_8_BUTTON4 },
	{ JOYCODE(7, CODETYPE_BUTTON, 4),	JOYCODE_8_BUTTON5 },
	{ JOYCODE(7, CODETYPE_BUTTON, 5),	JOYCODE_8_BUTTON6 },
	{ JOYCODE(7, CODETYPE_BUTTON, 6),	JOYCODE_8_BUTTON7 },
	{ JOYCODE(7, CODETYPE_BUTTON, 7),	JOYCODE_8_BUTTON8 },
	{ JOYCODE(7, CODETYPE_BUTTON, 8),	JOYCODE_8_BUTTON9 },
	{ JOYCODE(7, CODETYPE_BUTTON, 9),	JOYCODE_8_BUTTON10 },
	{ JOYCODE(7, CODETYPE_BUTTON, 10),	JOYCODE_8_BUTTON11 },
	{ JOYCODE(7, CODETYPE_BUTTON, 11),	JOYCODE_8_BUTTON12 },
	{ JOYCODE(7, CODETYPE_BUTTON, 12),	JOYCODE_8_BUTTON13 },
	{ JOYCODE(7, CODETYPE_BUTTON, 13),	JOYCODE_8_BUTTON14 },
	{ JOYCODE(7, CODETYPE_BUTTON, 14),	JOYCODE_8_BUTTON15 },
	{ JOYCODE(7, CODETYPE_BUTTON, 15),	JOYCODE_8_BUTTON16 },
	{ JOYCODE(7, CODETYPE_BUTTON, 16),	JOYCODE_8_BUTTON17 },
	{ JOYCODE(7, CODETYPE_BUTTON, 17),	JOYCODE_8_BUTTON18 },
	{ JOYCODE(7, CODETYPE_BUTTON, 18),	JOYCODE_8_BUTTON19 },
	{ JOYCODE(7, CODETYPE_BUTTON, 19),	JOYCODE_8_BUTTON20 },
	{ JOYCODE(7, CODETYPE_BUTTON, 20),	JOYCODE_8_BUTTON21 },
	{ JOYCODE(7, CODETYPE_BUTTON, 21),	JOYCODE_8_BUTTON22 },
	{ JOYCODE(7, CODETYPE_BUTTON, 22),	JOYCODE_8_BUTTON23 },
	{ JOYCODE(7, CODETYPE_BUTTON, 23),	JOYCODE_8_BUTTON24 },
	{ JOYCODE(7, CODETYPE_BUTTON, 24),	JOYCODE_8_BUTTON25 },
	{ JOYCODE(7, CODETYPE_BUTTON, 25),	JOYCODE_8_BUTTON26 },
	{ JOYCODE(7, CODETYPE_BUTTON, 26),	JOYCODE_8_BUTTON27 },
	{ JOYCODE(7, CODETYPE_BUTTON, 27),	JOYCODE_8_BUTTON28 },
	{ JOYCODE(7, CODETYPE_BUTTON, 28),	JOYCODE_8_BUTTON29 },
	{ JOYCODE(7, CODETYPE_BUTTON, 29),	JOYCODE_8_BUTTON30 },
	{ JOYCODE(7, CODETYPE_BUTTON, 30),	JOYCODE_8_BUTTON31 },
	{ JOYCODE(7, CODETYPE_BUTTON, 31),	JOYCODE_8_BUTTON32 },
	{ JOYCODE(7, CODETYPE_JOYAXIS, 0),	JOYCODE_8_ANALOG_X },
	{ JOYCODE(7, CODETYPE_JOYAXIS, 1),	JOYCODE_8_ANALOG_Y },
	{ JOYCODE(7, CODETYPE_JOYAXIS, 2),	JOYCODE_8_ANALOG_Z },

#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
	{ JOYCODE(0, CODETYPE_MOUSEAXIS_NEG, 0),JOYCODE_MOUSE_1_LEFT },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS_POS, 0),JOYCODE_MOUSE_1_RIGHT },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS_NEG, 1),JOYCODE_MOUSE_1_UP },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS_POS, 1),JOYCODE_MOUSE_1_DOWN },
#endif /* USE_JOY_MOUSE_MOVE */
	{ JOYCODE(0, CODETYPE_MOUSEBUTTON, 0), 	MOUSECODE_1_BUTTON1 },
	{ JOYCODE(0, CODETYPE_MOUSEBUTTON, 1), 	MOUSECODE_1_BUTTON2 },
	{ JOYCODE(0, CODETYPE_MOUSEBUTTON, 2), 	MOUSECODE_1_BUTTON3 },
	{ JOYCODE(0, CODETYPE_MOUSEBUTTON, 3), 	MOUSECODE_1_BUTTON4 },
	{ JOYCODE(0, CODETYPE_MOUSEBUTTON, 4), 	MOUSECODE_1_BUTTON5 },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS, 0),	MOUSECODE_1_ANALOG_X },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS, 1),	MOUSECODE_1_ANALOG_Y },
	{ JOYCODE(0, CODETYPE_MOUSEAXIS, 2),	MOUSECODE_1_ANALOG_Z },

	{ JOYCODE(1, CODETYPE_MOUSEBUTTON, 0), 	MOUSECODE_2_BUTTON1 },
	{ JOYCODE(1, CODETYPE_MOUSEBUTTON, 1), 	MOUSECODE_2_BUTTON2 },
	{ JOYCODE(1, CODETYPE_MOUSEBUTTON, 2), 	MOUSECODE_2_BUTTON3 },
	{ JOYCODE(1, CODETYPE_MOUSEBUTTON, 3), 	MOUSECODE_2_BUTTON4 },
	{ JOYCODE(1, CODETYPE_MOUSEBUTTON, 4), 	MOUSECODE_2_BUTTON5 },
	{ JOYCODE(1, CODETYPE_MOUSEAXIS, 0),	MOUSECODE_2_ANALOG_X },
	{ JOYCODE(1, CODETYPE_MOUSEAXIS, 1),	MOUSECODE_2_ANALOG_Y },
	{ JOYCODE(1, CODETYPE_MOUSEAXIS, 2),	MOUSECODE_2_ANALOG_Z },

	{ JOYCODE(0, CODETYPE_GUNAXIS, 0),		GUNCODE_1_ANALOG_X },
	{ JOYCODE(0, CODETYPE_GUNAXIS, 1),		GUNCODE_1_ANALOG_Y },

	{ JOYCODE(1, CODETYPE_GUNAXIS, 0),		GUNCODE_2_ANALOG_X },
	{ JOYCODE(1, CODETYPE_GUNAXIS, 1),		GUNCODE_2_ANALOG_Y },
};



//============================================================
//  decode_ledmode
//============================================================

static int decode_ledmode(struct rc_option *option, const char *arg, int priority)
{
	if( strcmp( arg, "ps/2" ) != 0 &&
		strcmp( arg, "usb" ) != 0 )
	{
		fprintf(stderr, _WINDOWS("error: invalid value for led_mode: %s\n"), arg);
		return -1;
	}
	option->priority = priority;
	return 0;
}



//============================================================
//  decode_analog_select
//============================================================

static int decode_analog_select(struct rc_option *option, const char *arg, int priority)
{
	if (strcmp(arg, "keyboard") == 0)
		analog_type[(int)option->min] = SELECT_TYPE_KEYBOARD;
	else if (strcmp(arg, "mouse") == 0)
		analog_type[(int)option->min] = SELECT_TYPE_MOUSE;
	else if (strcmp(arg, "joystick") == 0)
		analog_type[(int)option->min] = SELECT_TYPE_JOYSTICK;
	else if (strcmp(arg, "lightgun") == 0)
		analog_type[(int)option->min] = SELECT_TYPE_LIGHTGUN;
	else
	{
		fprintf(stderr, _WINDOWS("error: invalid value for %s: %s\n"), option->name, arg);
		return -1;
	}
	option->priority = priority;
	return 0;
}



//============================================================
//  decode_digital
//============================================================

static int decode_digital(struct rc_option *option, const char *arg, int priority)
{
	if (strcmp(arg, "none") == 0)
		memset(joystick_digital, 0, sizeof(joystick_digital));
	else if (strcmp(arg, "all") == 0)
		memset(joystick_digital, 1, sizeof(joystick_digital));
	else
	{
		/* scan the string */
		while (1)
		{
			int joynum = 0;
			int axisnum = 0;

			/* stop if we hit the end */
			if (arg[0] == 0)
				break;

			/* we require the next bits to be j<N> */
			if (tolower(arg[0]) != 'j' || sscanf(&arg[1], "%d", &joynum) != 1)
				goto usage;
			arg++;
			while (arg[0] != 0 && isdigit(arg[0]))
				arg++;

			/* if we are followed by a comma or an end, mark all the axes digital */
			if (arg[0] == 0 || arg[0] == ',')
			{
				if (joynum != 0 && joynum - 1 < MAX_JOYSTICKS)
					memset(&joystick_digital[joynum - 1], 1, sizeof(joystick_digital[joynum - 1]));
				if (arg[0] == 0)
					break;
				arg++;
				continue;
			}

			/* loop over axes */
			while (1)
			{
				/* stop if we hit the end */
				if (arg[0] == 0)
					break;

				/* if we hit a comma, skip it and break out */
				if (arg[0] == ',')
				{
					arg++;
					break;
				}

				/* we require the next bits to be a<N> */
				if (tolower(arg[0]) != 'a' || sscanf(&arg[1], "%d", &axisnum) != 1)
					goto usage;
				arg++;
				while (arg[0] != 0 && isdigit(arg[0]))
					arg++;

				/* set that axis to digital */
				if (joynum != 0 && joynum - 1 < MAX_JOYSTICKS && axisnum < MAX_AXES)
					joystick_digital[joynum - 1][axisnum] = 1;
			}
		}
	}
	option->priority = priority;
	return 0;

usage:
	fprintf(stderr, _WINDOWS("error: invalid value for digital: %s -- valid values are:\n"), arg);
	fprintf(stderr, _WINDOWS("         none -- no axes on any joysticks are digital\n"));
	fprintf(stderr, _WINDOWS("         all -- all axes on all joysticks are digital\n"));
	fprintf(stderr, _WINDOWS("         j<N> -- all axes on joystick <N> are digital\n"));
	fprintf(stderr, _WINDOWS("         j<N>a<M> -- axis <M> on joystick <N> is digital\n"));
	fprintf(stderr, _WINDOWS("    Multiple axes can be specified for one joystick:\n"));
	fprintf(stderr, _WINDOWS("         j1a5a6 -- axes 5 and 6 on joystick 1 are digital\n"));
	fprintf(stderr, _WINDOWS("    Multiple joysticks can be specified separated by commas:\n"));
	fprintf(stderr, _WINDOWS("         j1,j2a2 -- all joystick 1 axes and axis 2 on joystick 2 are digital\n"));
	return -1;
}



//============================================================
//  autoselect_analog_devices
//============================================================

static void autoselect_analog_devices(const input_port_entry *inp, int type1, int type2, int type3, int anatype, const char *ananame)
{
	// loop over input ports
	for ( ; inp->type != IPT_END; inp++)

		// if this port type is in use, apply the autoselect criteria
		if ((type1 != 0 && inp->type == type1) ||
			(type2 != 0 && inp->type == type2) ||
			(type3 != 0 && inp->type == type3))
		{
			// autoenable mouse devices
			if (analog_type[anatype] == SELECT_TYPE_MOUSE && !use_mouse)
			{
				use_mouse = 1;
				if (verbose)
					printf(_WINDOWS("Autoenabling mice due to presence of a %s\n"), ananame);
			}

			// autoenable joystick devices
			if (analog_type[anatype] == SELECT_TYPE_JOYSTICK && !use_joystick)
			{
				use_joystick = 1;
				if (verbose)
					printf(_WINDOWS("Autoenabling joysticks due to presence of a %s\n"), ananame);
			}

			// autoenable lightgun devices
			if (analog_type[anatype] == SELECT_TYPE_LIGHTGUN && !use_lightgun)
			{
				use_lightgun = 1;
				if (verbose)
					printf(_WINDOWS("Autoenabling lightguns due to presence of a %s\n"), ananame);
			}

			// all done
			break;
		}
}



//============================================================
//  enum_keyboard_callback
//============================================================

static BOOL CALLBACK enum_keyboard_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	HRESULT result;

	// if we're not out of mice, log this one
	if (keyboard_count >= MAX_KEYBOARDS)
		goto out_of_keyboards;

	// attempt to create a device
	result = IDirectInput_CreateDevice(dinput, &instance->guidInstance, &keyboard_device[keyboard_count], NULL);
	if (result != DI_OK)
		goto cant_create_device;

	// try to get a version 2 device for it
	result = IDirectInputDevice_QueryInterface(keyboard_device[keyboard_count], &IID_IDirectInputDevice2, (void **)&keyboard_device2[keyboard_count]);
	if (result != DI_OK)
		keyboard_device2[keyboard_count] = NULL;

	// get the caps
	keyboard_caps[keyboard_count].dwSize = STRUCTSIZE(DIDEVCAPS);
	result = IDirectInputDevice_GetCapabilities(keyboard_device[keyboard_count], &keyboard_caps[keyboard_count]);
	if (result != DI_OK)
		goto cant_get_caps;

	// attempt to set the data format
	result = IDirectInputDevice_SetDataFormat(keyboard_device[keyboard_count], &c_dfDIKeyboard);
	if (result != DI_OK)
		goto cant_set_format;

	// set the cooperative level
	result = IDirectInputDevice_SetCooperativeLevel(keyboard_device[keyboard_count], win_video_window,
					DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (result != DI_OK)
		goto cant_set_coop_level;

	// increment the count
	keyboard_count++;
	return DIENUM_CONTINUE;

cant_set_coop_level:
cant_set_format:
cant_get_caps:
	if (keyboard_device2[keyboard_count])
		IDirectInputDevice_Release(keyboard_device2[keyboard_count]);
	IDirectInputDevice_Release(keyboard_device[keyboard_count]);
cant_create_device:
out_of_keyboards:
	return DIENUM_CONTINUE;
}



//============================================================
//  enum_mouse_callback
//============================================================

static BOOL CALLBACK enum_mouse_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	DIPROPDWORD value;
	HRESULT result;

	// if we're not out of mice, log this one
	if (mouse_count > MAX_MICE)
		goto out_of_mice;

	// attempt to create a device
	result = IDirectInput_CreateDevice(dinput, &instance->guidInstance, &mouse_device[mouse_count], NULL);
	if (result != DI_OK)
		goto cant_create_device;

	// try to get a version 2 device for it
	result = IDirectInputDevice_QueryInterface(mouse_device[mouse_count], &IID_IDirectInputDevice2, (void **)&mouse_device2[mouse_count]);
	if (result != DI_OK)
		mouse_device2[mouse_count] = NULL;

	// remember the name
	strcpy(mouse_name[mouse_count], instance->tszInstanceName);

	// get the caps
	mouse_caps[mouse_count].dwSize = STRUCTSIZE(DIDEVCAPS);
	result = IDirectInputDevice_GetCapabilities(mouse_device[mouse_count], &mouse_caps[mouse_count]);
	if (result != DI_OK)
		goto cant_get_caps;

	// set relative mode
	value.diph.dwSize = sizeof(DIPROPDWORD);
	value.diph.dwHeaderSize = sizeof(value.diph);
	value.diph.dwObj = 0;
	value.diph.dwHow = DIPH_DEVICE;
	value.dwData = DIPROPAXISMODE_REL;
	result = IDirectInputDevice_SetProperty(mouse_device[mouse_count], DIPROP_AXISMODE, &value.diph);
	if (result != DI_OK)
		goto cant_set_axis_mode;

	// attempt to set the data format
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
	result = IDirectInputDevice_SetDataFormat(mouse_device[mouse_count], &c_dfDIMouse2);
#else /* USE_JOY_MOUSE_MOVE */
	result = IDirectInputDevice_SetDataFormat(mouse_device[mouse_count], &c_dfDIMouse);
#endif /* USE_JOY_MOUSE_MOVE */
	if (result != DI_OK)
		goto cant_set_format;

	// set the cooperative level
	if (use_lightgun)
		result = IDirectInputDevice_SetCooperativeLevel(mouse_device[mouse_count], win_video_window,
					DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	else
		result = IDirectInputDevice_SetCooperativeLevel(mouse_device[mouse_count], win_video_window,
					DISCL_FOREGROUND | DISCL_EXCLUSIVE);

	if (result != DI_OK)
		goto cant_set_coop_level;

	// increment the count
	if (use_lightgun)
		lightgun_count++;
	mouse_count++;
	return DIENUM_CONTINUE;

cant_set_coop_level:
cant_set_format:
cant_set_axis_mode:
cant_get_caps:
	if (mouse_device2[mouse_count])
		IDirectInputDevice_Release(mouse_device2[mouse_count]);
	IDirectInputDevice_Release(mouse_device[mouse_count]);
cant_create_device:
out_of_mice:
	return DIENUM_CONTINUE;
}



//============================================================
//  remove_dx_system_mouse
//============================================================

static void remove_dx_system_mouse(void)
{
	int i;
	LPDIRECTINPUTDEVICE  sys_mouse_device;
	LPDIRECTINPUTDEVICE2 sys_mouse_device2;
	DIDEVCAPS sys_mouse_caps;
	char sys_mouse_name[MAX_PATH];

	// store system mouse info so it does not get lost
	sys_mouse_device  = mouse_device[0];
	sys_mouse_device2 = mouse_device2[0];
	sys_mouse_caps = mouse_caps[0];

	if (mouse_count < 2) goto setup_sys_mouse;

	mouse_count--;

	// shift mouse list
	for (i = 0; i < mouse_count; i++)
	{
		if (mouse_device2[i+1])
			mouse_device2[i] = mouse_device2[i+1];
		mouse_device[i] = mouse_device[i+1];
		mouse_caps[i] = mouse_caps[i+1];
		strcpy(mouse_name[i], mouse_name[i+1]);
	}

setup_sys_mouse:
	// system mouse will be stored at the end of the list
	mouse_device[MAX_MICE]  = sys_mouse_device;
	mouse_device2[MAX_MICE] = sys_mouse_device2;
	mouse_caps[MAX_MICE] = sys_mouse_caps;
	strcpy(mouse_name[MAX_MICE], sys_mouse_name);

	return;
}



//============================================================
//  enum_joystick_callback
//============================================================

static BOOL CALLBACK enum_joystick_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	DIPROPDWORD value;
	HRESULT result = DI_OK;
	DWORD flags;

	// if we're not out of mice, log this one
	if (joystick_count >= MAX_JOYSTICKS)
		goto out_of_joysticks;

	// attempt to create a device
	result = IDirectInput_CreateDevice(dinput, &instance->guidInstance, &joystick_device[joystick_count], NULL);
	if (result != DI_OK)
		goto cant_create_device;

	// try to get a version 2 device for it
	result = IDirectInputDevice_QueryInterface(joystick_device[joystick_count], &IID_IDirectInputDevice2, (void **)&joystick_device2[joystick_count]);
	if (result != DI_OK)
		joystick_device2[joystick_count] = NULL;

	// remember the name
	strcpy(joystick_name[joystick_count], instance->tszInstanceName);

	// get the caps
	joystick_caps[joystick_count].dwSize = STRUCTSIZE(DIDEVCAPS);
	result = IDirectInputDevice_GetCapabilities(joystick_device[joystick_count], &joystick_caps[joystick_count]);
	if (result != DI_OK)
		goto cant_get_caps;

	// set absolute mode
	value.diph.dwSize = sizeof(DIPROPDWORD);
	value.diph.dwHeaderSize = sizeof(value.diph);
	value.diph.dwObj = 0;
	value.diph.dwHow = DIPH_DEVICE;
	value.dwData = DIPROPAXISMODE_ABS;
	result = IDirectInputDevice_SetProperty(joystick_device[joystick_count], DIPROP_AXISMODE, &value.diph);
 	if (result != DI_OK)
		goto cant_set_axis_mode;

	// attempt to set the data format
	result = IDirectInputDevice_SetDataFormat(joystick_device[joystick_count], &c_dfDIJoystick);
	if (result != DI_OK)
		goto cant_set_format;

	// set the cooperative level
#if HAS_WINDOW_MENU
	flags = DISCL_BACKGROUND | DISCL_EXCLUSIVE;
#else
	flags = DISCL_FOREGROUND | DISCL_EXCLUSIVE;
#endif
	result = IDirectInputDevice_SetCooperativeLevel(joystick_device[joystick_count], win_video_window, flags);
	if (result != DI_OK)
		goto cant_set_coop_level;

	// increment the count
	joystick_count++;
	return DIENUM_CONTINUE;

cant_set_coop_level:
cant_set_format:
cant_set_axis_mode:
cant_get_caps:
	if (joystick_device2[joystick_count])
		IDirectInputDevice_Release(joystick_device2[joystick_count]);
	IDirectInputDevice_Release(joystick_device[joystick_count]);
cant_create_device:
out_of_joysticks:
	return DIENUM_CONTINUE;
}



//============================================================
//  win_init_input
//============================================================

int win_init_input(void)
{
	const input_port_entry *inp;
	HRESULT result;

	// first attempt to initialize DirectInput
	dinput_version = DIRECTINPUT_VERSION;
	result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
	if (result != DI_OK)
	{
		dinput_version = 0x0500;
		result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
		if (result != DI_OK)
		{
			dinput_version = 0x0300;
			result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
			if (result != DI_OK)
				goto cant_create_dinput;
		}
	}
	if (verbose)
		fprintf(stderr, _WINDOWS("Using DirectInput %d\n"), dinput_version >> 8);

	// enable devices based on autoselect
	if (Machine != NULL && Machine->gamedrv != NULL)
	{
		begin_resource_tracking();
		inp = input_port_allocate(Machine->gamedrv->construct_ipt, NULL);
		autoselect_analog_devices(inp, IPT_PADDLE,     IPT_PADDLE_V,   0,             ANALOG_TYPE_PADDLE,   _WINDOWS("paddle"));
		autoselect_analog_devices(inp, IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z,ANALOG_TYPE_ADSTICK,  _WINDOWS("analog joystick"));
		autoselect_analog_devices(inp, IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y, 0,             ANALOG_TYPE_LIGHTGUN, _WINDOWS("lightgun"));
		autoselect_analog_devices(inp, IPT_PEDAL,      IPT_PEDAL2,     IPT_PEDAL3,    ANALOG_TYPE_PEDAL,    _WINDOWS("pedal"));
		autoselect_analog_devices(inp, IPT_DIAL,       IPT_DIAL_V,     0,             ANALOG_TYPE_DIAL,     _WINDOWS("dial"));
		autoselect_analog_devices(inp, IPT_TRACKBALL_X,IPT_TRACKBALL_Y,0,             ANALOG_TYPE_TRACKBALL,_WINDOWS("trackball"));
#ifdef MESS
		autoselect_analog_devices(inp, IPT_MOUSE_X,    IPT_MOUSE_Y,    0,             ANALOG_TYPE_MOUSE,    "mouse");
#endif // MESS
		end_resource_tracking();
	}

	// initialize keyboard devices
	keyboard_count = 0;
	result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_KEYBOARD, enum_keyboard_callback, 0, DIEDFL_ATTACHEDONLY);
	if (result != DI_OK)
		goto cant_init_keyboard;

	// initialize mouse devices
	lightgun_count = 0;
	mouse_count = 0;
	if (use_mouse || use_lightgun)
	{
		lightgun_dual_player_state[0] = lightgun_dual_player_state[1] = 0;
		lightgun_dual_player_state[2] = lightgun_dual_player_state[3] = 0;
		win_use_raw_mouse = init_raw_mouse();
		if (!win_use_raw_mouse)
		{
			result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_MOUSE, enum_mouse_callback, 0, DIEDFL_ATTACHEDONLY);
			if (result != DI_OK)
				goto cant_init_mouse;
			// remove system mouse on multi-mouse systems
			remove_dx_system_mouse();
		}

		// if we have at least one mouse, and the "Dual" option is selected,
		//  then the lightgun_count is 2 (The two guns are read as a single
		//  4-button mouse).
		if (mouse_count && use_lightgun_dual && lightgun_count < 2)
			lightgun_count = 2;
	}

	// initialize joystick devices
	joystick_count = 0;
	if (use_joystick)
	{
		result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_JOYSTICK, enum_joystick_callback, 0, DIEDFL_ATTACHEDONLY);
		if (result != DI_OK)
			goto cant_init_joystick;
	}

	total_codes = 0;

	// init the keyboard list
	init_keycodes();

	// init the joystick list
	init_joycodes();

	// terminate array
	memset(&codelist[total_codes], 0, sizeof(codelist[total_codes]));

	// print the results
	if (verbose)
	{
		fprintf(stderr, _WINDOWS("Keyboards=%d  Mice=%d  Joysticks=%d  Lightguns=%d\n"), keyboard_count, mouse_count, joystick_count, lightgun_count);
		if (options.controller)
			fprintf(stderr,_WINDOWS("\"%s\" controller support enabled\n"), options.controller);
	}
	return 0;

cant_init_joystick:
cant_init_mouse:
cant_init_keyboard:
	IDirectInput_Release(dinput);
cant_create_dinput:
	dinput = NULL;
	return 1;
}



//============================================================
//  win_shutdown_input
//============================================================

void win_shutdown_input(void)
{
	int i;

	// release all our keyboards
	for (i = 0; i < keyboard_count; i++)
	{
		IDirectInputDevice_Release(keyboard_device[i]);
		if (keyboard_device2[i])
			IDirectInputDevice_Release(keyboard_device2[i]);
		keyboard_device2[i]=0;
	}

	// release all our joysticks
	for (i = 0; i < joystick_count; i++)
	{
		IDirectInputDevice_Release(joystick_device[i]);
		if (joystick_device2[i])
			IDirectInputDevice_Release(joystick_device2[i]);
		joystick_device2[i]=0;
	}

	// release all our mice
	if (!win_use_raw_mouse)
	{
		if (mouse_count > 1)
		{
			IDirectInputDevice_Release(mouse_device[MAX_MICE]);
			if (mouse_device2[MAX_MICE])
				IDirectInputDevice_Release(mouse_device2[MAX_MICE]);
		}
		for (i = 0; i < mouse_count; i++)
		{
			IDirectInputDevice_Release(mouse_device[i]);
			if (mouse_device2[i])
				IDirectInputDevice_Release(mouse_device2[i]);
			mouse_device2[i]=0;
		}
	}

	// free allocated strings
	for (i = 0; i < total_codes; i++)
	{
		free(codelist[i].name);
		codelist[i].name = NULL;
	}

	// release DirectInput
	if (dinput)
		IDirectInput_Release(dinput);
	dinput = NULL;
}



//============================================================
//  win_pause_input
//============================================================

void win_pause_input(int paused)
{
	int i;

	// if paused, unacquire all devices
	if (paused)
	{
		// unacquire all keyboards
		for (i = 0; i < keyboard_count; i++)
			IDirectInputDevice_Unacquire(keyboard_device[i]);

		// unacquire all our mice
		if (!win_use_raw_mouse)
		{
			if (mouse_count > 1)
				IDirectInputDevice_Unacquire(mouse_device[MAX_MICE]);
			for (i = 0; i < mouse_count; i++)
				IDirectInputDevice_Unacquire(mouse_device[i]);
		}
	}

	// otherwise, reacquire all devices
	else
	{
		// acquire all keyboards
		for (i = 0; i < keyboard_count; i++)
			IDirectInputDevice_Acquire(keyboard_device[i]);

		// acquire all our mice if active
		if (!win_use_raw_mouse)
		{
			if (mouse_count > 1)
			IDirectInputDevice_Acquire(mouse_device[MAX_MICE]);
			if (mouse_active && !win_has_menu())
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
				for (i = 0; i < mouse_count && (use_mouse || use_lightgun || use_stickpoint); i++)
#else /* USE_JOY_MOUSE_MOVE */
				for (i = 0; i < mouse_count && (use_mouse || use_lightgun); i++)
#endif /* USE_JOY_MOUSE_MOVE */
					IDirectInputDevice_Acquire(mouse_device[i]);
		}
	}

	// set the paused state
	input_paused = paused;
	win_update_cursor_state();
}



//============================================================
//  win_poll_input
//============================================================

void win_poll_input(void)
{
	HWND focus = GetFocus();
	HRESULT result = 1;
	int i, j;
#ifdef ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP
	static int is_last_keypress_from_non_di;
#endif /* ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP */

	// remember when this happened
	last_poll = osd_cycles();

	// periodically process events, in case they're not coming through
	win_process_events_periodic();

	// if we don't have focus, turn off all keys
	if (!focus)
	{
		memset(&keyboard_state[0][0], 0, sizeof(keyboard_state[i]));
		updatekeyboard();
		return;
	}

	// poll all keyboards
#ifdef ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP
	if (is_last_keypress_from_non_di)
		result = DIERR_NOTACQUIRED;
	else
#endif /* ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP */
	for (i = 0; i < keyboard_count; i++)
	{
		// first poll the device
		if (keyboard_device2[i])
			IDirectInputDevice2_Poll(keyboard_device2[i]);

		// get the state
		result = IDirectInputDevice_GetDeviceState(keyboard_device[i], sizeof(keyboard_state[i]), &keyboard_state[i][0]);

		// handle lost inputs here
		if ((result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) && !input_paused)
		{
			result = IDirectInputDevice_Acquire(keyboard_device[i]);
			if (result == DI_OK)
				result = IDirectInputDevice_GetDeviceState(keyboard_device[i], sizeof(keyboard_state[i]), &keyboard_state[i][0]);
		}

		// convert to 0 or 1
		if (result == DI_OK)
			for (j = 0; j < sizeof(keyboard_state[i]); j++)
				keyboard_state[i][j] >>= 7;
	}

#ifdef ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP
	is_last_keypress_from_non_di = 0;
#endif /* ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP */

	// if we couldn't poll the keyboard that way, poll it via GetAsyncKeyState
	if (result != DI_OK)
		for (i = 0; codelist[i].oscode; i++)
			if (IS_KEYBOARD_CODE(codelist[i].oscode))
			{
				int dik = DICODE(codelist[i].oscode);
				int vk = VKCODE(codelist[i].oscode);

				// if we have a non-zero VK, query it
				if (vk)
				{
					keyboard_state[0][dik] = (GetAsyncKeyState(vk) >> 15) & 1;
#ifdef ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP
					if (keyboard_state[0][dik])
						is_last_keypress_from_non_di = 1;
#endif /* ENABLE_POLL_INPUT_HACK_FOR_SINGLE_STEP */
				}
			}

	// update the lagged keyboard
	updatekeyboard();

#ifndef NEW_DEBUGGER
	// if the debugger is up and visible, don't bother with the rest
	if (win_debug_window != NULL && IsWindowVisible(win_debug_window))
		return;
#endif

	// poll all joysticks
	for (i = 0; i < joystick_count; i++)
	{
		// first poll the device
		if (joystick_device2[i])
			IDirectInputDevice2_Poll(joystick_device2[i]);

		// get the state
		result = IDirectInputDevice_GetDeviceState(joystick_device[i], sizeof(joystick_state[i]), &joystick_state[i]);

		// handle lost inputs here
		if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
		{
			result = IDirectInputDevice_Acquire(joystick_device[i]);
			if (result == DI_OK)
				result = IDirectInputDevice_GetDeviceState(joystick_device[i], sizeof(joystick_state[i]), &joystick_state[i]);
		}
	}

	// update joystick axis history
	update_joystick_axes();

	// poll all our mice if active
	if (win_use_raw_mouse)
		win_read_raw_mouse();
	else
	{
		if (mouse_active && !win_has_menu())
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
			for (i = 0; i < mouse_count && (use_mouse||use_lightgun||use_stickpoint); i++)
#else /* USE_JOY_MOUSE_MOVE */
			for (i = 0; i < mouse_count && (use_mouse||use_lightgun); i++)
#endif /* USE_JOY_MOUSE_MOVE */
			{
				// first poll the device
				if (mouse_device2[i])
					IDirectInputDevice2_Poll(mouse_device2[i]);

				// get the state
				result = IDirectInputDevice_GetDeviceState(mouse_device[i], sizeof(mouse_state[i]), &mouse_state[i]);

				// handle lost inputs here
				if ((result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) && !input_paused)
				{
					result = IDirectInputDevice_Acquire(mouse_device[i]);
					if (result == DI_OK)
						result = IDirectInputDevice_GetDeviceState(mouse_device[i], sizeof(mouse_state[i]), &mouse_state[i]);
				}
			}

		// poll the lightguns
		poll_lightguns();
	}
}



//============================================================
//  is_mouse_captured
//============================================================

int win_is_mouse_captured(void)
{
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
	return (!input_paused && mouse_active && mouse_count > 0 && (use_mouse || use_stickpoint) && !win_has_menu());
#else /* USE_JOY_MOUSE_MOVE */
	return (!input_paused && mouse_active && mouse_count > 0 && use_mouse && !win_has_menu());
#endif /* USE_JOY_MOUSE_MOVE */
}



//============================================================
//  updatekeyboard
//============================================================

// since the keyboard controller is slow, it is not capable of reporting multiple
// key presses fast enough. We have to delay them in order not to lose special moves
// tied to simultaneous button presses.

static void updatekeyboard(void)
{
	int i, changed = 0;

	// see if any keys have changed state
	for (i = 0; i < MAX_KEYS; i++)
		if (keyboard_state[0][i] != oldkey[i])
		{
			changed = 1;

			// keypress was missed, turn it on for one frame
			if (keyboard_state[0][i] == 0 && currkey[i] == 0)
				currkey[i] = -1;
		}

	// if keyboard state is stable, copy it over
	if (!changed)
		memcpy(currkey, &keyboard_state[0][0], sizeof(currkey));

	// remember the previous state
	memcpy(oldkey, &keyboard_state[0][0], sizeof(oldkey));
}



//============================================================
//  is_key_pressed
//============================================================

static int is_key_pressed(os_code keycode)
{
	int dik = DICODE(keycode);

	// make sure we've polled recently
	if (osd_cycles() > last_poll + osd_cycles_per_second()/4)
		win_poll_input();

	// if the video window isn't visible, we have to get our events from the console
	if (!win_video_window || !IsWindowVisible(win_video_window))
	{
		// warning: this code relies on the assumption that when you're polling for
		// keyboard events before the system is initialized, they are all of the
		// "press any key" to continue variety
		int result = _kbhit();
		if (result)
			_getch();
		return result;
	}

#if defined(MAME_DEBUG) && defined(NEW_DEBUGGER)
	// if the debugger is visible and we don't have focus, the key is not pressed
	if (debugwin_is_debugger_visible() && GetFocus() != win_video_window)
		return 0;
#endif

	// otherwise, just return the current keystate
	if (steadykey)
		return currkey[dik];
	else
		return keyboard_state[0][dik];
}



//============================================================
//  osd_readkey_unicode
//============================================================

int osd_readkey_unicode(int flush)
{
#if 0
	if (flush) clear_keybuf();
	if (keypressed())
		return ureadkey(NULL);
	else
		return 0;
#endif
	return 0;
}



//============================================================
//  init_keycodes
//============================================================

static void init_keycodes(void)
{
	int key;

	// iterate over all possible keys
	for (key = 0; key < MAX_KEYS; key++)
	{
		DIDEVICEOBJECTINSTANCE instance = { 0 };
		HRESULT result;

		// attempt to get the object info
		instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
		result = IDirectInputDevice_GetObjectInfo(keyboard_device[0], &instance, key, DIPH_BYOFFSET);
		if (result == DI_OK)
		{
			// if it worked, assume we have a valid key

			// copy the name
			char *namecopy = malloc(strlen(instance.tszName) + 1);
			if (namecopy)
			{
				input_code standardcode;
				os_code code;
				int entry;

				// find the table entry, if there is one
				for (entry = 0; win_key_trans_table[entry][0] >= 0; entry++)
					if (win_key_trans_table[entry][DI_KEY] == key)
						break;

				// compute the code, which encodes DirectInput, virtual, and ASCII codes
				code = KEYCODE(key, 0, 0);
				standardcode = CODE_OTHER_DIGITAL;
				if (win_key_trans_table[entry][0] >= 0)
				{
					code = KEYCODE(key, win_key_trans_table[entry][VIRTUAL_KEY], win_key_trans_table[entry][ASCII_KEY]);
					standardcode = win_key_trans_table[entry][MAME_KEY];
				}

				// fill in the key description
				codelist[total_codes].name = strcpy(namecopy, instance.tszName);
				codelist[total_codes].oscode = code;
				codelist[total_codes].inputcode = standardcode;
				total_codes++;
			}
		}
	}
}



//============================================================
//  update_joystick_axes
//============================================================

static void update_joystick_axes(void)
{
	int joynum, axis;

	for (joynum = 0; joynum < joystick_count; joynum++)
		for (axis = 0; axis < MAX_AXES; axis++)
		{
			axis_history *history = &joystick_history[joynum][axis][0];
			LONG curval = ((LONG *)&joystick_state[joynum].lX)[axis];
			int newtype;

			/* if same as last time (within a small tolerance), update the count */
			if (history[0].count > 0 && (history[0].value - curval) > -4 && (history[0].value - curval) < 4)
				history[0].count++;

			/* otherwise, update the history */
			else
			{
				memmove(&history[1], &history[0], sizeof(history[0]) * (HISTORY_LENGTH - 1));
				history[0].count = 1;
				history[0].value = curval;
			}

			/* if we've only ever seen one value here, or if we've been stuck at the same value for a long */
			/* time (1 minute), mark the axis as dead or invalid */
			if (history[1].count == 0 || history[0].count > Machine->refresh_rate * 60)
				newtype = AXIS_TYPE_INVALID;

			/* scan the history and count unique values; if we get more than 3, it's analog */
			else
			{
				int bucketsize = (joystick_range[joynum][axis].lMax - joystick_range[joynum][axis].lMin) / 3;
				LONG uniqueval[3] = { 1234567890, 1234567890, 1234567890 };
				int histnum;

				/* assume digital unless we figure out otherwise */
				newtype = AXIS_TYPE_DIGITAL;

				/* loop over the whole history, bucketing the values */
				for (histnum = 0; histnum < HISTORY_LENGTH; histnum++)
					if (history[histnum].count > 0)
					{
						int bucket = (history[histnum].value - joystick_range[joynum][axis].lMin) / bucketsize;

						/* if we already have an entry in this bucket, we're analog */
						if (uniqueval[bucket] != 1234567890 && uniqueval[bucket] != history[histnum].value)
						{
							newtype = AXIS_TYPE_ANALOG;
							break;
						}

						/* remember this value */
						uniqueval[bucket] = history[histnum].value;
					}
			}

			/* if the type doesn't match, switch it */
			if (joystick_type[joynum][axis] != newtype)
			{
				static const char *axistypes[] = { "invalid", "digital", "analog" };
				joystick_type[joynum][axis] = newtype;
				if (verbose)
					fprintf(stderr, _WINDOWS("Joystick %d axis %d is now %s\n"), joynum, axis, _WINDOWS(axistypes[newtype]));
			}
		}
}



//============================================================
//  add_joylist_entry
//============================================================

static void add_joylist_entry(const char *name, os_code code, input_code standardcode)
{
	// copy the name
	char *namecopy = malloc(strlen(name) + 1);
	if (namecopy)
	{
		int entry;

		// find the table entry, if there is one
		for (entry = 0; entry < ELEMENTS(joy_trans_table); entry++)
			if (joy_trans_table[entry][0] == code)
				break;

		// fill in the joy description
		codelist[total_codes].name = strcpy(namecopy, name);
		codelist[total_codes].oscode = code;
		if (entry < ELEMENTS(joy_trans_table))
			standardcode = joy_trans_table[entry][1];
		codelist[total_codes].inputcode = standardcode;
		total_codes++;
	}
}



//============================================================
//  init_joycodes
//============================================================

static void init_joycodes(void)
{
	int mouse, gun, stick, axis, button, pov;
	char tempname[MAX_PATH];
	char mousename[30];

	// map mice first
	for (mouse = 0; mouse < mouse_count; mouse++)
	{
		if (mouse_count > 1)
			sprintf(mousename, _WINDOWS("Mouse %d "), mouse + 1);
		else
			sprintf(mousename, _WINDOWS("Mouse "));

		// log the info
		if (verbose)
			fprintf(stderr, "%s: %s\n", mousename, mouse_name[mouse]);

		// add analog axes (fix me -- should enumerate these)
		sprintf(tempname, "%sX", mousename);
		add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEAXIS, 0), CODE_OTHER_ANALOG_RELATIVE);
		sprintf(tempname, "%sY", mousename);
		add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEAXIS, 1), CODE_OTHER_ANALOG_RELATIVE);
		sprintf(tempname, "%sZ", mousename);
		add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEAXIS, 2), CODE_OTHER_ANALOG_RELATIVE);

#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
		if (!win_use_raw_mouse)
		{
			// add joy-mouse axes
			// loop over all axes
			for (axis = 0; axis < MAX_MOUSEAXES; axis++)
			{
				DIDEVICEOBJECTINSTANCE instance = { 0 };
				HRESULT result;

				// attempt to get the object info
				instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
				result = IDirectInputDevice_GetObjectInfo(mouse_device[mouse], &instance, offsetof(DIMOUSESTATE2, lX) + axis * sizeof(LONG), DIPH_BYOFFSET);
				if (result == DI_OK)
				{
					// add negative value
					if (mouse_count > 1)
						sprintf(tempname, _WINDOWS("Stick %d %s -"), mouse + 1, instance.tszName);
					else
						sprintf(tempname, _WINDOWS("Stick %s -"), instance.tszName);
					add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEAXIS_NEG, axis), CODE_OTHER_ANALOG_RELATIVE);

					// add positive value
					if (mouse_count > 1)
						sprintf(tempname, _WINDOWS("Stick %d %s +"), mouse + 1, instance.tszName);
					else
						sprintf(tempname, _WINDOWS("Stick %s +"), instance.tszName);
					add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEAXIS_POS, axis), CODE_OTHER_ANALOG_RELATIVE);

					// get the axis range while we're here
					mouse_range[mouse][axis].diph.dwSize = sizeof(DIPROPRANGE);
					mouse_range[mouse][axis].diph.dwHeaderSize = sizeof(mouse_range[mouse][axis].diph);
					mouse_range[mouse][axis].diph.dwObj = offsetof(DIMOUSESTATE2, lX) + axis * sizeof(LONG);
					mouse_range[mouse][axis].diph.dwHow = DIPH_BYOFFSET;
					result = IDirectInputDevice_GetProperty(mouse_device[mouse], DIPROP_RANGE, &mouse_range[mouse][axis].diph);
				}
			}
		}
#endif /* USE_JOY_MOUSE_MOVE */

		// add mouse buttons
		for (button = 0; button < 4; button++)
		{
			if (win_use_raw_mouse)
			{
				sprintf(tempname, _WINDOWS("%s Button %d"), mousename, button + 1);
				add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEBUTTON, button), CODE_OTHER_DIGITAL);
			}
			else
			{
				DIDEVICEOBJECTINSTANCE instance = { 0 };
				HRESULT result;

				// attempt to get the object info
				instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
				// for VAIO U101 Center Button Patch
				result = IDirectInputDevice_GetObjectInfo(mouse_device[mouse], &instance, offsetof(DIMOUSESTATE2, rgbButtons[button]), DIPH_BYOFFSET);
#else /* USE_JOY_MOUSE_MOVE */
				result = IDirectInputDevice_GetObjectInfo(mouse_device[mouse], &instance, offsetof(DIMOUSESTATE, rgbButtons[button]), DIPH_BYOFFSET);
#endif /* USE_JOY_MOUSE_MOVE */
				if (result == DI_OK)
				{
					sprintf(tempname, "%s %s", mousename, instance.tszName);
					add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEBUTTON, button), CODE_OTHER_DIGITAL);
				}
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
				// for VAIO U101 Center Button Patch
				else if (button == 2)
				{
					sprintf(tempname, "%s %s", mousename, "B2");
					add_joylist_entry(tempname, JOYCODE(mouse, CODETYPE_MOUSEBUTTON, button), CODE_OTHER_DIGITAL);
				}
#endif /* USE_JOY_MOUSE_MOVE */
			}
		}
	}

	// map lightguns second
	for (gun = 0; gun < lightgun_count; gun++)
	{
		if (win_use_raw_mouse)
			sprintf(mousename, _WINDOWS("Lightgun on Mouse %d "), mouse + 1);
		else
			sprintf(mousename, _WINDOWS("Lightgun %d "), mouse + 1);
		// add lightgun axes (fix me -- should enumerate these)
		sprintf(tempname, "%sX", mousename);
		add_joylist_entry(tempname, JOYCODE(gun, CODETYPE_GUNAXIS, 0), CODE_OTHER_ANALOG_ABSOLUTE);
		sprintf(tempname, "%sY", mousename);
		add_joylist_entry(tempname, JOYCODE(gun, CODETYPE_GUNAXIS, 1), CODE_OTHER_ANALOG_ABSOLUTE);
	}

	// now map joysticks
	for (stick = 0; stick < joystick_count; stick++)
	{
		// log the info
		if (verbose)
			fprintf(stderr, _WINDOWS("Joystick %d: %s (%d axes, %d buttons, %d POVs)\n"), stick + 1, joystick_name[stick], (int)joystick_caps[stick].dwAxes, (int)joystick_caps[stick].dwButtons, (int)joystick_caps[stick].dwPOVs);

		// loop over all axes
		for (axis = 0; axis < MAX_AXES; axis++)
		{
			DIDEVICEOBJECTINSTANCE instance = { 0 };
			HRESULT result;

			// reset the type
			joystick_type[stick][axis] = AXIS_TYPE_INVALID;

			// attempt to get the object info
			instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
			result = IDirectInputDevice_GetObjectInfo(joystick_device[stick], &instance, offsetof(DIJOYSTATE, lX) + axis * sizeof(LONG), DIPH_BYOFFSET);
			if (result == DI_OK)
			{
				if (verbose)
					fprintf(stderr, _WINDOWS("  Axis %d (%s)%s\n"), axis, instance.tszName, joystick_digital[stick][axis] ? _WINDOWS(" - digital") : "");

				// add analog axis
				if (!joystick_digital[stick][axis])
				{
					sprintf(tempname, _WINDOWS("J%d %s"), stick + 1, instance.tszName);
					add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_JOYAXIS, axis), CODE_OTHER_ANALOG_ABSOLUTE);
				}

				// add negative value
				sprintf(tempname, _WINDOWS("J%d %s -"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_AXIS_NEG, axis), CODE_OTHER_DIGITAL);

				// add positive value
				sprintf(tempname, _WINDOWS("J%d %s +"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_AXIS_POS, axis), CODE_OTHER_DIGITAL);

				// get the axis range while we're here
				joystick_range[stick][axis].diph.dwSize = sizeof(DIPROPRANGE);
				joystick_range[stick][axis].diph.dwHeaderSize = sizeof(joystick_range[stick][axis].diph);
				joystick_range[stick][axis].diph.dwObj = offsetof(DIJOYSTATE, lX) + axis * sizeof(LONG);
				joystick_range[stick][axis].diph.dwHow = DIPH_BYOFFSET;
				result = IDirectInputDevice_GetProperty(joystick_device[stick], DIPROP_RANGE, &joystick_range[stick][axis].diph);
			}
		}

		// loop over all buttons
		for (button = 0; button < MAX_BUTTONS; button++)
		{
			DIDEVICEOBJECTINSTANCE instance = { 0 };
			HRESULT result;

			// attempt to get the object info
			instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
			result = IDirectInputDevice_GetObjectInfo(joystick_device[stick], &instance, offsetof(DIJOYSTATE, rgbButtons[button]), DIPH_BYOFFSET);
			if (result == DI_OK)
			{
				// make the name for this item
				sprintf(tempname, _WINDOWS("J%d %s"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_BUTTON, button), CODE_OTHER_DIGITAL);
			}
		}

		// check POV hats
		for (pov = 0; pov < MAX_POV; pov++)
		{
			DIDEVICEOBJECTINSTANCE instance = { 0 };
			HRESULT result;

			// attempt to get the object info
			instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
			result = IDirectInputDevice_GetObjectInfo(joystick_device[stick], &instance, offsetof(DIJOYSTATE, rgdwPOV[pov]), DIPH_BYOFFSET);
			if (result == DI_OK)
			{
				// add up direction
				sprintf(tempname, _WINDOWS("J%d %s U"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_POV_UP, pov), CODE_OTHER_DIGITAL);

				// add down direction
				sprintf(tempname, _WINDOWS("J%d %s D"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_POV_DOWN, pov), CODE_OTHER_DIGITAL);

				// add left direction
				sprintf(tempname, _WINDOWS("J%d %s L"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_POV_LEFT, pov), CODE_OTHER_DIGITAL);

				// add right direction
				sprintf(tempname, _WINDOWS("J%d %s R"), stick + 1, instance.tszName);
				add_joylist_entry(tempname, JOYCODE(stick, CODETYPE_POV_RIGHT, pov), CODE_OTHER_DIGITAL);
			}
		}
	}
}



//============================================================
//  get_joycode_value
//============================================================

static INT32 get_joycode_value(os_code joycode)
{
	int joyindex = JOYINDEX(joycode);
	int codetype = CODETYPE(joycode);
	int joynum = JOYNUM(joycode);
	DWORD pov;

	// switch off the type
	switch (codetype)
	{
		case CODETYPE_MOUSEBUTTON:
			if (!win_use_raw_mouse) {
				/* ActLabs lightgun - remap button 2 (shot off-screen) as button 1 */
				if (use_lightgun_dual && joyindex<4) {
					if (use_lightgun_reload && joynum==0) {
						if (joyindex==0 && lightgun_dual_player_state[1])
							return 1;
						if (joyindex==1 && lightgun_dual_player_state[1])
							return 0;
						if (joyindex==2 && lightgun_dual_player_state[3])
							return 1;
						if (joyindex==3 && lightgun_dual_player_state[3])
							return 0;
					}
					return lightgun_dual_player_state[joyindex];
				}

				if (use_lightgun) {
					if (use_lightgun_reload && joynum==0) {
						if (joyindex==0 && (mouse_state[0].rgbButtons[1]&0x80))
							return 1;
						if (joyindex==1 && (mouse_state[0].rgbButtons[1]&0x80))
							return 0;
					}
				}
			}
			return mouse_state[joynum].rgbButtons[joyindex] >> 7;

		case CODETYPE_BUTTON:
#ifdef JOYSTICK_ID
			return joystick_state[joyid[joynum]].rgbButtons[joyindex] >> 7;
#else /* JOYSTICK_ID */
			return joystick_state[joynum].rgbButtons[joyindex] >> 7;
#endif /* JOYSTICK_ID */

		case CODETYPE_AXIS_POS:
		case CODETYPE_AXIS_NEG:
		{
#ifdef JOYSTICK_ID
			LONG val = ((LONG *)&joystick_state[joyid[joynum]].lX)[joyindex];
			LONG top = joystick_range[joyid[joynum]][joyindex].lMax;
			LONG bottom = joystick_range[joyid[joynum]][joyindex].lMin;
#else /* JOYSTICK_ID */
			LONG val = ((LONG *)&joystick_state[joynum].lX)[joyindex];
			LONG top = joystick_range[joynum][joyindex].lMax;
			LONG bottom = joystick_range[joynum][joyindex].lMin;
#endif /* JOYSTICK_ID */
			LONG middle = (top + bottom) / 2;

			// watch for movement greater "a2d_deadzone" along either axis
			// FIXME in the two-axis joystick case, we need to find out
			// the angle. Anything else is unprecise.
			if (codetype == CODETYPE_AXIS_POS)
				return (val > middle + ((top - middle) * a2d_deadzone));
			else
				return (val < middle - ((middle - bottom) * a2d_deadzone));
		}

		// anywhere from 0-45 (315) deg to 0+45 (45) deg
		case CODETYPE_POV_UP:
#ifdef JOYSTICK_ID
			pov = joystick_state[joyid[joynum]].rgdwPOV[joyindex];
#else /* JOYSTICK_ID */
			pov = joystick_state[joynum].rgdwPOV[joyindex];
#endif /* JOYSTICK_ID */
			return ((pov & 0xffff) != 0xffff && (pov >= 31500 || pov <= 4500));

		// anywhere from 90-45 (45) deg to 90+45 (135) deg
		case CODETYPE_POV_RIGHT:
#ifdef JOYSTICK_ID
			pov = joystick_state[joyid[joynum]].rgdwPOV[joyindex];
#else /* JOYSTICK_ID */
			pov = joystick_state[joynum].rgdwPOV[joyindex];
#endif /* JOYSTICK_ID */
			return ((pov & 0xffff) != 0xffff && (pov >= 4500 && pov <= 13500));

		// anywhere from 180-45 (135) deg to 180+45 (225) deg
		case CODETYPE_POV_DOWN:
#ifdef JOYSTICK_ID
			pov = joystick_state[joyid[joynum]].rgdwPOV[joyindex];
#else /* JOYSTICK_ID */
			pov = joystick_state[joynum].rgdwPOV[joyindex];
#endif /* JOYSTICK_ID */
			return ((pov & 0xffff) != 0xffff && (pov >= 13500 && pov <= 22500));

		// anywhere from 270-45 (225) deg to 270+45 (315) deg
		case CODETYPE_POV_LEFT:
#ifdef JOYSTICK_ID
			pov = joystick_state[joyid[joynum]].rgdwPOV[joyindex];
#else /* JOYSTICK_ID */
			pov = joystick_state[joynum].rgdwPOV[joyindex];
#endif /* JOYSTICK_ID */
			return ((pov & 0xffff) != 0xffff && (pov >= 22500 && pov <= 31500));

#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
		// joy-mouse axis
		case CODETYPE_MOUSEAXIS_POS:
		case CODETYPE_MOUSEAXIS_NEG:
			if (mouse_active && use_stickpoint)
			{
				LONG val = ((LONG*)&mouse_state[joynum].lX)[joyindex];
				LONG top = 20;   //mouse_range[joynum][joyindex].lMax;
				LONG bottom = -20; //mouse_range[joynum][joyindex].lMin;
				LONG middle = (top + bottom) / 2;

				// watch for movement greater "a2d_deadzone" along either axis
				// FIXME in the two-axis joystick case, we need to find out
				// the angle. Anything else is unprecise.
				if (codetype == CODETYPE_MOUSEAXIS_POS)
					return (val > middle + ((top - middle) * a2d_deadzone));
				else
					return (val < middle - ((middle - bottom) * a2d_deadzone));
			}
			return 0;
#endif /* USE_JOY_MOUSE_MOVE */

		// analog joystick axis
		case CODETYPE_JOYAXIS:
		{
			if (joystick_type[joynum][joyindex] != AXIS_TYPE_ANALOG)
				return ANALOG_VALUE_INVALID;
			else
			{
				LONG val = ((LONG *)&joystick_state[joynum].lX)[joyindex];
				LONG top = joystick_range[joynum][joyindex].lMax;
				LONG bottom = joystick_range[joynum][joyindex].lMin;

				if (!use_joystick)
					return 0;
				val = (INT64)(val - bottom) * (INT64)(ANALOG_VALUE_MAX - ANALOG_VALUE_MIN) / (INT64)(top - bottom) + ANALOG_VALUE_MIN;
				if (val < ANALOG_VALUE_MIN) val = ANALOG_VALUE_MIN;
				if (val > ANALOG_VALUE_MAX) val = ANALOG_VALUE_MAX;
				return val;
			}
		}

		// analog mouse axis
		case CODETYPE_MOUSEAXIS:
			// if the mouse isn't yet active, make it so
#ifdef USE_JOY_MOUSE_MOVE // Support Stick-type Pointing Device (miko2u@hotmail.com)
			if (!mouse_active && (use_mouse||use_stickpoint) && !win_has_menu())
#else /* USE_JOY_MOUSE_MOVE */
			if (!mouse_active && use_mouse && !win_has_menu())
#endif /* USE_JOY_MOUSE_MOVE */
			{
				mouse_active = 1;
				win_pause_input(0);
			}

			if (win_use_raw_mouse && (raw_mouse_device[joynum].flags != MOUSE_MOVE_RELATIVE))
				return 0;

			// return the latest mouse info
			if (joyindex == 0)
				return mouse_state[joynum].lX * 512;
			if (joyindex == 1)
				return mouse_state[joynum].lY * 512;
			if (joyindex == 2)
				return mouse_state[joynum].lZ * 512;
			return 0;

		// analog gun axis
		case CODETYPE_GUNAXIS:
			if (joyindex >= MAX_LIGHTGUN_AXIS)
				return 0;

			if (win_use_raw_mouse) {
				if (raw_mouse_device[joynum].flags == MOUSE_MOVE_RELATIVE)
					return 0;
				// convert absolute mouse data to the range we need
				return (INT64)((joyindex ? mouse_state[joynum].lY : mouse_state[joynum].lX) + 1) * (INT64)(ANALOG_VALUE_MAX - ANALOG_VALUE_MIN + 1) / 65536 - 1 + ANALOG_VALUE_MIN;
			}

			// return the latest gun info
			if (joynum >= MAX_DX_LIGHTGUNS)
				return 0;
			return gun_axis[joynum][joyindex];
	}

	// keep the compiler happy
	return 0;
}



//============================================================
//  osd_is_code_pressed
//============================================================

INT32 osd_get_code_value(os_code code)
{
	if (IS_KEYBOARD_CODE(code))
		return is_key_pressed(code);
	else
		return get_joycode_value(code);
}



//============================================================
//  osd_get_code_list
//============================================================

const os_code_info *osd_get_code_list(void)
{
	return codelist;
}



//============================================================
//  osd_lightgun_read
//============================================================

void input_mouse_button_down(int button, int x, int y)
{
	if (!use_lightgun_dual)
		return;

	lightgun_dual_player_state[button]=1;
	lightgun_dual_player_pos[button].x=x;
	lightgun_dual_player_pos[button].y=y;

	//logerror("mouse %d at %d %d\n",button,x,y);
}

void input_mouse_button_up(int button)
{
	if (!use_lightgun_dual)
		return;

	lightgun_dual_player_state[button]=0;
}

static void poll_lightguns(void)
{
	POINT point;
	int player;

	// if the mouse isn't yet active, make it so
	if (!mouse_active && (use_mouse || use_lightgun) && !win_has_menu())
	{
		mouse_active = 1;
		win_pause_input(0);
	}

	// if out of range, skip it
	if (!use_lightgun || !win_physical_width || !win_physical_height)
		return;

	// Warning message to users - design wise this probably isn't the best function to put this in...
	if (win_window_mode)
		ui_popup(_WINDOWS("Lightgun not supported in windowed mode"));

	// loop over players
	for (player = 0; player < MAX_DX_LIGHTGUNS; player++)
	{
		// Hack - if button 2 is pressed on lightgun, then return 0,0 (off-screen) to simulate reload
		if (use_lightgun_reload)
		{
			int return_offscreen=0;

			// In dualmode we need to use the buttons returned from Windows messages
			if (use_lightgun_dual)
			{
				if (player==0 && lightgun_dual_player_state[1])
					return_offscreen=1;

				if (player==1 && lightgun_dual_player_state[3])
					return_offscreen=1;
			}
			else
			{
				if (mouse_state[0].rgbButtons[1]&0x80)
					return_offscreen=1;
			}

			if (return_offscreen)
			{
				gun_axis[player][0] = ANALOG_VALUE_MIN;
				gun_axis[player][1] = ANALOG_VALUE_MIN;
				continue;
			}
		}

		// Act-Labs dual lightgun - _only_ works with Windows messages for input location
		if (use_lightgun_dual)
		{
			if (player==0)
			{
				point.x=lightgun_dual_player_pos[0].x; // Button 0 is player 1
				point.y=lightgun_dual_player_pos[0].y; // Button 0 is player 1
			}
			else if (player==1)
			{
				point.x=lightgun_dual_player_pos[2].x; // Button 2 is player 2
				point.y=lightgun_dual_player_pos[2].y; // Button 2 is player 2
			}
			else
			{
				point.x=point.y=0;
			}
		}
		else
		{
			// I would much prefer to use DirectInput to read the gun values but there seem to be
			// some problems...  DirectInput (8.0 tested) on Win98 returns garbage for both buffered
			// and immediate, absolute and relative axis modes.  Win2k (DX 8.1) returns good data
			// for buffered absolute reads, but WinXP (8.1) returns garbage on all modes.  DX9 betas
			// seem to exhibit the same behaviour.  I have no idea of the cause of this, the only
			// consistent way to read the location seems to be the Windows system call GetCursorPos
			// which requires the application have non-exclusive access to the mouse device
			//
			GetCursorPos(&point);
		}

		// Map absolute pixel values into ANALOG_VALUE_MIN -> ANALOG_VALUE_MAX range
		gun_axis[player][0] = (point.x * (ANALOG_VALUE_MAX - ANALOG_VALUE_MIN) + win_physical_width/2) / (win_physical_width-1) + ANALOG_VALUE_MIN;
		gun_axis[player][1] = (point.y * (ANALOG_VALUE_MAX - ANALOG_VALUE_MIN) + win_physical_height/2) / (win_physical_height-1) + ANALOG_VALUE_MIN;

		if (gun_axis[player][0] < ANALOG_VALUE_MIN) gun_axis[player][0] = ANALOG_VALUE_MIN;
		if (gun_axis[player][0] > ANALOG_VALUE_MAX) gun_axis[player][0] = ANALOG_VALUE_MAX;
		if (gun_axis[player][1] < ANALOG_VALUE_MIN) gun_axis[player][1] = ANALOG_VALUE_MIN;
		if (gun_axis[player][1] > ANALOG_VALUE_MAX) gun_axis[player][1] = ANALOG_VALUE_MAX;
	}
}



//============================================================
//  osd_joystick_needs_calibration
//============================================================

int osd_joystick_needs_calibration(void)
{
	return 0;
}



//============================================================
//  osd_joystick_start_calibration
//============================================================

void osd_joystick_start_calibration(void)
{
}



//============================================================
//  osd_joystick_calibrate_next
//============================================================

const char *osd_joystick_calibrate_next(void)
{
	return 0;
}



//============================================================
//  osd_joystick_calibrate
//============================================================

void osd_joystick_calibrate(void)
{
}



//============================================================
//  osd_joystick_end_calibration
//============================================================

void osd_joystick_end_calibration(void)
{
}



//============================================================
//  osd_customize_inputport_list
//============================================================

void osd_customize_inputport_list(input_port_default_entry *defaults)
{
	static input_seq no_alt_tab_seq = SEQ_DEF_5(KEYCODE_TAB, CODE_NOT, KEYCODE_LALT, CODE_NOT, KEYCODE_RALT);
	input_port_default_entry *idef = defaults;

	// loop over all the defaults
	while (idef->type != IPT_END)
	{
		// map in some OSD-specific keys
		switch (idef->type)
		{
			// alt-enter for fullscreen
			case IPT_OSD_1:
				idef->token = "TOGGLE_FULLSCREEN";
				idef->name = _WINDOWS("Toggle fullscreen");
				seq_set_2(&idef->defaultseq, KEYCODE_LALT, KEYCODE_ENTER);
				break;

#ifdef MESS
			case IPT_OSD_2:
				if (options.disable_normal_ui)
				{
					idef->token = "TOGGLE_MENUBAR";
					idef->name = "Toggle menubar";
					seq_set_1 (&idef->defaultseq, KEYCODE_SCRLOCK);
				}
				break;
#endif /* MESS */
		}

		// disable the config menu if the ALT key is down
		// (allows ALT-TAB to switch between windows apps)
		if (idef->type == IPT_UI_CONFIGURE)
			seq_copy(&idef->defaultseq, &no_alt_tab_seq);

#ifdef MESS
		if (idef->type == IPT_UI_THROTTLE)
			seq_set_0(&idef->defaultseq);
#endif /* MESS */

		// Dual lightguns - remap default buttons to suit
		if (use_lightgun && use_lightgun_dual)
		{
			static input_seq p1b2 = SEQ_DEF_3(KEYCODE_LALT, CODE_OR, JOYCODE_1_BUTTON2);
			static input_seq p1b3 = SEQ_DEF_3(KEYCODE_SPACE, CODE_OR, JOYCODE_1_BUTTON3);
			static input_seq p2b1 = SEQ_DEF_5(KEYCODE_A, CODE_OR, JOYCODE_2_BUTTON1, CODE_OR, MOUSECODE_1_BUTTON3);
			static input_seq p2b2 = SEQ_DEF_3(KEYCODE_S, CODE_OR, JOYCODE_2_BUTTON2);

			if (idef->type == IPT_BUTTON2 && idef->player == 1)
				seq_copy(&idef->defaultseq, &p1b2);
			if (idef->type == IPT_BUTTON3 && idef->player == 1)
				seq_copy(&idef->defaultseq, &p1b3);
			if (idef->type == IPT_BUTTON1 && idef->player == 2)
				seq_copy(&idef->defaultseq, &p2b1);
			if (idef->type == IPT_BUTTON2 && idef->player == 2)
				seq_copy(&idef->defaultseq, &p2b2);
		}

		// find the next one
		idef++;
	}
}



//============================================================
//  osd_get_leds
//============================================================

int osd_get_leds(void)
{
	int result = 0;

	if (!use_keyboard_leds)
		return 0;

	// if we're on Win9x, use GetKeyboardState
	if( ledmethod == 0 )
	{
		BYTE key_states[256];

		// get the current state
		GetKeyboardState(&key_states[0]);

		// set the numlock bit
		result |= (key_states[VK_NUMLOCK] & 1);
		result |= (key_states[VK_CAPITAL] & 1) << 1;
		result |= (key_states[VK_SCROLL] & 1) << 2;
	}
	else if( ledmethod == 1 ) // WinNT/2K/XP, use GetKeyboardState
	{
		BYTE key_states[256];

		// get the current state
		GetKeyboardState(&key_states[0]);

		// set the numlock bit
		result |= (key_states[VK_NUMLOCK] & 1);
		result |= (key_states[VK_CAPITAL] & 1) << 1;
		result |= (key_states[VK_SCROLL] & 1) << 2;
	}
	else // WinNT/2K/XP, use DeviceIoControl
	{
		KEYBOARD_INDICATOR_PARAMETERS OutputBuffer;	  // Output buffer for DeviceIoControl
		ULONG				DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
		ULONG				ReturnedLength; // Number of bytes returned in output buffer

		// Address first keyboard
		OutputBuffer.UnitId = 0;

		DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_QUERY_INDICATORS,
						NULL, 0,
						&OutputBuffer, DataLength,
						&ReturnedLength, NULL);

		// Demangle lights to match 95/98
		if (OutputBuffer.LedFlags & KEYBOARD_NUM_LOCK_ON) result |= 0x1;
		if (OutputBuffer.LedFlags & KEYBOARD_CAPS_LOCK_ON) result |= 0x2;
		if (OutputBuffer.LedFlags & KEYBOARD_SCROLL_LOCK_ON) result |= 0x4;
	}

	return result;
}



//============================================================
//  osd_set_leds
//============================================================

void osd_set_leds(int state)
{
	if (!use_keyboard_leds)
		return;

	// if we're on Win9x, use SetKeyboardState
	if( ledmethod == 0 )
	{
		// thanks to Lee Taylor for the original version of this code
		BYTE key_states[256];

		// get the current state
		GetKeyboardState(&key_states[0]);

		// mask states and set new states
		key_states[VK_NUMLOCK] = (key_states[VK_NUMLOCK] & ~1) | ((state >> 0) & 1);
		key_states[VK_CAPITAL] = (key_states[VK_CAPITAL] & ~1) | ((state >> 1) & 1);
		key_states[VK_SCROLL] = (key_states[VK_SCROLL] & ~1) | ((state >> 2) & 1);

		SetKeyboardState(&key_states[0]);
	}
	else if( ledmethod == 1 ) // WinNT/2K/XP, use keybd_event()
	{
		int k;
		BYTE keyState[ 256 ];
		const BYTE vk[ 3 ] = { VK_NUMLOCK, VK_CAPITAL, VK_SCROLL };

		GetKeyboardState( (LPBYTE)&keyState );
		for( k = 0; k < 3; k++ )
		{
			if( (  ( ( state >> k ) & 1 ) && !( keyState[ vk[ k ] ] & 1 ) ) ||
				( !( ( state >> k ) & 1 ) &&  ( keyState[ vk[ k ] ] & 1 ) ) )
			{
				// Simulate a key press
				keybd_event( vk[ k ], 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0 );

				// Simulate a key release
				keybd_event( vk[ k ], 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
			}
		}
	}
	else // WinNT/2K/XP, use DeviceIoControl
	{
		KEYBOARD_INDICATOR_PARAMETERS InputBuffer;	  // Input buffer for DeviceIoControl
		ULONG				DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
		ULONG				ReturnedLength; // Number of bytes returned in output buffer
		UINT				LedFlags=0;

		// Demangle lights to match 95/98
		if (state & 0x1) LedFlags |= KEYBOARD_NUM_LOCK_ON;
		if (state & 0x2) LedFlags |= KEYBOARD_CAPS_LOCK_ON;
		if (state & 0x4) LedFlags |= KEYBOARD_SCROLL_LOCK_ON;

		// Address first keyboard
		InputBuffer.UnitId = 0;
		InputBuffer.LedFlags = LedFlags;

		DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_SET_INDICATORS,
						&InputBuffer, DataLength,
						NULL, 0,
						&ReturnedLength, NULL);
	}

	return;
}



//============================================================
//  start_led
//============================================================

void start_led(void)
{
	OSVERSIONINFO osinfo = { sizeof(OSVERSIONINFO) };

	if (!use_keyboard_leds)
		return;

	// retrive windows version
	GetVersionEx(&osinfo);

	if ( osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
	{
		// 98
		ledmethod = 0;
	}
	else if( strcmp( ledmode, "usb" ) == 0 )
	{
		// nt/2k/xp
		ledmethod = 1;
	}
	else
	{
		// nt/2k/xp
		int error_number;

		ledmethod = 2;

		if (!DefineDosDevice (DDD_RAW_TARGET_PATH, "Kbd",
					"\\Device\\KeyboardClass0"))
		{
			error_number = GetLastError();
			fprintf(stderr, _WINDOWS("Unable to open the keyboard device. (error %d)\n"), error_number);
			return;
		}

		hKbdDev = CreateFile("\\\\.\\Kbd", GENERIC_WRITE, 0,
					NULL,	OPEN_EXISTING,	0,	NULL);

		if (hKbdDev == INVALID_HANDLE_VALUE)
		{
			error_number = GetLastError();
			fprintf(stderr, _WINDOWS("Unable to open the keyboard device. (error %d)\n"), error_number);
			return;
		}
	}

	// remember the initial LED states
	original_leds = osd_get_leds();

	return;
}



//============================================================
//  stop_led
//============================================================

void stop_led(void)
{
	int error_number = 0;

	if (!use_keyboard_leds)
		return;

	// restore the initial LED states
	osd_set_leds(original_leds);

	if( ledmethod == 0 )
	{
	}
	else if( ledmethod == 1 )
	{
	}
	else
	{
		// nt/2k/xp
		if (!DefineDosDevice (DDD_REMOVE_DEFINITION, "Kbd", NULL))
		{
			error_number = GetLastError();
			fprintf(stderr, _WINDOWS("Unable to close the keyboard device. (error %d)\n"), error_number);
			return;
		}

		if (!CloseHandle(hKbdDev))
		{
			error_number = GetLastError();
			fprintf(stderr, _WINDOWS("Unable to close the keyboard device. (error %d)\n"), error_number);
			return;
		}
	}

	return;
}



//============================================================
//  set_rawmouse_device_name
//============================================================
static void set_rawmouse_device_name(const char *raw_string, unsigned int mouse_num)
{
	// This routine is used to get the mouse name.  This will give
	// us the same name that DX will report on non-XP systems.
	// It is a total mess to find the real name of a USB device from
	// the RAW name.  The mouse is named as the HID device, but
	// the actual physical name that the mouse reports is in the USB
	// device.
	// If the mouse is not HID, we use the name pointed to by the raw_string key.
	// If the mouse is HID, we try to get the LocationInformation value
	// from the parent USB device.  If this is not available we default
	// to the name pointed to by the raw_string key.  Fun, eh?
	// The raw_string as passed is formatted as:
	//   \??\type-id#hardware-id#instance-id#{DeviceClasses-id}

	char reg_string[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Enum\\";
	TCHAR t_reg_string[2 * MAX_PATH];		// final reg string
	char parent_id_prefix[MAX_PATH];		// the id we are looking for
	char test_parent_id_prefix[MAX_PATH];	// the id we are testing
	char *test_pos;							// general purpose test pointer for positioning
	DWORD name_length = MAX_PATH;
	DWORD instance, hardware;
	HKEY hardware_key, reg_key, sub_key;
	int key_pos, hardware_key_pos;		// used to keep track of where the keys are added.
	LONG hardware_result, instance_result;

	// too many mice?
	if (mouse_num > MAX_MICE) return;
	mouse_name[mouse_num][0] = 0;

	// is string formated in RAW format?
	if (strncmp(raw_string, "\\??\\", 4)) return;
	key_pos = strlen(reg_string);

	// remove \??\ from start and add this onto the end of the key string
	strcat(reg_string, raw_string + 4);
	// then remove the final DeviceClasses string
	*(strrchr(reg_string, '#')) = 0;

	// convert the remaining 2 '#' to '\'
	test_pos = strchr(reg_string, '#');
	*test_pos = '\\';
	*(strchr(test_pos, '#')) = '\\';
	// finialize registry key
	lstrcpy (t_reg_string, reg_string);

	// Open the key.  If we can't and we're HID then try USB
	instance_result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
							t_reg_string,
							0, KEY_READ, &reg_key);
	if (instance_result == ERROR_SUCCESS)
	{
		// first check for the name in LocationInformation (slim chance)
		// if it is not found then default to the name in DeviceDesc
		instance_result = RegQueryValueEx(reg_key,
								"LocationInformation",
								NULL, NULL,
								mouse_name[mouse_num],
								&name_length)
				&& RegQueryValueEx(reg_key,
								"DeviceDesc",
								NULL, NULL,
								mouse_name[mouse_num],
								&name_length);
		RegCloseKey(reg_key);
	}

	// give up if not HID
	if (strncmp(raw_string + 4, "HID", 3)) return;

	// Try and track down the parent USB device.
	// The raw name we are passed contains the HID Hardware ID, and USB Parent ID.
	// Some times the HID Hardware ID is the same as the USB Hardware ID.  But not always.
	// We need to look in the device instances of the each USB hardware instance for the parent-id.
	strncpy(reg_string + key_pos, "USB", 3);
	test_pos = strrchr(reg_string, '\\');
	strcpy(parent_id_prefix, test_pos + 1);
	key_pos += 4;
	*(reg_string + key_pos) = 0;
	hardware_key_pos = strlen(reg_string);
	// reg_string now contains the key name for the USB hardware.
	// parent_id_prefix is the value we will be looking to match.
	lstrcpy (t_reg_string, reg_string);
	hardware_result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
						t_reg_string,
						0, KEY_READ, &hardware_key);
	if (hardware_result != ERROR_SUCCESS) return;

	// Start checking each peice of USB hardware
	for (hardware = 0; hardware_result == ERROR_SUCCESS; hardware++)
	{
		name_length = MAX_PATH - hardware_key_pos;
		// get hardware key name
		hardware_result = RegEnumKeyEx(hardware_key, hardware,
								reg_string + hardware_key_pos,
								&name_length, NULL, NULL, NULL, NULL);
		if (hardware_result == ERROR_SUCCESS)
		{
			strcat(reg_string, "\\");
			key_pos = strlen(reg_string);
			// open hardware key
			lstrcpy (t_reg_string, reg_string);
			hardware_result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
											t_reg_string,
											0, KEY_READ, &reg_key);
			if (hardware_result == ERROR_SUCCESS)
			{
				// We now have to enumerate the keys in Hardware key.  You may have
				// more then one of the same device.
				instance_result = ERROR_SUCCESS;
				for (instance = 0; instance_result == ERROR_SUCCESS; instance++)
				{
					name_length = MAX_PATH - key_pos;
					// get sub key name
					instance_result = RegEnumKeyEx(reg_key, instance,
											reg_string + key_pos,
											&name_length, NULL, NULL, NULL, NULL);
					if (instance_result == ERROR_SUCCESS)
					{
						// open sub_key
						lstrcpy (t_reg_string, reg_string);
						instance_result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
												t_reg_string,
												0, KEY_READ, &sub_key);
						if (instance_result == ERROR_SUCCESS)
						{
							// get the ParentIdPrefix of this instance of the hardware
							name_length = MAX_PATH;
							if (RegQueryValueEx(sub_key,
													"ParentIdPrefix",
													NULL, NULL,
													test_parent_id_prefix,
													&name_length) == ERROR_SUCCESS)
							{
								// is this the ParentIdPrefix that we are looking for?
								if (!strncmp(test_parent_id_prefix, parent_id_prefix, strlen(test_parent_id_prefix)))
									break;
							} // keep looking
							RegCloseKey(sub_key);
						} // next - can't open sub-key
					} // next - can't enumerate or we are out of instances of the hardware
				} // check next hardware instance
				RegCloseKey(reg_key);
				if (instance_result == ERROR_SUCCESS) break;
			} // done - can't open Hardware key
		} // done - can't enumerate or we are out of USB devices
	} // check next USB instance

	RegCloseKey(hardware_key);

	if (instance_result == ERROR_SUCCESS)
	{
		// We should now be at the USB parent device.  Get the real mouse name.
		name_length = MAX_PATH;
		instance_result = RegQueryValueEx(sub_key,
								"LocationInformation",
								NULL, NULL,
								mouse_name[mouse_num],
								&name_length);
		RegCloseKey(sub_key);
		RegCloseKey(reg_key);
	}

	return;
}



//============================================================
//  is_rm_rdp_mouse
//============================================================
// returns TRUE if it is a remote desktop mouse

// A weak solution to weeding out the Terminal Services virtual mouse from the
// list of devices. I read somewhere that you shouldn't trust the device name's format.
// In other words, you shouldn't do what I'm doing.
// Here's the quote from http://www.jsiinc.com/SUBJ/tip4600/rh4628.htm:
//   "Note that you should not make any programmatic assumption about how an instance ID
//    is formatted. To determine root devices, you can check device status bits."
//  So tell me, what are these (how you say) "status bits" and where can I get some?

static BOOL is_rm_rdp_mouse(const char *device_string)
{
	return !(strncmp(device_string, "\\??\\Root#RDP_MOU#0000#", 22));
}



//============================================================
//  register_raw_mouse
//============================================================
// returns TRUE if registration succeeds

static BOOL register_raw_mouse(void)
{
	// This function registers to receive the WM_INPUT messages
	RAWINPUTDEVICE rid[1]; // Register only for mouse messages from wm_input.

	//register to get wm_input messages
	rid[0].usUsagePage = 0x01;
	rid[0].usUsage = 0x02;
	rid[0].dwFlags = 0;// RIDEV_NOLEGACY;   // adds HID mouse and also ignores legacy mouse messages
	rid[0].hwndTarget = NULL;

	// Register to receive the WM_INPUT message for any change in mouse
	// (buttons, wheel, and movement will all generate the same message)
	return ((*_RegisterRawInputDevices)(rid, 1, sizeof (rid[0])));
}



//============================================================
//  init_raw_mouse
//============================================================
// returns TRUE if initialization succeeds

static BOOL init_raw_mouse(void)
{
	PRAWINPUTDEVICELIST raw_input_device_list;
	char *ps_name = NULL;
	HMODULE user32;
	int input_devices, size, i;

	/* Check to see if OS is raw input capable */
	user32 = LoadLibrary("user32.dll");
	if (!user32) goto cant_use_raw_input;
	_RegisterRawInputDevices = (pRegisterRawInputDevices)GetProcAddress(user32,"RegisterRawInputDevices");
	if (!_RegisterRawInputDevices) goto cant_use_raw_input;
	_GetRawInputDeviceList = (pGetRawInputDeviceList)GetProcAddress(user32,"GetRawInputDeviceList");
	if (!_GetRawInputDeviceList) goto cant_use_raw_input;
	_GetRawInputDeviceInfoA = (pGetRawInputDeviceInfoA)GetProcAddress(user32,"GetRawInputDeviceInfoA");
	if (!_GetRawInputDeviceInfoA) goto cant_use_raw_input;
	_GetRawInputData = (pGetRawInputData)GetProcAddress(user32,"GetRawInputData");
	if (!_GetRawInputData) goto cant_use_raw_input;

	// 1st call to GetRawInputDeviceList: Pass NULL to get the number of devices.
	if ((*_GetRawInputDeviceList)(NULL, &input_devices, sizeof(RAWINPUTDEVICELIST)) != 0)
		goto cant_use_raw_input;

	// Allocate the array to hold the DeviceList
	if ((raw_input_device_list = malloc(sizeof(RAWINPUTDEVICELIST) * input_devices)) == NULL)
		goto cant_use_raw_input;

	// 2nd call to GetRawInputDeviceList: Pass the pointer to our DeviceList and GetRawInputDeviceList() will fill the array
	if ((*_GetRawInputDeviceList)(raw_input_device_list, &input_devices, sizeof(RAWINPUTDEVICELIST)) == -1)
		goto cant_create_raw_input;

	// Loop through all devices and setup the mice.
	// RAWMOUSE reports the list last mouse to first,
	//  so we will read from the end of the list first.
	// Otherwise every new mouse plugged in becomes mouse 1.
	for (i = input_devices - 1; (i >= 0) && (mouse_count < MAX_MICE); i--)
	{
		if (raw_input_device_list[i].dwType == RIM_TYPEMOUSE)
		{
			/* Get the device name and use it to determine if it's the RDP Terminal Services virtual device. */

			// 1st call to GetRawInputDeviceInfo: Pass NULL to get the size of the device name
			if ((*_GetRawInputDeviceInfoA)(raw_input_device_list[i].hDevice, RIDI_DEVICENAME, NULL, &size) != 0)
				goto cant_create_raw_input;

			// Allocate the array to hold the name
			if ((ps_name = (char *)malloc(sizeof(TCHAR) * size)) == NULL)
				goto cant_create_raw_input;

			// 2nd call to GetRawInputDeviceInfo: Pass our pointer to get the device name
			if ((int)(*_GetRawInputDeviceInfoA)(raw_input_device_list[i].hDevice, RIDI_DEVICENAME, ps_name, &size) < 0)
				goto cant_create_raw_input;

			// Use this mouse if it's not an RDP mouse
			if (!is_rm_rdp_mouse(ps_name))
			{
				raw_mouse_device[mouse_count].device_handle = raw_input_device_list[i].hDevice;
				set_rawmouse_device_name(ps_name, mouse_count);
				mouse_count++;
			}

			free(ps_name);
		}
	}

	free(raw_input_device_list);

	// only use raw mouse support for multiple mice
	if (mouse_count < 2)
	{
		mouse_count = 0;
		goto dont_use_raw_input;
	}

	// finally, register to recieve raw input WM_INPUT messages
	if (!register_raw_mouse())
		goto cant_init_raw_input;

	if (verbose)
		fprintf(stderr, "Using RAWMOUSE for Mouse input\n");

	// override lightgun settings.  Not needed with RAWinput.
	use_lightgun = 0;
	use_lightgun_dual = 0;
	// There is no way to know if a mouse is a lightgun or mouse at init.
	// Treat every mouse as a possible lightgun.
	// When the data is received from the device,
	//   it will update the mouse data if relative,
	//   else it will update the lightgun data if absolute.
	// The only way I can think of to tell if a mouse is a lightgun is to
	//  use the device id.  And keep a list of known lightguns.
	lightgun_count = mouse_count;
	return 1;

cant_create_raw_input:
	free(raw_input_device_list);
	free(ps_name);
dont_use_raw_input:
cant_use_raw_input:
cant_init_raw_input:
	return 0;
}



//============================================================
//  process_raw_input
//============================================================

static void process_raw_input(PRAWINPUT raw)
{
	int i;
	LONG z;
	USHORT button_flags;
	BYTE *buttons;

	for ( i=0; i < mouse_count; i++)
	{
		if (raw_mouse_device[i].device_handle == raw->header.hDevice)
		{
			// Z axis on a mouse is incremeted 120 for each change.
			// It uses a signed SHORT value stored in a unsigned USHORT.
			// We will convert it to reflect +/- 1 increments.
			z = (LONG)((SHORT)raw->data.mouse.usButtonData / 120);

			// Update the axis values for the specified mouse
			if (raw_mouse_device[i].flags == MOUSE_MOVE_RELATIVE)
			{
				raw_mouse_device[i].mouse_state.lX += raw->data.mouse.lLastX;
				raw_mouse_device[i].mouse_state.lY += raw->data.mouse.lLastY;
				raw_mouse_device[i].mouse_state.lZ += z;
			}
			else
			{
				raw_mouse_device[i].mouse_state.lX = raw->data.mouse.lLastX;
				raw_mouse_device[i].mouse_state.lY = raw->data.mouse.lLastY;
				raw_mouse_device[i].mouse_state.lZ = z;
			}

			// The following 2 lines are just to avoid a lot of pointer re-direction
			button_flags = raw->data.mouse.usButtonFlags;
			buttons = raw_mouse_device[i].mouse_state.rgbButtons;

			// Update the button values for the specified mouse
			if (button_flags & RI_MOUSE_BUTTON_1_DOWN) buttons[0] = 0x80;
			if (button_flags & RI_MOUSE_BUTTON_1_UP)   buttons[0] = 0;
			if (button_flags & RI_MOUSE_BUTTON_2_DOWN) buttons[1] = 0x80;
			if (button_flags & RI_MOUSE_BUTTON_2_UP)   buttons[1] = 0;
			if (button_flags & RI_MOUSE_BUTTON_3_DOWN) buttons[2] = 0x80;
			if (button_flags & RI_MOUSE_BUTTON_3_UP)   buttons[2] = 0;
			if (button_flags & RI_MOUSE_BUTTON_4_DOWN) buttons[3] = 0x80;
			if (button_flags & RI_MOUSE_BUTTON_4_UP)   buttons[3] = 0;

			raw_mouse_device[i].flags = raw->data.mouse.usFlags;
		}
	}

	return;
}



//============================================================
//  win_raw_mouse_update
//============================================================
// returns TRUE if raw mouse info successfully updated

BOOL win_raw_mouse_update(HANDLE in_device_handle)
{
	//  When the WM_INPUT message is received, the lparam must be passed to this
	//  function to keep a running tally of all mouse moves.

	LPBYTE data;
	int size;

	if ((*_GetRawInputData)((HRAWINPUT)in_device_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) == -1)
		goto cant_find_raw_data;

	data = (LPBYTE)malloc(sizeof(LPBYTE) * size);
	if (data == NULL)
		goto cant_find_raw_data;

	if ((*_GetRawInputData)((HRAWINPUT)in_device_handle, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)) != size )
		goto cant_read_raw_data;

	process_raw_input((RAWINPUT*)data);

	free(data);
	return 1;

cant_read_raw_data:
	free(data);
cant_find_raw_data:
	return 0;
}



//============================================================
//  win_read_raw_mouse
//============================================================
// use this to poll and reset the current mouse info

static void win_read_raw_mouse(void)
{
	int i;

	for ( i = 0; i < mouse_count; i++)
	{
		mouse_state[i] = raw_mouse_device[i].mouse_state;

		// set X,Y to MIN values if offscreen reload is used and fire
		if (use_lightgun_reload && mouse_state[i].rgbButtons[1] & 0x80)
		{
			mouse_state[i].lX = ANALOG_VALUE_MIN;
			mouse_state[i].lY = ANALOG_VALUE_MIN;
			mouse_state[i].rgbButtons[0] = 0x80;
		}

		// do not clear if absolute
		if (raw_mouse_device[i].flags == MOUSE_MOVE_RELATIVE)
		{
			raw_mouse_device[i].mouse_state.lX = 0;
			raw_mouse_device[i].mouse_state.lY = 0;
			raw_mouse_device[i].mouse_state.lZ = 0;
		}
	}
}
