diff options
Diffstat (limited to 'src/emu/input.c')
-rw-r--r-- | src/emu/input.c | 1603 |
1 files changed, 1603 insertions, 0 deletions
diff --git a/src/emu/input.c b/src/emu/input.c new file mode 100644 index 00000000000..9a961ea0b7a --- /dev/null +++ b/src/emu/input.c @@ -0,0 +1,1603 @@ +/*************************************************************************** + + input.c + + Handle input from the user. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + To do: + * auto-selecting joystick configs + * per-joystick configs? + * test half-axis selections + * add input test menu + * get rid of osd_customize_inputport_list + +***************************************************************************/ + +#include "input.h" +#include "mame.h" +#include "emuopts.h" +#include "profiler.h" +#include <ctype.h> + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* joystick mapping codes */ +#define JOYSTICK_MAP_NEUTRAL 0x00 +#define JOYSTICK_MAP_LEFT 0x01 +#define JOYSTICK_MAP_RIGHT 0x02 +#define JOYSTICK_MAP_UP 0x04 +#define JOYSTICK_MAP_DOWN 0x08 +#define JOYSTICK_MAP_STICKY 0x0f + +/* the largest number of tracked pressed switches for memory */ +#define MAX_PRESSED_SWITCHES 16 + +/* invalid memory value for axis polling */ +#define INVALID_AXIS_VALUE 0x7fffffff + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* single device item */ +typedef struct _input_device_item input_device_item; +struct _input_device_item +{ + input_device_class devclass; /* device class of parent item */ + int devindex; /* device index of parent item */ + astring * name; /* string name of item */ + astring * token; /* tokenized name for non-standard items */ + void * internal; /* internal callback pointer */ + input_item_class itemclass; /* class of the item */ + input_item_id itemid; /* originally specified item id */ + item_get_state_cb getstate; /* get state callback */ + INT32 current; /* current raw value */ + INT32 memory; /* "memory" value, to remember where we started during polling */ + INT32 oncelatch; /* latched "once" value, cleared after each read */ + + /* keyboard information */ + INT32 steadykey; /* the live steadykey state */ + INT32 oldkey; /* old live state */ +}; + + +/* a 9x9 joystick map */ +typedef struct _joystick_map joystick_map; +struct _joystick_map +{ + UINT8 map[9][9]; /* 9x9 grid */ +}; + + +/* a single input device */ +struct _input_device +{ + astring * name; /* string name of device */ + input_device_class devclass; /* class of this device */ + int devindex; /* device index of this device */ + input_device_item * item[ITEM_ID_ABSOLUTE_MAXIMUM]; /* array of pointers to items */ + input_item_id maxitem; /* maximum item index */ + void * internal; /* internal callback pointer */ + + /* joystick information */ + joystick_map joymap; /* joystick map for this device */ + UINT8 lastmap; /* last joystick map value for this device */ +}; + + +/* a list of devices as an array */ +typedef struct _input_device_list input_device_list; +struct _input_device_list +{ + input_device * list; /* the array */ + int count; /* elements in the array */ + UINT8 enabled; /* is this class enabled? */ + UINT8 multi; /* are multiple instances of this class allowed? */ +}; + + +/* code <-> string matching */ +typedef struct _code_string_table code_string_table; +struct _code_string_table +{ + UINT32 code; + const char * string; +}; + + + +/*************************************************************************** + TOKEN/STRING TABLES +***************************************************************************/ + +/* token strings for device classes */ +static const code_string_table devclass_token_table[] = +{ + { DEVICE_CLASS_KEYBOARD, "KEYCODE" }, + { DEVICE_CLASS_MOUSE, "MOUSECODE" }, + { DEVICE_CLASS_LIGHTGUN, "GUNCODE" }, + { DEVICE_CLASS_JOYSTICK, "JOYCODE" }, + { ~0, "UNKCODE" } +}; + +/* friendly strings for device classes */ +static const code_string_table devclass_string_table[] = +{ + { DEVICE_CLASS_KEYBOARD, "Kbd" }, + { DEVICE_CLASS_MOUSE, "Mouse" }, + { DEVICE_CLASS_LIGHTGUN, "Gun" }, + { DEVICE_CLASS_JOYSTICK, "Joy" }, + { ~0, "Unk" } +}; + +/* token strings for item modifiers */ +static const code_string_table modifier_token_table[] = +{ + { ITEM_MODIFIER_POS, "POS" }, + { ITEM_MODIFIER_NEG, "NEG" }, + { ITEM_MODIFIER_LEFT, "LEFT" }, + { ITEM_MODIFIER_RIGHT, "RIGHT" }, + { ITEM_MODIFIER_UP, "UP" }, + { ITEM_MODIFIER_DOWN, "DOWN" }, + { ~0, "" } +}; + +/* friendly strings for item modifiers */ +static const code_string_table modifier_string_table[] = +{ + { ITEM_MODIFIER_POS, "+" }, + { ITEM_MODIFIER_NEG, "-" }, + { ITEM_MODIFIER_LEFT, "Left" }, + { ITEM_MODIFIER_RIGHT, "Right" }, + { ITEM_MODIFIER_UP, "Up" }, + { ITEM_MODIFIER_DOWN, "Down" }, + { ~0, "" } +}; + +/* token strings for item classes */ +static const code_string_table itemclass_token_table[] = +{ + { ITEM_CLASS_SWITCH, "SWITCH" }, + { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" }, + { ITEM_CLASS_RELATIVE, "RELATIVE" }, + { ~0, "" } +}; + +/* token strings for standard item ids */ +static const code_string_table itemid_token_table[] = +{ + /* standard keyboard codes */ + { ITEM_ID_A, "A" }, + { ITEM_ID_B, "B" }, + { ITEM_ID_C, "C" }, + { ITEM_ID_D, "D" }, + { ITEM_ID_E, "E" }, + { ITEM_ID_F, "F" }, + { ITEM_ID_G, "G" }, + { ITEM_ID_H, "H" }, + { ITEM_ID_I, "I" }, + { ITEM_ID_J, "J" }, + { ITEM_ID_K, "K" }, + { ITEM_ID_L, "L" }, + { ITEM_ID_M, "M" }, + { ITEM_ID_N, "N" }, + { ITEM_ID_O, "O" }, + { ITEM_ID_P, "P" }, + { ITEM_ID_Q, "Q" }, + { ITEM_ID_R, "R" }, + { ITEM_ID_S, "S" }, + { ITEM_ID_T, "T" }, + { ITEM_ID_U, "U" }, + { ITEM_ID_V, "V" }, + { ITEM_ID_W, "W" }, + { ITEM_ID_X, "X" }, + { ITEM_ID_Y, "Y" }, + { ITEM_ID_Z, "Z" }, + { ITEM_ID_0, "0" }, + { ITEM_ID_1, "1" }, + { ITEM_ID_2, "2" }, + { ITEM_ID_3, "3" }, + { ITEM_ID_4, "4" }, + { ITEM_ID_5, "5" }, + { ITEM_ID_6, "6" }, + { ITEM_ID_7, "7" }, + { ITEM_ID_8, "8" }, + { ITEM_ID_9, "9" }, + { ITEM_ID_F1, "F1" }, + { ITEM_ID_F2, "F2" }, + { ITEM_ID_F3, "F3" }, + { ITEM_ID_F4, "F4" }, + { ITEM_ID_F5, "F5" }, + { ITEM_ID_F6, "F6" }, + { ITEM_ID_F7, "F7" }, + { ITEM_ID_F8, "F8" }, + { ITEM_ID_F9, "F9" }, + { ITEM_ID_F10, "F10" }, + { ITEM_ID_F11, "F11" }, + { ITEM_ID_F12, "F12" }, + { ITEM_ID_F13, "F13" }, + { ITEM_ID_F14, "F14" }, + { ITEM_ID_F15, "F15" }, + { ITEM_ID_ESC, "ESC" }, + { ITEM_ID_TILDE, "TILDE" }, + { ITEM_ID_MINUS, "MINUS" }, + { ITEM_ID_EQUALS, "EQUALS" }, + { ITEM_ID_BACKSPACE, "BACKSPACE" }, + { ITEM_ID_TAB, "TAB" }, + { ITEM_ID_OPENBRACE, "OPENBRACE" }, + { ITEM_ID_CLOSEBRACE, "CLOSEBRACE" }, + { ITEM_ID_ENTER, "ENTER" }, + { ITEM_ID_COLON, "COLON" }, + { ITEM_ID_QUOTE, "QUOTE" }, + { ITEM_ID_BACKSLASH, "BACKSLASH" }, + { ITEM_ID_BACKSLASH2, "BACKSLASH2" }, + { ITEM_ID_COMMA, "COMMA" }, + { ITEM_ID_STOP, "STOP" }, + { ITEM_ID_SLASH, "SLASH" }, + { ITEM_ID_SPACE, "SPACE" }, + { ITEM_ID_INSERT, "INSERT" }, + { ITEM_ID_DEL, "DEL" }, + { ITEM_ID_HOME, "HOME" }, + { ITEM_ID_END, "END" }, + { ITEM_ID_PGUP, "PGUP" }, + { ITEM_ID_PGDN, "PGDN" }, + { ITEM_ID_LEFT, "LEFT" }, + { ITEM_ID_RIGHT, "RIGHT" }, + { ITEM_ID_UP, "UP" }, + { ITEM_ID_DOWN, "DOWN" }, + { ITEM_ID_0_PAD, "0PAD" }, + { ITEM_ID_1_PAD, "1PAD" }, + { ITEM_ID_2_PAD, "2PAD" }, + { ITEM_ID_3_PAD, "3PAD" }, + { ITEM_ID_4_PAD, "4PAD" }, + { ITEM_ID_5_PAD, "5PAD" }, + { ITEM_ID_6_PAD, "6PAD" }, + { ITEM_ID_7_PAD, "7PAD" }, + { ITEM_ID_8_PAD, "8PAD" }, + { ITEM_ID_9_PAD, "9PAD" }, + { ITEM_ID_SLASH_PAD, "SLASHPAD" }, + { ITEM_ID_ASTERISK, "ASTERISK" }, + { ITEM_ID_MINUS_PAD, "MINUSPAD" }, + { ITEM_ID_PLUS_PAD, "PLUSPAD" }, + { ITEM_ID_DEL_PAD, "DELPAD" }, + { ITEM_ID_ENTER_PAD, "ENTERPAD" }, + { ITEM_ID_PRTSCR, "PRTSCR" }, + { ITEM_ID_PAUSE, "PAUSE" }, + { ITEM_ID_LSHIFT, "LSHIFT" }, + { ITEM_ID_RSHIFT, "RSHIFT" }, + { ITEM_ID_LCONTROL, "LCONTROL" }, + { ITEM_ID_RCONTROL, "RCONTROL" }, + { ITEM_ID_LALT, "LALT" }, + { ITEM_ID_RALT, "RALT" }, + { ITEM_ID_SCRLOCK, "SCRLOCK" }, + { ITEM_ID_NUMLOCK, "NUMLOCK" }, + { ITEM_ID_CAPSLOCK, "CAPSLOCK" }, + { ITEM_ID_LWIN, "LWIN" }, + { ITEM_ID_RWIN, "RWIN" }, + { ITEM_ID_MENU, "MENU" }, + { ITEM_ID_CANCEL, "CANCEL" }, + + /* standard mouse/joystick/gun codes */ + { ITEM_ID_XAXIS, "XAXIS" }, + { ITEM_ID_YAXIS, "YAXIS" }, + { ITEM_ID_ZAXIS, "ZAXIS" }, + { ITEM_ID_RXAXIS, "RXAXIS" }, + { ITEM_ID_RYAXIS, "RYAXIS" }, + { ITEM_ID_RZAXIS, "RZAXIS" }, + { ITEM_ID_SLIDER1, "SLIDER1" }, + { ITEM_ID_SLIDER2, "SLIDER2" }, + { ITEM_ID_BUTTON1, "BUTTON1" }, + { ITEM_ID_BUTTON2, "BUTTON2" }, + { ITEM_ID_BUTTON3, "BUTTON3" }, + { ITEM_ID_BUTTON4, "BUTTON4" }, + { ITEM_ID_BUTTON5, "BUTTON5" }, + { ITEM_ID_BUTTON6, "BUTTON6" }, + { ITEM_ID_BUTTON7, "BUTTON7" }, + { ITEM_ID_BUTTON8, "BUTTON8" }, + { ITEM_ID_BUTTON9, "BUTTON9" }, + { ITEM_ID_BUTTON10, "BUTTON10" }, + { ITEM_ID_BUTTON11, "BUTTON11" }, + { ITEM_ID_BUTTON12, "BUTTON12" }, + { ITEM_ID_BUTTON13, "BUTTON13" }, + { ITEM_ID_BUTTON14, "BUTTON14" }, + { ITEM_ID_BUTTON15, "BUTTON15" }, + { ITEM_ID_BUTTON16, "BUTTON16" }, + { ITEM_ID_START, "START" }, + { ITEM_ID_SELECT, "SELECT" }, + + { ~0, NULL } +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* array of devices for each class */ +static input_device_list device_list[DEVICE_CLASS_MAXIMUM]; +static input_code code_pressed_memory[MAX_PRESSED_SWITCHES]; + +/* device configuration */ +static UINT8 steadykey_enabled; +static UINT8 lightgun_reload_button; +static const char * joystick_map_default; +static INT32 joystick_deadzone; +static INT32 joystick_saturation; + +/* standard joystick mappings */ +const char * joystick_map_8way = "7778...4445"; +const char * joystick_map_4way_sticky = "s8.4s8.44s8.4445"; +const char * joystick_map_4way_diagonal = "4444s8888..444458888.444555888.ss5.222555666.222256666.2222s6666.2222s6666"; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void input_frame(running_machine *machine); +static input_device_item *input_code_item(input_code code); +static INT32 convert_absolute_value(input_code code, input_device_item *item); +static INT32 convert_relative_value(input_code code, input_device_item *item); +static INT32 convert_switch_value(input_code code, input_device_item *item); +static INT32 apply_deadzone_and_saturation(input_code code, INT32 result); +static int joystick_map_parse(const char *mapstring, joystick_map *map); +static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + input_code_device - given an input_code return + a pointer to the associated device +-------------------------------------------------*/ + +INLINE input_device *input_code_device(input_code code) +{ + /* if the class is valid... */ + input_device_class devclass = INPUT_CODE_DEVCLASS(code); + if (devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM) + { + /* ...and the index is valid for that class, return a pointer to the device */ + int devindex = INPUT_CODE_DEVINDEX(code); + if (devindex < device_list[devclass].count) + return &device_list[devclass].list[devindex]; + } + + /* otherwise, return NULL */ + return NULL; +} + + +/*------------------------------------------------- + device_item_to_code - convert a device/item + pair to a standard code +-------------------------------------------------*/ + +INLINE input_code device_item_to_code(input_device *device, input_item_id itemid) +{ + int devindex = device->devindex; + + assert(devindex < device_list[device->devclass].count); + assert(itemid < ITEM_ID_ABSOLUTE_MAXIMUM); + assert(device->item[itemid] != NULL); + + return INPUT_CODE(device->devclass, devindex, device->item[itemid]->itemclass, ITEM_MODIFIER_NONE, itemid); +} + + +/*------------------------------------------------- + input_item_standard_class - return the class + of a standard item +-------------------------------------------------*/ + +INLINE input_item_class input_item_standard_class(input_device_class devclass, input_item_id itemid) +{ + /* most everything standard is a switch, apart from the axes */ + if (itemid == ITEM_ID_OTHER_SWITCH || itemid < ITEM_ID_XAXIS || itemid > ITEM_ID_SLIDER2) + return ITEM_CLASS_SWITCH; + + /* standard mouse axes are relative */ + else if (devclass == DEVICE_CLASS_MOUSE || itemid == ITEM_ID_OTHER_AXIS_RELATIVE) + return ITEM_CLASS_RELATIVE; + + /* all other standard axes are absolute */ + else + return ITEM_CLASS_ABSOLUTE; +} + + +/*------------------------------------------------- + input_item_update_value - update the value + of an input item +-------------------------------------------------*/ + +INLINE void input_item_update_value(input_device_item *item) +{ + item->current = (*item->getstate)(device_list[item->devclass].list[item->devindex].internal, item->internal); +} + + +/*------------------------------------------------- + code_pressed_memory_reset - reset the array + of memory for pressed switches +-------------------------------------------------*/ + +INLINE void code_pressed_memory_reset(void) +{ + int memnum; + for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++) + code_pressed_memory[memnum] = INPUT_CODE_INVALID; +} + + +/*------------------------------------------------- + string_to_code - convert a string to a code + via table lookup +-------------------------------------------------*/ + +INLINE UINT32 string_to_code(const code_string_table *table, const char *string) +{ + /* find a matching string */ + for ( ; table->code != ~0; table++) + if (strcmp(string, table->string) == 0) + return table->code; + + /* on failure, return ~0 */ + return ~0; +} + + +/*------------------------------------------------- + code_to_string - convert a code to a string + via table lookup +-------------------------------------------------*/ + +INLINE const char *code_to_string(const code_string_table *table, UINT32 code) +{ + /* find a matching code, or */ + for ( ; table->code != ~0; table++) + if (table->code == code) + return table->string; + + /* return the default string */ + return table->string; +} + + + +/*************************************************************************** + INITIALIZATION +***************************************************************************/ + +/*------------------------------------------------- + input_init - initialize the input lists +-------------------------------------------------*/ + +void input_init(running_machine *machine) +{ + joystick_map map; + + /* reset the device lists */ + memset(device_list, 0, sizeof(device_list)); + + /* reset code memory */ + code_pressed_memory_reset(); + + /* request a per-frame callback for bookkeeping */ + add_frame_callback(machine, input_frame); + + /* read input enable options */ + device_list[DEVICE_CLASS_KEYBOARD].enabled = TRUE; + device_list[DEVICE_CLASS_MOUSE].enabled = options_get_bool(mame_options(), OPTION_MOUSE); + device_list[DEVICE_CLASS_LIGHTGUN].enabled = options_get_bool(mame_options(), OPTION_LIGHTGUN); + device_list[DEVICE_CLASS_JOYSTICK].enabled = options_get_bool(mame_options(), OPTION_JOYSTICK); + + /* read input device multi options */ + device_list[DEVICE_CLASS_KEYBOARD].multi = options_get_bool(mame_options(), OPTION_MULTIKEYBOARD); + device_list[DEVICE_CLASS_MOUSE].multi = options_get_bool(mame_options(), OPTION_MULTIMOUSE); + device_list[DEVICE_CLASS_LIGHTGUN].multi = TRUE; + device_list[DEVICE_CLASS_JOYSTICK].multi = TRUE; + + /* read other input options */ + steadykey_enabled = options_get_bool(mame_options(), OPTION_STEADYKEY); + lightgun_reload_button = options_get_bool(mame_options(), OPTION_OFFSCREEN_RELOAD); + joystick_deadzone = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_DEADZONE) * INPUT_ABSOLUTE_MAX); + joystick_saturation = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_SATURATION) * INPUT_ABSOLUTE_MAX); + + /* get the default joystick map */ + joystick_map_default = options_get_string(mame_options(), OPTION_JOYSTICK_MAP); + if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0) + joystick_map_default = joystick_map_8way; + if (!joystick_map_parse(joystick_map_default, &map)) + mame_printf_error("Invalid joystick map: %s\n", joystick_map_default); + else if (joystick_map_default != joystick_map_8way) + joystick_map_print("Input: Default joystick map", joystick_map_default, &map); +} + + +/*------------------------------------------------- + input_device_class_enable - enable or disable + a device class +-------------------------------------------------*/ + +void input_device_class_enable(input_device_class devclass, UINT8 enable) +{ + assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM); + device_list[devclass].enabled = enable; +} + + +/*------------------------------------------------- + input_enable_device_class - is a device class + enabled? +-------------------------------------------------*/ + +UINT8 input_device_class_enabled(input_device_class devclass) +{ + assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM); + return device_list[devclass].enabled; +} + + +/*------------------------------------------------- + input_device_set_joystick_map - set the + joystick map for a device +-------------------------------------------------*/ + +int input_device_set_joystick_map(int devindex, const char *mapstring) +{ + int startindex = devindex; + int stopindex = devindex; + joystick_map map; + int joynum; + + /* parse the map */ + if (!joystick_map_parse(mapstring, &map)) + return FALSE; + + /* devindex -1 means set the same for all */ + if (devindex == -1) + { + startindex = 0; + stopindex = device_list[DEVICE_CLASS_JOYSTICK].count - 1; + joystick_map_print("Input: Changing default joystick map", mapstring, &map); + } + + /* ignore if out of range */ + else if (devindex >= device_list[DEVICE_CLASS_JOYSTICK].count) + return TRUE; + + /* iterate over joysticks and set the map */ + for (joynum = startindex; joynum <= stopindex; joynum++) + device_list[DEVICE_CLASS_JOYSTICK].list[joynum].joymap = map; + return TRUE; +} + + +/*------------------------------------------------- + input_frame - per-frame callback for various + bookkeeping +-------------------------------------------------*/ + +static void input_frame(running_machine *machine) +{ + /* if steadykey is enabled, do processing here */ + if (steadykey_enabled) + { + int devnum; + + /* iterate over keyboards */ + for (devnum = 0; devnum < device_list[DEVICE_CLASS_KEYBOARD].count; devnum++) + { + input_device *device = &device_list[DEVICE_CLASS_KEYBOARD].list[devnum]; + input_item_id itemid; + int changed = FALSE; + + /* update the state of all the keys and see if any changed state */ + for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++) + { + input_device_item *item = device->item[itemid]; + if (item != NULL && item->itemclass == ITEM_CLASS_SWITCH) + { + input_item_update_value(item); + if ((item->current ^ item->oldkey) & 1) + { + changed = TRUE; + + /* if the keypress was missed, turn it on for one frame */ + if (((item->current | item->steadykey) & 1) == 0) + item->steadykey = 1; + } + } + } + + /* if the keyboard state is stable, copy it over */ + for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++) + { + input_device_item *item = device->item[itemid]; + if (item != NULL && item->itemclass == ITEM_CLASS_SWITCH) + { + if (!changed) + item->steadykey = item->current; + item->oldkey = item->current; + } + } + } + } +} + + + +/*************************************************************************** + OSD CONFIGURATION AND ACCESS +***************************************************************************/ + +/*------------------------------------------------- + input_device_add - add a new input device +-------------------------------------------------*/ + +input_device *input_device_add(input_device_class devclass, const char *name, void *internal) +{ + input_device_list *devlist = &device_list[devclass]; + input_device *device; + + assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call input_device_add at init time!"); + assert(name != NULL); + assert(devclass != DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM); + + /* allocate a new device */ + devlist->list = auto_realloc(devlist->list, (devlist->count + 1) * sizeof(devlist->list[0])); + device = &devlist->list[devlist->count++]; + memset(device, 0, sizeof(*device)); + + /* fill in the data */ + device->name = astring_cpyc(auto_astring_alloc(), name); + device->devclass = devclass; + device->devindex = devlist->count - 1; + device->internal = internal; + + /* default to 8-way map for joysticks */ + if (devclass == DEVICE_CLASS_JOYSTICK) + { + joystick_map_parse(joystick_map_default, &device->joymap); + device->lastmap = JOYSTICK_MAP_NEUTRAL; + } + + mame_printf_verbose("Input: Adding %s #%d: %s\n", code_to_string(devclass_string_table, devclass), devlist->count, astring_c(device->name)); + return device; +} + + +/*------------------------------------------------- + input_device_item_add - add a new item to an + input device +-------------------------------------------------*/ + +void input_device_item_add(input_device *device, const char *name, void *internal, input_item_id itemid, item_get_state_cb getstate) +{ + input_device_item *item; + + assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call input_device_item_add at init time!"); + assert(name != NULL); + assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM); + assert(getstate != NULL); + + /* if we have a generic ID, pick a new internal one */ + if (itemid >= ITEM_ID_OTHER_SWITCH && itemid <= ITEM_ID_OTHER_AXIS_RELATIVE) + for (itemid = ITEM_ID_MAXIMUM + 1; itemid <= ITEM_ID_ABSOLUTE_MAXIMUM; itemid++) + if (device->item[itemid] == NULL) + break; + assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM); + + /* make sure we don't have any overlap */ + assert(device->item[itemid] == NULL); + + /* allocate a new item and copy data into it */ + item = auto_malloc(sizeof(*item)); + memset(item, 0, sizeof(*item)); + device->item[itemid] = item; + device->maxitem = MAX(device->maxitem, itemid); + + /* copy in the data passed in from the item list */ + item->devclass = device->devclass; + item->devindex = device->devindex; + item->name = astring_cpyc(auto_astring_alloc(), name); + item->token = NULL; + item->internal = internal; + item->itemclass = input_item_standard_class(device->devclass, itemid); + item->itemid = itemid; + item->getstate = getstate; + + /* if we're custom, create a tokenized name */ + if (itemid > ITEM_ID_MAXIMUM) + { + /* copy the item name, removing spaces/underscores and making all caps */ + item->token = astring_toupper(astring_cpyc(auto_astring_alloc(), name)); + astring_delchr(item->token, ' '); + astring_delchr(item->token, '_'); + } + + /* otherwise, make sure we have a valid standard token */ + else + { + assert(code_to_string(itemid_token_table, itemid) != NULL); + } +} + + + +/*************************************************************************** + STATE QUERIES +***************************************************************************/ + +/*------------------------------------------------- + input_code_value - return the value of a + given input code +-------------------------------------------------*/ + +INT32 input_code_value(input_code code) +{ + input_device_class devclass = INPUT_CODE_DEVCLASS(code); + int startindex = INPUT_CODE_DEVINDEX(code); + int stopindex = startindex; + INT32 result = 0; + int curindex; + + profiler_mark(PROFILER_INPUT); + + /* return 0 for any disabled or invalid device classes */ + if (devclass <= DEVICE_CLASS_INVALID || devclass >= DEVICE_CLASS_MAXIMUM || !device_list[devclass].enabled) + goto exit; + if (startindex >= device_list[devclass].count) + goto exit; + + /* if this is not a multi device, only return data for item 0 */ + if (!device_list[devclass].multi) + { + if (startindex != 0) + goto exit; + + /* otherwise, iterate over all */ + startindex = 0; + stopindex = device_list[devclass].count - 1; + } + + /* iterate over all indices */ + for (curindex = startindex; curindex <= stopindex; curindex++) + { + /* lookup the item for the appropriate index */ + input_device_item *item = input_code_item(INPUT_CODE_SET_DEVINDEX(code, curindex)); + if (item == NULL) + continue; + + /* update the value of this item */ + input_item_update_value(item); + + /* process items according to their native type */ + switch (item->itemclass) + { + case ITEM_CLASS_ABSOLUTE: + result = convert_absolute_value(code, item); + break; + + case ITEM_CLASS_RELATIVE: + result += convert_relative_value(code, item); + break; + + case ITEM_CLASS_SWITCH: + result |= convert_switch_value(code, item); + break; + + default: + break; + } + } + +exit: + profiler_mark(PROFILER_END); + return result; +} + + +/*------------------------------------------------- + input_code_pressed - return non-zero if a + given input code has been pressed +-------------------------------------------------*/ + +int input_code_pressed(input_code code) +{ + return (input_code_value(code) != 0); +} + + +/*------------------------------------------------- + input_code_pressed_once - return non-zero if a + given input code has transitioned from off to + on since the last call +-------------------------------------------------*/ + +int input_code_pressed_once(input_code code) +{ + int curvalue = input_code_pressed(code); + int memnum, empty = -1; + + /* look for the code in the memory */ + for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++) + { + /* were we previous pressed on the last time through here? */ + if (code_pressed_memory[memnum] == code) + { + /* if no longer pressed, clear entry */ + if (!curvalue) + code_pressed_memory[memnum] = INPUT_CODE_INVALID; + + /* always return 0 */ + return 0; + } + + /* remember the first empty entry */ + if (empty == -1 && code_pressed_memory[memnum] == INPUT_CODE_INVALID) + empty = memnum; + } + + /* if we get here, we were not previously pressed; if still not pressed, return 0 */ + if (!curvalue) + return 0; + + /* otherwise, add ourself to the memory and return 1 */ + assert(empty != -1); + if (empty != -1) + code_pressed_memory[empty] = code; + return 1; +} + + +/*------------------------------------------------- + input_code_poll_switches - poll for any input +-------------------------------------------------*/ + +input_code input_code_poll_switches(int reset) +{ + input_device_class devclass; + + /* if resetting memory, do it now */ + if (reset) + code_pressed_memory_reset(); + + /* iterate over device classes and devices */ + for (devclass = DEVICE_CLASS_INVALID + 1; devclass < DEVICE_CLASS_MAXIMUM; devclass++) + { + input_device_list *devlist = &device_list[devclass]; + int devnum; + + /* iterate over devices within each class */ + for (devnum = 0; devnum < devlist->count; devnum++) + { + input_device *device = &devlist->list[devnum]; + input_item_id itemid; + + /* iterate over items within each device */ + for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++) + { + input_device_item *item = device->item[itemid]; + if (item != NULL) + { + input_code code = device_item_to_code(device, itemid); + + /* if the item is natively a switch, poll it */ + if (item->itemclass == ITEM_CLASS_SWITCH) + { + if (input_code_pressed_once(code)) + return code; + } + + /* otherwise, poll axes digitally */ + else + { + code = INPUT_CODE_SET_ITEMCLASS(code, ITEM_CLASS_SWITCH); + + /* if this is a joystick X axis, check with left/right modifiers */ + if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS) + { + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_LEFT); + if (input_code_pressed_once(code)) + return code; + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_RIGHT); + if (input_code_pressed_once(code)) + return code; + } + + /* if this is a joystick Y axis, check with up/down modifiers */ + else if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_YAXIS) + { + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_UP); + if (input_code_pressed_once(code)) + return code; + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_DOWN); + if (input_code_pressed_once(code)) + return code; + } + + /* any other axis, check with pos/neg modifiers */ + else + { + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_POS); + if (input_code_pressed_once(code)) + return code; + code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_NEG); + if (input_code_pressed_once(code)) + return code; + } + } + } + } + } + } + + /* if nothing, return an invalid code */ + return INPUT_CODE_INVALID; +} + + +/*------------------------------------------------- + input_code_poll_axes - poll for any input +-------------------------------------------------*/ + +input_code input_code_poll_axes(int reset) +{ + input_device_class devclass; + + /* iterate over device classes and devices */ + for (devclass = DEVICE_CLASS_INVALID + 1; devclass < DEVICE_CLASS_MAXIMUM; devclass++) + { + input_device_list *devlist = &device_list[devclass]; + int devnum; + + /* iterate over devices within each class */ + for (devnum = 0; devnum < devlist->count; devnum++) + { + input_device *device = &devlist->list[devnum]; + input_item_id itemid; + + /* iterate over items within each device */ + for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++) + { + input_device_item *item = device->item[itemid]; + if (item != NULL) + { + input_code code = device_item_to_code(device, itemid); + INT32 curval, diff; + + /* skip any switches or already-reported axes */ + if (item->itemclass == ITEM_CLASS_SWITCH) + continue; + + /* poll the current value and reset the memory */ + curval = input_code_value(code); + if (reset) + item->memory = curval; + + /* if we've already reported this one, don't bother */ + if (item->memory == INVALID_AXIS_VALUE) + continue; + + /* compute the diff against memory */ + diff = curval - item->memory; + if (diff < 0) + diff = -diff; + + /* for absolute axes, look for 25% of maximum */ + if (item->itemclass == ITEM_CLASS_ABSOLUTE && diff > (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN) / 4) + { + item->memory = INVALID_AXIS_VALUE; + return code; + } + + /* for relative axes, look for ~20 pixels movement */ + if (item->itemclass == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL) + { + item->memory = INVALID_AXIS_VALUE; + return code; + } + } + } + } + } + + /* if nothing, return an invalid code */ + return INPUT_CODE_INVALID; +} + + + +/*************************************************************************** + STRINGS AND TOKENIZATION +***************************************************************************/ + +/*------------------------------------------------- + input_code_name - convert an input code into + a friendly name +-------------------------------------------------*/ + +astring *input_code_name(astring *string, input_code code) +{ + input_device_item *item = input_code_item(code); + const char *devclass; + const char *devcode; + const char *modifier; + char devindex[10]; + + /* if nothing there, return an empty string */ + if (item == NULL) + return astring_reset(string); + + /* determine the devclass part */ + devclass = code_to_string(devclass_string_table, INPUT_CODE_DEVCLASS(code)); + + /* determine the devindex part */ + sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1); + + /* if we're unifying all devices, don't display a number */ + if (!device_list[INPUT_CODE_DEVCLASS(code)].multi) + devindex[0] = 0; + + /* keyboard 0 doesn't show a class or index if it is the only one */ + if (item->devclass == DEVICE_CLASS_KEYBOARD && device_list[DEVICE_CLASS_KEYBOARD].count == 1) + { + devclass = ""; + devindex[0] = 0; + } + + /* devcode part comes from the item name */ + devcode = astring_c(item->name); + + /* determine the modifier part */ + modifier = code_to_string(modifier_string_table, INPUT_CODE_MODIFIER(code)); + + /* devcode is redundant with joystick switch left/right/up/down */ + if (item->devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH) + if (INPUT_CODE_MODIFIER(code) >= ITEM_MODIFIER_LEFT && INPUT_CODE_MODIFIER(code) <= ITEM_MODIFIER_DOWN) + devcode = ""; + + /* concatenate the strings */ + astring_cpyc(string, devclass); + if (devindex[0] != 0) + astring_catc(astring_catc(string, " "), devindex); + if (devcode[0] != 0) + astring_catc(astring_catc(string, " "), devcode); + if (modifier[0] != 0) + astring_catc(astring_catc(string, " "), modifier); + + /* delete any leading spaces */ + return astring_trimspace(string); +} + + +/*------------------------------------------------- + input_code_to_token - create a token for + a given code +-------------------------------------------------*/ + +astring *input_code_to_token(astring *string, input_code code) +{ + input_device_item *item = input_code_item(code); + const char *devclass; + const char *devcode; + const char *itemclass; + const char *modifier; + char devindex[10]; + + /* determine the devclass part */ + devclass = code_to_string(devclass_token_table, INPUT_CODE_DEVCLASS(code)); + + /* determine the devindex part; keyboard 0 doesn't show an index */ + sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1); + if (INPUT_CODE_DEVCLASS(code) == DEVICE_CLASS_KEYBOARD && INPUT_CODE_DEVINDEX(code) == 0) + devindex[0] = 0; + + /* determine the itemid part; look up in the table if we don't have a token */ + if (item != NULL && item->token != NULL) + devcode = astring_c(item->token); + else + devcode = code_to_string(itemid_token_table, INPUT_CODE_ITEMID(code)); + assert(devcode != NULL); + + /* determine the modifier part */ + modifier = code_to_string(modifier_token_table, INPUT_CODE_MODIFIER(code)); + + /* determine the itemclass part; if we match the native class, we don't include this */ + if (item == NULL || item->itemclass == INPUT_CODE_ITEMCLASS(code)) + itemclass = ""; + else + itemclass = code_to_string(itemclass_token_table, INPUT_CODE_ITEMCLASS(code)); + + /* concatenate the strings */ + astring_cpyc(string, devclass); + if (devindex[0] != 0) + astring_catc(astring_catc(string, "_"), devindex); + if (devcode[0] != 0) + astring_catc(astring_catc(string, "_"), devcode); + if (modifier[0] != 0) + astring_catc(astring_catc(string, "_"), modifier); + if (itemclass[0] != 0) + astring_catc(astring_catc(string, "_"), itemclass); + return string; +} + + +/*------------------------------------------------- + input_code_from_token - extract an input + code from a token +-------------------------------------------------*/ + +input_code input_code_from_token(const char *_token) +{ + UINT32 devclass, itemid, devindex, modifier, standard; + UINT32 itemclass = ITEM_CLASS_INVALID; + input_code code = INPUT_CODE_INVALID; + astring *token[6] = { NULL }; + int numtokens, curtok; + + /* copy the token and break it into pieces */ + for (numtokens = 0; numtokens < ARRAY_LENGTH(token); ) + { + /* make a token up to the next underscore */ + char *score = strchr(_token, '_'); + token[numtokens++] = astring_dupch(_token, (score == NULL) ? strlen(_token) : (score - _token)); + + /* if we hit the end, we're done, else advance our pointer */ + if (score == NULL) + break; + _token = score + 1; + } + + /* first token should be the devclass */ + curtok = 0; + devclass = string_to_code(devclass_token_table, astring_c(token[curtok++])); + if (devclass == ~0) + goto exit; + + /* second token might be index; look for number */ + devindex = 0; + if (numtokens > 2 && sscanf(astring_c(token[curtok]), "%d", &devindex) == 1) + { + curtok++; + devindex--; + } + if (curtok >= numtokens) + goto exit; + + /* next token is the item ID */ + itemid = string_to_code(itemid_token_table, astring_c(token[curtok])); + standard = (itemid != ~0); + + /* if we're a standard code, default the itemclass based on it */ + if (standard) + itemclass = input_item_standard_class(devclass, itemid); + + /* otherwise, keep parsing */ + else + { + input_device *device; + + /* if this is an invalid device, we have nothing to look up */ + if (devindex >= device_list[devclass].count) + goto exit; + device = &device_list[devclass].list[devindex]; + + /* if not a standard code, look it up in the device specific codes */ + for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++) + { + input_device_item *item = device->item[itemid]; + if (item != NULL && item->token != NULL && astring_cmp(token[curtok], item->token) == 0) + { + /* take the itemclass from the item */ + itemclass = item->itemclass; + break; + } + } + + /* bail on fail */ + if (itemid > device->maxitem) + goto exit; + } + curtok++; + + /* if we have another token, it is probably a modifier */ + modifier = ITEM_MODIFIER_NONE; + if (curtok < numtokens) + { + modifier = string_to_code(modifier_token_table, astring_c(token[curtok])); + if (modifier != ~0) + curtok++; + else + modifier = ITEM_MODIFIER_NONE; + } + + /* if we have another token, it is the item class */ + if (curtok < numtokens) + { + UINT32 temp = string_to_code(itemclass_token_table, astring_c(token[curtok])); + if (temp != ~0) + { + curtok++; + itemclass = temp; + } + } + + /* we should have consumed all tokens */ + if (curtok != numtokens) + goto exit; + + /* assemble the final code */ + code = INPUT_CODE(devclass, devindex, itemclass, modifier, itemid); + +exit: + for (curtok = 0; curtok < ARRAY_LENGTH(token); curtok++) + if (token[curtok] != NULL) + astring_free(token[curtok]); + return code; +} + + + +/*************************************************************************** + INTERNAL FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + input_code_item - get a pointer to the item + from the code +-------------------------------------------------*/ + +static input_device_item *input_code_item(input_code code) +{ + input_device *device = input_code_device(code); + input_item_id itemid; + + /* if no device, we fail */ + if (device == NULL) + return NULL; + + /* if the devcode is in range, return a pointer to the item */ + itemid = INPUT_CODE_ITEMID(code); + if (itemid < ARRAY_LENGTH(device->item)) + return device->item[itemid]; + + /* otherwise fail with NULL */ + return NULL; +} + + +/*------------------------------------------------- + convert_absolute_value - convert an absolute + value into the class specified by code +-------------------------------------------------*/ + +static INT32 convert_absolute_value(input_code code, input_device_item *item) +{ + /* make sure values are valid */ + assert(item->current >= INPUT_ABSOLUTE_MIN && item->current <= INPUT_ABSOLUTE_MAX); + + /* relative value conversion not supported */ + if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE) + return 0; + + /* if we want the absolute value, process it according to the modifier field */ + else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE) + { + input_item_modifier modifier = INPUT_CODE_MODIFIER(code); + INT32 result = apply_deadzone_and_saturation(code, item->current); + + /* if we're doing a lightgun reload hack, override the value */ + if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN) + { + input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2]; + if (button2_item != NULL) + { + /* if it is pressed, return (min,max) */ + input_item_update_value(button2_item); + if (button2_item->current) + result = (INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX; + } + } + + /* standard axis: apply deadzone and saturation */ + if (modifier == ITEM_MODIFIER_NONE) + return result; + + /* positive/negative: scale to full axis */ + else if (modifier == ITEM_MODIFIER_POS) + return MAX(result, 0) * 2 + INPUT_ABSOLUTE_MIN; + else if (modifier == ITEM_MODIFIER_NEG) + return MAX(-result, 0) * 2 + INPUT_ABSOLUTE_MIN; + } + + /* if we want a switch value, process it according to the modifier field */ + else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH) + { + input_item_modifier modifier = INPUT_CODE_MODIFIER(code); + + /* left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert */ + if (modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN && item->devclass == DEVICE_CLASS_JOYSTICK) + { + input_device *device = &device_list[item->devclass].list[item->devindex]; + input_device_item *xaxis_item = device->item[ITEM_ID_XAXIS]; + input_device_item *yaxis_item = device->item[ITEM_ID_YAXIS]; + if (xaxis_item != NULL && yaxis_item != NULL) + { + INT32 xaxisval, yaxisval; + UINT8 mapval; + + /* determine which item we didn't update, and update it */ + assert(item == xaxis_item || item == yaxis_item); + input_item_update_value((item == xaxis_item) ? yaxis_item : xaxis_item); + + /* now map the X and Y axes to a 9x9 grid */ + xaxisval = ((xaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1); + yaxisval = ((yaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1); + mapval = device->joymap.map[yaxisval][xaxisval]; + + /* handle stickiness */ + if (mapval == JOYSTICK_MAP_STICKY) + mapval = device->lastmap; + else + device->lastmap = mapval; + + /* return based on whether the appropriate bit is set */ + return (mapval >> (modifier - ITEM_MODIFIER_LEFT)) & 1; + } + } + + /* positive/negative: TRUE if past the deadzone in either direction */ + if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN) + return (apply_deadzone_and_saturation(code, item->current) > 0); + else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP) + return (apply_deadzone_and_saturation(code, item->current) < 0); + } + + return 0; +} + + +/*------------------------------------------------- + convert_relative_value - convert a relative + value into the class specified by code +-------------------------------------------------*/ + +static INT32 convert_relative_value(input_code code, input_device_item *item) +{ + /* if we want relative values, take it as-is */ + if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE) + return item->current; + + /* absolute value conversion not supported */ + else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE) + return 0; + + /* if we want a switch value, process it according to the modifier field */ + else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH) + { + input_item_modifier modifier = INPUT_CODE_MODIFIER(code); + + /* positive/negative: TRUE if non-zero in either direction */ + if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN) + return (item->current > 0); + else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP) + return (item->current < 0); + } + + return 0; +} + + +/*------------------------------------------------- + convert_switch_value - convert a switch + value into the class specified by code +-------------------------------------------------*/ + +static INT32 convert_switch_value(input_code code, input_device_item *item) +{ + /* only a switch is supported */ + if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH) + { + /* if we're doing a lightgun reload hack, button 1 and 2 operate differently */ + if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN) + { + /* button 1 is pressed if either button 1 or 2 are active */ + if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON1) + { + input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2]; + if (button2_item != NULL) + { + input_item_update_value(button2_item); + return item->current | button2_item->current; + } + } + + /* button 2 is never officially pressed */ + if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON2) + return 0; + } + + /* steadykey for keyboards */ + if (steadykey_enabled && item->devclass == DEVICE_CLASS_KEYBOARD) + return item->steadykey; + + /* everything else is just the current value as-is */ + return item->current; + } + + return 0; +} + + +/*------------------------------------------------- + apply_deadzone_and_saturation - apply global + deadzone and saturation parameters to an + absolute value +-------------------------------------------------*/ + +static INT32 apply_deadzone_and_saturation(input_code code, INT32 result) +{ + int negative = FALSE; + + /* ignore if not a joystick */ + if (INPUT_CODE_DEVCLASS(code) != DEVICE_CLASS_JOYSTICK) + return result; + + /* properties are symmetric */ + if (result < 0) + { + negative = TRUE; + result = -result; + } + + /* if in the deadzone, return 0 */ + if (result < joystick_deadzone) + result = 0; + + /* if saturated, return the max */ + else if (result > joystick_saturation) + result = INPUT_ABSOLUTE_MAX; + + /* otherwise, scale */ + else + result = (INT64)(result - joystick_deadzone) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(joystick_saturation - joystick_deadzone); + + /* apply sign and return */ + return negative ? -result : result; +} + + +/*------------------------------------------------- + joystick_map_parse - parse a string into + a joystick map +-------------------------------------------------*/ + +static int joystick_map_parse(const char *mapstring, joystick_map *map) +{ + int rownum, colnum; + + /* iterate over rows */ + for (rownum = 0; rownum < 9; rownum++) + { + /* if we're done, copy from another row */ + if (*mapstring == 0 || *mapstring == '.') + { + int symmetric = (rownum >= 5 && *mapstring == 0); + const UINT8 *srcrow = &map->map[symmetric ? (8 - rownum) : (rownum - 1)][0]; + + /* if this is row 0, we don't have a source row -- invalid */ + if (rownum == 0) + return FALSE; + + /* copy from the srcrow, applying up/down symmetry if in the bottom half */ + for (colnum = 0; colnum < 9; colnum++) + { + UINT8 val = srcrow[colnum]; + if (symmetric) + val = (val & (JOYSTICK_MAP_LEFT | JOYSTICK_MAP_RIGHT)) | ((val & JOYSTICK_MAP_UP) << 1) | ((val & JOYSTICK_MAP_DOWN) >> 1); + map->map[rownum][colnum] = val; + } + } + + /* otherwise, parse this column */ + else + { + for (colnum = 0; colnum < 9; colnum++) + { + /* if we're at the end of row, copy previous to the middle, then apply left/right symmetry */ + if (*mapstring == 0 || *mapstring == '.') + { + int symmetric = (colnum >= 5); + UINT8 val = map->map[rownum][symmetric ? (8 - colnum) : (colnum - 1)]; + if (symmetric) + val = (val & (JOYSTICK_MAP_UP | JOYSTICK_MAP_DOWN)) | ((val & JOYSTICK_MAP_LEFT) << 1) | ((val & JOYSTICK_MAP_RIGHT) >> 1); + map->map[rownum][colnum] = val; + } + + /* otherwise, convert the character to its value */ + else + { + static const UINT8 charmap[] = + { + JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT, + JOYSTICK_MAP_UP, + JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT, + JOYSTICK_MAP_LEFT, + JOYSTICK_MAP_NEUTRAL, + JOYSTICK_MAP_RIGHT, + JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT, + JOYSTICK_MAP_DOWN, + JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT, + JOYSTICK_MAP_STICKY + }; + static const char *validchars = "789456123s"; + const char *ptr = strchr(validchars, *mapstring++); + + /* invalid characters exit immediately */ + if (ptr == NULL) + return FALSE; + map->map[rownum][colnum] = charmap[ptr - validchars]; + } + } + } + + /* if we ended with a period, advance to the next row */ + if (*mapstring == '.') + mapstring++; + } + + return TRUE; +} + + +/*------------------------------------------------- + joystick_map_print - print a joystick map via + the verbose output +-------------------------------------------------*/ + +static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map) +{ + int rownum, colnum; + + mame_printf_verbose("%s = %s\n", header, origstring); + for (rownum = 0; rownum < 9; rownum++) + { + mame_printf_verbose(" "); + for (colnum = 0; colnum < 9; colnum++) + switch (map->map[rownum][colnum]) + { + case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: mame_printf_verbose("7"); break; + case JOYSTICK_MAP_UP: mame_printf_verbose("8"); break; + case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: mame_printf_verbose("9"); break; + case JOYSTICK_MAP_LEFT: mame_printf_verbose("4"); break; + case JOYSTICK_MAP_NEUTRAL: mame_printf_verbose("5"); break; + case JOYSTICK_MAP_RIGHT: mame_printf_verbose("6"); break; + case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: mame_printf_verbose("1"); break; + case JOYSTICK_MAP_DOWN: mame_printf_verbose("2"); break; + case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:mame_printf_verbose("3"); break; + case JOYSTICK_MAP_STICKY: mame_printf_verbose("s"); break; + default: mame_printf_verbose("?"); break; + } + + mame_printf_verbose("\n"); + } +} |