diff --git a/conf/config_file.c b/conf/config_file.c index 0293bf9c44..eccbdddfa2 100644 --- a/conf/config_file.c +++ b/conf/config_file.c @@ -357,9 +357,16 @@ void config_set_bool(config_file_t *conf, const char *key, bool val) bool config_file_write(config_file_t *conf, const char *path) { - FILE *file = fopen(path, "w"); - if (!file) - return false; + FILE *file; + + if (path) + { + file = fopen(path, "w"); + if (!file) + return false; + } + else + file = stdout; struct entry_list *list = conf->entries; @@ -369,6 +376,8 @@ bool config_file_write(config_file_t *conf, const char *path) list = list->next; } - fclose(file); + if (path) + fclose(file); + return true; } diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c new file mode 100644 index 0000000000..f6458df57d --- /dev/null +++ b/tools/ssnes-joyconfig.c @@ -0,0 +1,260 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + +#ifdef HAVE_SDL +#include "SDL.h" +#endif + +#include "conf/config_file.h" +#include +#include +#include + +static int g_player = 1; +static int g_joypad = 1; +static char *g_in_path = NULL; +static char *g_out_path = NULL; + +static void print_help(void) +{ + puts("=================="); + puts("ssnes-joyconfig"); + puts("=================="); + puts(""); + puts("-p/--player: Which player to configure for (1 or 2)."); + puts("-j/--joypad: Which joypad to use when configuring (1 or 2)."); + puts("-i/--input: Input file to configure with. Binds will be added on or overwritten."); + puts("\tIf not selected, an empty config will be used as a base."); + puts("-o/--output: Output file to write to. If not selected, config file will be dumped to stdout."); + puts("-h/--help: This help."); +} + +struct bind +{ + char *keystr; + char *confbtn[2]; + char *confaxis[2]; +}; + +#define BIND(x, k) { x, { "input_player1_" #k "_btn", "input_player2_" #k "_btn" }, {"input_player1_" #k "_axis", "input_player2_" #k "_axis"}}, +static struct bind binds[] = { + BIND("A button (right)", a) + BIND("B button (down)", b) + BIND("X button (top)", x) + BIND("Y button (left)", y) + BIND("L button (left shoulder)", l) + BIND("R button (right shoulder)", r) + BIND("Start button", start) + BIND("Select button", select) + BIND("Left D-pad", left) + BIND("Up D-pad", up) + BIND("Right D-pad", right) + BIND("Down D-pad", down) +}; + +void get_binds(config_file_t *conf, int player, int joypad) +{ + if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO) < 0) + { + fprintf(stderr, "Failed to init joystick subsystem.\n"); + exit(1); + } + SDL_Joystick *joystick; + int num = SDL_NumJoysticks(); + if (joypad >= num) + { + fprintf(stderr, "Cannot find joystick number %d, only have %d joysticks available ...\n", joypad + 1, num); + exit(1); + } + + joystick = SDL_JoystickOpen(joypad); + if (!joystick) + { + fprintf(stderr, "Cannot open joystick.\n"); + exit(1); + } + + SDL_JoystickUpdate(); + + int last_axis = 0xFF; + int num_axes = SDL_JoystickNumAxes(joystick); + int initial_axes[num_axes]; + for (int i = 0; i < num_axes; i++) + initial_axes[i] = SDL_JoystickGetAxis(joystick, i); + + + fprintf(stderr, "Configuring binds for player #%d on joypad #%d (%s)\n", player + 1, joypad + 1, SDL_JoystickName(joypad)); + fprintf(stderr, "Press Ctrl-C to exit early.\n"); + fprintf(stderr, "\n"); + + for (unsigned i = 0; i < sizeof(binds) / sizeof(struct bind); i++) + { + fprintf(stderr, "%s\n", binds[i].keystr); + + bool done = false; + SDL_Event event; + int value; + const char *quark; + + while (SDL_WaitEvent(&event) && !done) + { + switch (event.type) + { + case SDL_JOYBUTTONDOWN: + fprintf(stderr, "\tJoybutton pressed: %d\n", (int)event.jbutton.button); + done = true; + config_set_int(conf, binds[i].confbtn[player], event.jbutton.button); + break; + + case SDL_JOYAXISMOTION: + if (abs(event.jaxis.value) > 20000 && + abs((int)event.jaxis.value - initial_axes[event.jaxis.axis]) > 20000 && + event.jaxis.axis != last_axis) + { + last_axis = event.jaxis.axis; + fprintf(stderr, "\tJoyaxis moved: Axis %d, Value %d\n", (int)event.jaxis.axis, (int)event.jaxis.value); + done = true; + + char buf[8]; + snprintf(buf, sizeof(buf), event.jaxis.value > 0 ? "+%d" : "-%d", event.jaxis.axis); + config_set_string(conf, binds[i].confaxis[player], buf); + } + break; + + case SDL_JOYHATMOTION: + value = event.jhat.value; + if (value & SDL_HAT_UP) + quark = "up"; + else if (value & SDL_HAT_DOWN) + quark = "down"; + else if (value & SDL_HAT_LEFT) + quark = "left"; + else if (value & SDL_HAT_RIGHT) + quark = "right"; + else + break; + + fprintf(stderr, "\tJoyhat moved: Hat %d, direction %s\n", (int)event.jhat.hat, quark); + + done = true; + char buf[16]; + snprintf(buf, sizeof(buf), "h%d%s", event.jhat.hat, quark); + config_set_string(conf, binds[i].confbtn[player], buf); + break; + + + case SDL_QUIT: + goto end; + + default: + break; + } + } + } + +end: + SDL_JoystickClose(joystick); + SDL_Quit(); +} + +static void parse_input(int argc, char *argv[]) +{ + char optstring[] = "i:o:p:j:h"; + struct option opts[] = { + { "input", 1, NULL, 'i' }, + { "output", 1, NULL, 'o' }, + { "player", 1, NULL, 'p' }, + { "joypad", 1, NULL, 'j' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int option_index = 0; + for(;;) + { + int c = getopt_long(argc, argv, optstring, opts, &option_index); + if (c == -1) + break; + + switch (c) + { + case 'h': + print_help(); + exit(0); + + case 'i': + g_in_path = strdup(optarg); + break; + + case 'o': + g_out_path = strdup(optarg); + break; + + case 'j': + g_joypad = strtol(optarg, NULL, 0); + if (g_joypad < 1) + { + fprintf(stderr, "Joypad number can't be less than 1!\n"); + exit(1); + } + else if (g_joypad > 2) + { + fprintf(stderr, "Joypad number can't be over 2! (1 or 2 allowed)\n"); + exit(1); + } + break; + + case 'p': + g_player = strtol(optarg, NULL, 0); + if (g_player < 1) + { + fprintf(stderr, "Player number must be at least 1!\n"); + exit(1); + } + else if (g_player > 2) + { + fprintf(stderr, "Player number must be 1 or 2.\n"); + exit(1); + } + break; + + default: + break; + } + } + + if (optind < argc) + { + print_help(); + exit(1); + } + +} + +int main(int argc, char *argv[]) +{ + parse_input(argc, argv); + + config_file_t *conf = config_file_new(g_in_path); + get_binds(conf, g_player - 1, g_joypad - 1); + config_file_write(conf, g_out_path); + config_file_free(conf); + if (g_in_path) + free(g_in_path); + if (g_out_path) + free(g_out_path); +}