Mercurial > repos > blastem
diff controller_info.c @ 1692:5dacaef602a7 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 05 Jan 2019 00:58:08 -0800 |
parents | 84ef1eb2c96a |
children | 3a8c4ee68568 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/controller_info.c Sat Jan 05 00:58:08 2019 -0800 @@ -0,0 +1,334 @@ +#include <string.h> +#include "render_sdl.h" +#include "controller_info.h" +#include "config.h" +#include "util.h" + +typedef struct { + char const *name; + controller_info info; +} heuristic; + +static heuristic heuristics[] = { + //TODO: Add more heuristic rules + {"DualShock 4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, + {"PS4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, + {"PS3", {.type = TYPE_PSX, .subtype = SUBTYPE_PS3}}, + {"X360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, + {"Xbox 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, + {"X-box 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, + {"Xbox One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}}, + {"X-box One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}}, + {"WiiU", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}}, + {"Wii U", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}}, + {"Nintendo Switch", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_SWITCH}}, + {"Saturn", {.type = TYPE_SEGA, .subtype = SUBTYPE_SATURN}} +}; +const uint32_t num_heuristics = sizeof(heuristics)/sizeof(*heuristics); + +static tern_node *info_config; +static uint8_t loaded; +static const char *subtype_names[] = { + "unknown", + "xbox", + "xbox 360", + "xbone", + "ps2", + "ps3", + "ps4", + "wiiu", + "switch", + "genesis", + "saturn" +}; +static const char *subtype_human_names[] = { + "unknown", + "Xbos", + "Xbox 360", + "Xbox One", + "PS2", + "PS3", + "PS4", + "Wii-U", + "Switch", + "Genesis", + "Saturn" +}; +static const char *variant_names[] = { + "normal", + "6b bumpers", + "6b right" +}; + +static void load_ctype_config(void) +{ + if (!loaded) { + info_config = load_overrideable_config("controller_types.cfg", "controller_types.cfg"); + loaded = 1; + } +} + +controller_info get_controller_info(int joystick) +{ + load_ctype_config(); + char guid_string[33]; + SDL_Joystick *stick = render_get_joystick(joystick); + SDL_GameController *control = render_get_controller(joystick); + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, sizeof(guid_string)); + tern_node *info = tern_find_node(info_config, guid_string); + if (info) { + controller_info res; + char *subtype = tern_find_ptr(info, "subtype"); + res.subtype = SUBTYPE_UNKNOWN; + if (subtype) { + for (int i = 0; i < SUBTYPE_NUM; i++) + { + if (!strcmp(subtype_names[i], subtype)) { + res.subtype = i; + break; + } + } + } + switch (res.subtype) + { + case SUBTYPE_XBOX: + case SUBTYPE_X360: + case SUBTYPE_XBONE: + res.type = TYPE_XBOX; + break; + case SUBTYPE_PS2: + case SUBTYPE_PS3: + case SUBTYPE_PS4: + res.type = TYPE_PSX; + break; + case SUBTYPE_WIIU: + case SUBTYPE_SWITCH: + res.type = TYPE_NINTENDO; + break; + case SUBTYPE_GENESIS: + case SUBTYPE_SATURN: + res.type = TYPE_SEGA; + break; + default: + res.type = TYPE_UNKNOWN; + break; + } + char *variant = tern_find_ptr(info, "variant"); + res.variant = VARIANT_NORMAL; + if (variant) { + for (int i = 0; i < VARIANT_NUM; i++) + { + if (!strcmp(variant_names[i], variant)) { + res.variant = i; + break; + } + } + } + res.name = control ? SDL_GameControllerName(control) : SDL_JoystickName(stick); + if (control) { + SDL_GameControllerClose(control); + } + return res; + } + if (!control) { + return (controller_info) { + .type = TYPE_UNKNOWN, + .subtype = SUBTYPE_UNKNOWN, + .variant = VARIANT_NORMAL, + .name = SDL_JoystickName(stick) + }; + } + const char *name = SDL_GameControllerName(control); + SDL_GameControllerClose(control); + for (uint32_t i = 0; i < num_heuristics; i++) + { + if (strstr(name, heuristics[i].name)) { + controller_info res = heuristics[i].info; + res.name = name; + return res; + } + } + //default to a 360 + return (controller_info){ + .type = TYPE_GENERIC_MAPPING, + .subtype = SUBTYPE_UNKNOWN, + .variant = VARIANT_NORMAL, + .name = name + }; +} + +static void mappings_iter(char *key, tern_val val, uint8_t valtype, void *data) +{ + if (valtype != TVAL_NODE) { + return; + } + char *mapping = tern_find_ptr(val.ptrval, "mapping"); + if (mapping) { + const char *parts[] = {key, ",", mapping}; + char * full = alloc_concat_m(3, parts); + SDL_GameControllerAddMapping(full); + free(full); + } +} + +void controller_add_mappings(void) +{ + load_ctype_config(); + if (info_config) { + tern_foreach(info_config, mappings_iter, NULL); + } +} + +void save_controller_info(int joystick, controller_info *info) +{ + char guid_string[33]; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string)); + tern_node *existing = tern_find_node(info_config, guid_string); + existing = tern_insert_ptr(existing, "subtype", (void *)subtype_names[info->subtype]); + existing = tern_insert_ptr(existing, "variant", (void *)variant_names[info->variant]); + info_config = tern_insert_node(info_config, guid_string, existing); + persist_config_at(info_config, "controller_types.cfg"); + +} + +void save_controller_mapping(int joystick, char *mapping_string) +{ + char guid_string[33]; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string)); + tern_node *existing = tern_find_node(info_config, guid_string); + existing = tern_insert_ptr(existing, "mapping", mapping_string); + info_config = tern_insert_node(info_config, guid_string, existing); + persist_config_at(info_config, "controller_types.cfg"); +} + +char const *labels_xbox[] = { + "A", "B", "X", "Y", "Back", NULL, "Start", "Click", "Click", "White", "Black", "LT", "RT" +}; +char const *labels_360[] = { + "A", "B", "X", "Y", "Back", "Xbox", "Start", "Click", "Click", "LB", "RB", "LT", "RT" +}; +static char const *labels_xbone[] = { + "A", "B", "X", "Y", "View", "Xbox", "Menu", "Click", "Click", "LB", "RB", "LT", "RT" +}; +static char const *labels_ps3[] = { + "cross", "circle", "square", "triangle", "Select", "PS", "Start", "L3", "R3", "L1", "R1", "L2", "R2" +}; +static char const *labels_ps4[] = { + "cross", "circle", "square", "triangle", "Share", "PS", "Options", "L3", "R3", "L1", "R1", "L2", "R2" +}; +static char const *labels_nintendo[] = { + "B", "A", "Y", "X", "-", "Home", "+", "Click", "Click", "L", "R", "ZL", "ZR" +}; +static char const *labels_genesis[] = { + "A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", NULL, "Mode" +}; +static char const *labels_saturn[] = { + "A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", "LT", "RT" +}; + +static const char** label_source(controller_info *info) +{ + if (info->type == TYPE_UNKNOWN || info->type == TYPE_GENERIC_MAPPING || info->subtype ==SUBTYPE_X360) { + return labels_360; + } else if (info->type == TYPE_NINTENDO) { + return labels_nintendo; + } else if (info->type == TYPE_PSX) { + if (info->subtype == SUBTYPE_PS4) { + return labels_ps4; + } else { + return labels_ps3; + } + } else if (info->type == TYPE_XBOX) { + if (info->subtype == SUBTYPE_XBONE) { + return labels_xbone; + } else { + return labels_xbox; + } + } else { + if (info->subtype == SUBTYPE_GENESIS) { + return labels_genesis; + } else { + return labels_saturn; + } + } +} + +const char *get_button_label(controller_info *info, int button) +{ + if (button >= SDL_CONTROLLER_BUTTON_DPAD_UP) { + static char const * dirs[] = {"Up", "Down", "Left", "Right"}; + return dirs[button - SDL_CONTROLLER_BUTTON_DPAD_UP]; + } + return label_source(info)[button]; +} + +static char const *axis_labels[] = { + "Left X", "Left Y", "Right X", "Right Y" +}; +const char *get_axis_label(controller_info *info, int axis) +{ + if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT) { + return axis_labels[axis]; + } else { + return label_source(info)[axis - SDL_CONTROLLER_AXIS_TRIGGERLEFT + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + 1]; + } +} + +char *make_controller_type_key(controller_info *info) +{ + const char *subtype; + if (info->subtype == SUBTYPE_UNKNOWN) { + switch(info->type) + { + case TYPE_XBOX: + subtype = subtype_names[SUBTYPE_X360]; + break; + case TYPE_PSX: + subtype = subtype_names[SUBTYPE_PS4]; + break; + case TYPE_NINTENDO: + subtype = subtype_names[SUBTYPE_SWITCH]; + break; + default: + subtype = "unknown"; + } + } else { + subtype = subtype_names[info->subtype]; + } + const char *variant = variant_names[info->variant]; + const char *parts[] = {subtype, "_", variant}; + char *ret = alloc_concat_m(3, parts); + for (char *cur = ret; *cur; cur++) + { + if (*cur == ' ') + { + *cur = '_'; + } + } + return ret; +} + +char *make_human_readable_type_name(controller_info *info) +{ + const char *base = subtype_human_names[info->subtype]; + char *prefix; + if (info->variant == VARIANT_NORMAL) { + prefix = "Normal "; + } else { + static const char *parts[] = {"6 button (", NULL, "/", NULL, ") "}; + if (info->variant == VARIANT_6B_BUMPERS) { + parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + parts[3] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + } else { + parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + parts[3] = get_axis_label(info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + } + prefix = alloc_concat_m(5, parts); + } + char *ret = alloc_concat(prefix, base); + if (info->variant != VARIANT_NORMAL) { + free(prefix); + } + return ret; +} +