# HG changeset patch # User Michael Pavone # Date 1532756456 25200 # Node ID 1fc61c844ec5fe166d0ac7ae036fe197dcbcb9c5 # Parent 5e2af89c3467ccc006bcd9cbec8e3e027aec26a5 Allow selecting controller type when controllers have an SDL 2 mapping, but heuristics fail to idenify details diff -r 5e2af89c3467 -r 1fc61c844ec5 Makefile --- a/Makefile Wed Jul 25 09:38:40 2018 -0700 +++ b/Makefile Fri Jul 27 22:40:56 2018 -0700 @@ -70,12 +70,8 @@ endif #Windows ifdef DEBUG -ifeq ($(OS),Darwin) OPT:=-g3 -O0 else -OPT:=-g3 -Og -endif #Darwin -else ifdef NOLTO OPT:=-O2 else diff -r 5e2af89c3467 -r 1fc61c844ec5 config.c --- a/config.c Wed Jul 25 09:38:40 2018 -0700 +++ b/config.c Fri Jul 27 22:40:56 2018 -0700 @@ -216,11 +216,33 @@ return ret; } +tern_node *load_overrideable_config(char *name, char *bundled_name) +{ + char const *confdir = get_config_dir(); + char *confpath = NULL; + tern_node *ret; + if (confdir) { + confpath = path_append(confdir, name); + ret = parse_config_file(confpath); + if (ret) { + free(confpath); + return ret; + } + } + + ret = parse_bundled_config(bundled_name); + if (ret) { + free(confpath); + return ret; + } + return NULL; +} + tern_node *load_config() { char const *confdir = get_config_dir(); char *confpath = NULL; - tern_node *ret; + tern_node *ret = load_overrideable_config("blastem.cfg", "default.cfg"); if (confdir) { confpath = path_append(confdir, "blastem.cfg"); ret = parse_config_file(confpath); @@ -236,8 +258,8 @@ return ret; } - if (confpath) { - fatal_error("Failed to find a config file at %s or in the blastem executable directory\n", confpath); + if (get_config_dir()) { + fatal_error("Failed to find a config file at %s or in the blastem executable directory\n", get_config_dir()); } else { fatal_error("Failed to find a config file in the BlastEm executable directory and the config directory path could not be determined\n"); } @@ -245,20 +267,25 @@ return NULL; } -void persist_config(tern_node *config) +void persist_config_at(tern_node *config, char *fname) { char const *confdir = get_config_dir(); if (!confdir) { fatal_error("Failed to locate config file directory\n"); } ensure_dir_exists(confdir); - char *confpath = path_append(confdir, "blastem.cfg"); + char *confpath = path_append(confdir, fname); if (!serialize_config_file(config, confpath)) { fatal_error("Failed to write config to %s\n", confpath); } free(confpath); } +void persist_config(tern_node *config) +{ + persist_config_at(config, "blastem.cfg"); +} + char **get_extension_list(tern_node *config, uint32_t *num_exts_out) { char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg"}, TVAL_PTR).ptrval); diff -r 5e2af89c3467 -r 1fc61c844ec5 config.h --- a/config.h Wed Jul 25 09:38:40 2018 -0700 +++ b/config.h Fri Jul 27 22:40:56 2018 -0700 @@ -9,9 +9,11 @@ tern_node *parse_config_file(char *config_path); tern_node *parse_bundled_config(char *config_name); +tern_node *load_overrideable_config(char *name, char *bundled_name); tern_node *load_config(); char *serialize_config(tern_node *config, uint32_t *size_out); uint8_t serialize_config_file(tern_node *config, char *path); +void persist_config_at(tern_node *config, char *fname); void persist_config(tern_node *config); char **get_extension_list(tern_node *config, uint32_t *num_exts_out); uint32_t get_lowpass_cutoff(tern_node *config); diff -r 5e2af89c3467 -r 1fc61c844ec5 controller_info.c --- a/controller_info.c Wed Jul 25 09:38:40 2018 -0700 +++ b/controller_info.c Fri Jul 27 22:40:56 2018 -0700 @@ -1,13 +1,14 @@ #include #include "render_sdl.h" #include "controller_info.h" +#include "config.h" typedef struct { char const *name; controller_info info; } heuristic; -heuristic heuristics[] = { +static heuristic heuristics[] = { //TODO: Add more heuristic rules {"DualShock 4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, {"PS4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, @@ -24,15 +25,97 @@ }; 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 *variant_names[] = { + "normal", + "6b bumpers", + "6b right" +}; controller_info get_controller_info(int joystick) { + if (!loaded) { + info_config = load_overrideable_config("controller_types.cfg", "controller_types.cfg"); + loaded = 1; + } + 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(render_get_joystick(joystick)) + .name = SDL_JoystickName(stick) }; } const char *name = SDL_GameControllerName(control); @@ -54,6 +137,18 @@ }; } +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"); + +} + char const *labels_xbox[] = { "A", "B", "X", "Y", "Back", NULL, "Start", "Click", "Click", "White", "Black", "LT", "RT" }; diff -r 5e2af89c3467 -r 1fc61c844ec5 controller_info.h --- a/controller_info.h Wed Jul 25 09:38:40 2018 -0700 +++ b/controller_info.h Fri Jul 27 22:40:56 2018 -0700 @@ -22,13 +22,15 @@ SUBTYPE_WIIU, SUBTYPE_SWITCH, SUBTYPE_GENESIS, - SUBTYPE_SATURN + SUBTYPE_SATURN, + SUBTYPE_NUM }; enum { VARIANT_NORMAL, VARIANT_6B_BUMPERS, //C and Z positions are RB and LB respectively - VARIANT_6B_RIGHT //C and Z positions are RT and RB respectively + VARIANT_6B_RIGHT, //C and Z positions are RT and RB respectively + VARIANT_NUM }; typedef struct { @@ -41,5 +43,6 @@ controller_info get_controller_info(int index); const char *get_button_label(controller_info *info, int button); const char *get_axis_label(controller_info *info, int axis); +void save_controller_info(int joystick, controller_info *info); #endif //CONTROLLER_INFO_H_ \ No newline at end of file diff -r 5e2af89c3467 -r 1fc61c844ec5 nuklear_ui/blastem_nuklear.c --- a/nuklear_ui/blastem_nuklear.c Wed Jul 25 09:38:40 2018 -0700 +++ b/nuklear_ui/blastem_nuklear.c Fri Jul 27 22:40:56 2018 -0700 @@ -516,16 +516,33 @@ enum { UP,DOWN,LEFT,RIGHT }; -void binding_box(struct nk_context *context, char *name, float x, float y, float width, int num_binds, int *binds) + +static char * config_ps_names[] = { + [SDL_CONTROLLER_BUTTON_A] = "cross", + [SDL_CONTROLLER_BUTTON_B] = "circle", + [SDL_CONTROLLER_BUTTON_X] = "square", + [SDL_CONTROLLER_BUTTON_Y] = "triangle", + [SDL_CONTROLLER_BUTTON_BACK] = "share", + [SDL_CONTROLLER_BUTTON_START] = "options", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "l1", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "r1", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "l3", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "r3", +}; + +static void binding_box(struct nk_context *context, char *name, float x, float y, float width, int num_binds, int *binds) { const struct nk_user_font *font = context->style.font; float row_height = font->height * 2; - nk_layout_space_push(context, nk_rect(x, y, width, num_binds * (row_height + 4) + 4)); - nk_group_begin(context, name, NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR); + char const **labels = calloc(sizeof(char *), num_binds); + char **conf_keys = calloc(sizeof(char *), num_binds); + float max_width = 0.0f; - char const **labels = calloc(sizeof(char *), num_binds); - float max_width = 0.0f; + static const char base_path[] = "bindings\0pads"; + static const char buttons[] = "buttons"; + char padkey[] = {'0' + selected_controller, 0}; + int skipped = 0; for (int i = 0; i < num_binds; i++) { if (binds[i] & AXIS) { @@ -535,16 +552,37 @@ labels[i] = dirs[binds[i] & 3]; } else { labels[i] = get_button_label(&selected_controller_info, binds[i]); + char template[] = "bindings\0pads\00\0buttons\0"; + const char *but_name = SDL_GameControllerGetStringForButton(binds[i]); + size_t namelen = strlen(but_name); + conf_keys[i] = malloc(sizeof(base_path) + sizeof(padkey) + sizeof(buttons) + namelen + 2); + memcpy(conf_keys[i], base_path, sizeof(base_path)); + memcpy(conf_keys[i] + sizeof(base_path), padkey, sizeof(padkey)); + memcpy(conf_keys[i] + sizeof(base_path) + sizeof(padkey), buttons, sizeof(buttons)); + + memcpy(conf_keys[i] + sizeof(base_path) + sizeof(padkey) + sizeof(buttons), but_name, namelen+1); + conf_keys[i][sizeof(base_path) + sizeof(padkey) + sizeof(buttons) + namelen + 1] = 0; + } + if (!labels[i]) { + skipped++; + continue; } float lb_width = font->width(font->userdata, font->height, labels[i], strlen(labels[i])); max_width = max_width < lb_width ? lb_width : max_width; } + nk_layout_space_push(context, nk_rect(x, y, width, (num_binds - skipped) * (row_height + 4) + 4)); + nk_group_begin(context, name, NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR); + float widths[] = {max_width + 3, width - (max_width + 6)}; nk_layout_row(context, NK_STATIC, row_height, 2, widths); for (int i = 0; i < num_binds; i++) { + if (!labels[i]) { + continue; + } nk_label(context, labels[i], NK_TEXT_LEFT); nk_button_label(context, i & 1 ? "Internal Screenshot" : "A"); + free(conf_keys[i]); } nk_group_end(context); } @@ -655,6 +693,43 @@ } } +void controller_type_group(struct nk_context *context, char *name, int type_id, int first_subtype_id, const char **types, uint32_t num_types) +{ + nk_layout_row_static(context, (context->style.font->height + 3) * num_types + context->style.font->height, render_width() - 80, 1); + if (nk_group_begin(context, name, NK_WINDOW_TITLE)) { + nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2); + for (int i = 0; i < num_types; i++) + { + if (nk_button_label(context, types[i])) { + selected_controller_info.subtype = first_subtype_id + i; + save_controller_info(selected_controller, &selected_controller_info); + pop_view(); + push_view(view_controller_bindings); + } + } + nk_group_end(context); + } +} + +void view_controller_type(struct nk_context *context) +{ + if (nk_begin(context, "Controller Type", nk_rect(0, 0, render_width(), render_height()), 0)) { + controller_type_group(context, "Xbox", TYPE_XBOX, SUBTYPE_XBOX, (const char *[]){ + "Original", "Xbox 360", "Xbox One" + }, 3); + controller_type_group(context, "Playstation", TYPE_PSX, SUBTYPE_PS3, (const char *[]){ + "PS3", "PS4" + }, 2); + controller_type_group(context, "Sega", TYPE_SEGA, SUBTYPE_GENESIS, (const char *[]){ + "Genesis", "Saturn" + }, 2); + controller_type_group(context, "Nintendo", TYPE_NINTENDO, SUBTYPE_WIIU, (const char *[]){ + "WiiU", "Switch" + }, 2); + nk_end(context); + } +} + void view_controllers(struct nk_context *context) { if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { @@ -667,12 +742,21 @@ controller_info info = get_controller_info(i); nk_layout_row_begin(context, NK_STATIC, height, 2); nk_layout_row_push(context, image_width); - nk_image(context, controller_360_image); + if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { + nk_label(context, "?", NK_TEXT_CENTERED); + } else { + nk_image(context, controller_360_image); + } nk_layout_row_push(context, render_width() - image_width - 2 * context->style.font->height); if (nk_button_label(context, info.name)) { selected_controller = i; selected_controller_info = info; - push_view(view_controller_bindings); + if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { + push_view(view_controller_type); + } else { + push_view(view_controller_bindings); + } + } nk_layout_row_end(context); }