summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--config.c37
-rw-r--r--config.h2
-rw-r--r--controller_info.c99
-rw-r--r--controller_info.h7
-rw-r--r--nuklear_ui/blastem_nuklear.c96
6 files changed, 226 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 58dbf59..b3c6fa5 100644
--- a/Makefile
+++ b/Makefile
@@ -70,12 +70,8 @@ endif #PORTABLE
endif #Windows
ifdef DEBUG
-ifeq ($(OS),Darwin)
OPT:=-g3 -O0
else
-OPT:=-g3 -Og
-endif #Darwin
-else
ifdef NOLTO
OPT:=-O2
else
diff --git a/config.c b/config.c
index 2fc965c..10238b8 100644
--- a/config.c
+++ b/config.c
@@ -216,12 +216,34 @@ tern_node *parse_bundled_config(char *config_name)
return ret;
}
-tern_node *load_config()
+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 = load_overrideable_config("blastem.cfg", "default.cfg");
+ if (confdir) {
confpath = path_append(confdir, "blastem.cfg");
ret = parse_config_file(confpath);
if (ret) {
@@ -236,8 +258,8 @@ tern_node *load_config()
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 @@ tern_node *load_config()
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 --git a/config.h b/config.h
index d72051c..6764557 100644
--- a/config.h
+++ b/config.h
@@ -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 --git a/controller_info.c b/controller_info.c
index a96713b..e8d3c18 100644
--- a/controller_info.c
+++ b/controller_info.c
@@ -1,13 +1,14 @@
#include <string.h>
#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 @@ heuristic heuristics[] = {
};
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 @@ controller_info get_controller_info(int joystick)
};
}
+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 --git a/controller_info.h b/controller_info.h
index 0bef684..60ff38d 100644
--- a/controller_info.h
+++ b/controller_info.h
@@ -22,13 +22,15 @@ enum {
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 @@ typedef struct {
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 --git a/nuklear_ui/blastem_nuklear.c b/nuklear_ui/blastem_nuklear.c
index 298c3a7..1e5a6c6 100644
--- a/nuklear_ui/blastem_nuklear.c
+++ b/nuklear_ui/blastem_nuklear.c
@@ -516,16 +516,33 @@ static uint32_t controller_360_width, controller_360_height;
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;
+
+ 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 @@ void binding_box(struct nk_context *context, char *name, float x, float y, float
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 view_controller_bindings(struct nk_context *context)
}
}
+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 @@ void view_controllers(struct nk_context *context)
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);
}