From e5d2df13e75bd09d7b9e28ece16cb9adca8ad9b6 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 14:40:54 +0100 Subject: [PATCH 01/50] Fix error message when file cannot be opened. --- ssnes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssnes.c b/ssnes.c index af3a1b724c..eab1ff97ad 100644 --- a/ssnes.c +++ b/ssnes.c @@ -329,7 +329,7 @@ static void parse_input(int argc, char *argv[]) g_extern.rom_file = fopen(argv[optind], "rb"); if (g_extern.rom_file == NULL) { - SSNES_ERR("Could not open file: \"%s\"\n", optarg); + SSNES_ERR("Could not open file: \"%s\"\n", argv[optind]); exit(1); } if (strlen(g_extern.savefile_name_srm) == 0) From 2a5addc79f960c4af93c538fbe39a4ea2c0aa0a6 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 18:37:45 +0100 Subject: [PATCH 02/50] Clean up input handling some more. Removes the hack to set fast forward toggles. --- config.def.h | 45 +++++++++++----------- driver.h | 10 ++++- general.h | 8 +--- input/sdl.c | 25 +++++++++---- settings.c | 104 +++++++++++++++++++-------------------------------- ssnes.c | 13 ++++--- ssnes.cfg | 13 ++++--- 7 files changed, 102 insertions(+), 116 deletions(-) diff --git a/config.def.h b/config.def.h index 1a0e8881b0..fed2f9895e 100644 --- a/config.def.h +++ b/config.def.h @@ -142,28 +142,34 @@ static const bool audio_sync = true; #define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF) #define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U) #define AXIS_NONE ((uint32_t)0xFFFFFFFFU) +#define NO_BTN 0xFFFF // I hope no joypad will ever have this many buttons ... ;) // To figure out which joypad buttons to use, check jstest or similar. // Axes are configured using the axis number for the positive (up, right) // direction and the number's two's-complement (~) for negative directions. // To use the axis, set the button to -1. +// SDL sometimes reverses the axes for some odd reason, but hey. :D // Player 1 static const struct snes_keybind snes_keybinds_1[] = { // SNES button | keyboard key | js btn | js axis | - { SNES_DEVICE_ID_JOYPAD_A, SDLK_x, 1, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_B, SDLK_z, 0, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_X, SDLK_s, 3, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, 2, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_L, SDLK_q, 4, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_R, SDLK_w, 5, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, 11, AXIS_NEG(0) }, - { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, 12, AXIS_POS(0) }, - { SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, 13, AXIS_POS(1) }, - { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_NEG(1) }, - { SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, 7, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, 6, AXIS_NONE }, - { SSNES_FAST_FORWARD_KEY, SDLK_SPACE, 10, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_A, SDLK_x, 1, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_B, SDLK_z, 0, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_X, SDLK_s, 3, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, 2, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_L, SDLK_q, 4, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_R, SDLK_w, 5, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, 11, AXIS_NEG(0) }, + { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, 12, AXIS_POS(0) }, + { SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, 13, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, 7, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, 6, AXIS_NONE }, + { SSNES_FAST_FORWARD_KEY, SDLK_SPACE, 10, AXIS_NONE }, + { SSNES_SAVE_STATE_KEY, SDLK_F2, NO_BTN, AXIS_NONE }, + { SSNES_LOAD_STATE_KEY, SDLK_F4, NO_BTN, AXIS_NONE }, + { SSNES_FULLSCREEN_TOGGLE_KEY, SDLK_f, NO_BTN, AXIS_NONE }, + { SSNES_QUIT_KEY, SDLK_ESCAPE, NO_BTN, AXIS_NONE }, { -1 } }; @@ -178,21 +184,12 @@ static const struct snes_keybind snes_keybinds_2[] = { { SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE }, { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) }, { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) }, - { SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_POS(1) }, - { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) }, { SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE }, { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE }, { -1 } }; -///// Save state -#define SAVE_STATE_KEY SDLK_F2 -///// Load state -#define LOAD_STATE_KEY SDLK_F4 - -//// Toggles between fullscreen and windowed mode. -#define TOGGLE_FULLSCREEN SDLK_f - - #endif diff --git a/driver.h b/driver.h index ec5f3dcca8..778bce9172 100644 --- a/driver.h +++ b/driver.h @@ -25,8 +25,14 @@ #include #include -#define SSNES_FAST_FORWARD_KEY 0x666 // Hurr, durr -void set_fast_forward_button(bool state); +enum +{ + SSNES_FAST_FORWARD_KEY = 0x666, // Hurr, durr + SSNES_LOAD_STATE_KEY, + SSNES_SAVE_STATE_KEY, + SSNES_FULLSCREEN_TOGGLE_KEY, + SSNES_QUIT_KEY, +}; struct snes_keybind { diff --git a/general.h b/general.h index ef1bfffbb3..1fcda776e2 100644 --- a/general.h +++ b/general.h @@ -33,8 +33,8 @@ #endif -#define MAX_PLAYERS 2 -#define MAX_BINDS 14 +#define MAX_PLAYERS 2 // Should be 5 when multi-tap stuff is added ... +#define MAX_BINDS 18 // Needs to be increased every time there are new binds added. struct settings { struct @@ -70,10 +70,6 @@ struct settings { char driver[32]; struct snes_keybind binds[MAX_PLAYERS][MAX_BINDS]; - int save_state_key; - int load_state_key; - int toggle_fullscreen_key; - int exit_emulator_key; float axis_threshold; } input; diff --git a/input/sdl.c b/input/sdl.c index a2260ebe2e..aca92a9ae7 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -56,6 +56,7 @@ static void* sdl_input_init(void) return sdl; } + static bool sdl_key_pressed(void *data, int key) { int num_keys; @@ -97,6 +98,17 @@ static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_key return false; } +static bool sdl_bind_button_pressed(void *data, int key) +{ + const struct snes_keybind *binds = g_settings.input.binds[0]; + for (int i = 0; binds[i].id != -1; i++) + { + if (binds[i].id == key) + return sdl_is_pressed(data, 0, &binds[i]); + } + return false; +} + static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) { sdl_input_t *sdl = data; @@ -105,18 +117,15 @@ static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bo const struct snes_keybind *snes_keybinds = binds[port == SNES_PORT_1 ? 0 : 1]; - // Checks if button is pressed, and sets fast-forwarding state - bool pressed = false; + // Checks if button is pressed. int port_num = port == SNES_PORT_1 ? 0 : 1; for (int i = 0; snes_keybinds[i].id != -1; i++) { - if (snes_keybinds[i].id == SSNES_FAST_FORWARD_KEY) - set_fast_forward_button(sdl_is_pressed(sdl, port_num, &snes_keybinds[i])); - else if (!pressed && snes_keybinds[i].id == (int)id) - pressed = sdl_is_pressed(sdl, port_num, &snes_keybinds[i]); + if (snes_keybinds[i].id == (int)id) + return sdl_is_pressed(sdl, port_num, &snes_keybinds[i]); } - return pressed; + return false; } static void sdl_input_free(void *data) @@ -170,7 +179,7 @@ const input_driver_t input_sdl = { .init = sdl_input_init, .poll = sdl_input_poll, .input_state = sdl_input_state, - .key_pressed = sdl_key_pressed, + .key_pressed = sdl_bind_button_pressed, .free = sdl_input_free, .ident = "sdl" }; diff --git a/settings.c b/settings.c index 5337f042c7..8e352cea4b 100644 --- a/settings.c +++ b/settings.c @@ -111,11 +111,7 @@ static void set_defaults(void) memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1)); memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2)); - g_settings.input.save_state_key = SAVE_STATE_KEY; - g_settings.input.load_state_key = LOAD_STATE_KEY; - g_settings.input.toggle_fullscreen_key = TOGGLE_FULLSCREEN; g_settings.input.axis_threshold = AXIS_THRESHOLD; - g_settings.input.exit_emulator_key = SDLK_ESCAPE; } void parse_config(void) @@ -311,37 +307,47 @@ struct bind_map int snes_key; }; + +#define DECLARE_BIND(x, bind) { "input_" #x, "input_" #x "_btn", "input_" #x "_axis", bind }, // Big and nasty bind map... :) -static const struct bind_map bind_maps[2][13] = { +static const struct bind_map bind_maps[2][MAX_BINDS - 1] = { { - { "input_player1_a", "input_player1_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A }, - { "input_player1_b", "input_player1_b_btn", NULL, SNES_DEVICE_ID_JOYPAD_B }, - { "input_player1_y", "input_player1_y_btn", NULL, SNES_DEVICE_ID_JOYPAD_Y }, - { "input_player1_x", "input_player1_x_btn", NULL, SNES_DEVICE_ID_JOYPAD_X }, - { "input_player1_start", "input_player1_start_btn", NULL, SNES_DEVICE_ID_JOYPAD_START }, - { "input_player1_select", "input_player1_select_btn", NULL, SNES_DEVICE_ID_JOYPAD_SELECT }, - { "input_player1_l", "input_player1_l_btn", NULL, SNES_DEVICE_ID_JOYPAD_L }, - { "input_player1_r", "input_player1_r_btn", NULL, SNES_DEVICE_ID_JOYPAD_R }, - { "input_player1_left", "input_player1_left_btn", "input_player1_left_axis", SNES_DEVICE_ID_JOYPAD_LEFT }, - { "input_player1_right", "input_player1_right_btn", "input_player1_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, - { "input_player1_up", "input_player1_up_btn", "input_player1_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, - { "input_player1_down", "input_player1_down_btn", "input_player1_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } - }, + DECLARE_BIND(player1_a, SNES_DEVICE_ID_JOYPAD_A) + DECLARE_BIND(player1_b, SNES_DEVICE_ID_JOYPAD_B) + DECLARE_BIND(player1_y, SNES_DEVICE_ID_JOYPAD_Y) + DECLARE_BIND(player1_x, SNES_DEVICE_ID_JOYPAD_X) + DECLARE_BIND(player1_start, SNES_DEVICE_ID_JOYPAD_START) + DECLARE_BIND(player1_select, SNES_DEVICE_ID_JOYPAD_SELECT) + DECLARE_BIND(player1_l, SNES_DEVICE_ID_JOYPAD_L) + DECLARE_BIND(player1_r, SNES_DEVICE_ID_JOYPAD_R) + DECLARE_BIND(player1_left, SNES_DEVICE_ID_JOYPAD_LEFT) + DECLARE_BIND(player1_right, SNES_DEVICE_ID_JOYPAD_RIGHT) + DECLARE_BIND(player1_up, SNES_DEVICE_ID_JOYPAD_UP) + DECLARE_BIND(player1_down, SNES_DEVICE_ID_JOYPAD_DOWN) + DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY) + DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY) + DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) + DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) + DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) + }, { - { "input_player2_a", "input_player2_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A }, - { "input_player2_b", "input_player2_b_btn", NULL, SNES_DEVICE_ID_JOYPAD_B }, - { "input_player2_y", "input_player2_y_btn", NULL, SNES_DEVICE_ID_JOYPAD_Y }, - { "input_player2_x", "input_player2_x_btn", NULL, SNES_DEVICE_ID_JOYPAD_X }, - { "input_player2_start", "input_player2_start_btn", NULL, SNES_DEVICE_ID_JOYPAD_START }, - { "input_player2_select", "input_player2_select_btn", NULL, SNES_DEVICE_ID_JOYPAD_SELECT }, - { "input_player2_l", "input_player2_l_btn", NULL, SNES_DEVICE_ID_JOYPAD_L }, - { "input_player2_r", "input_player2_r_btn", NULL, SNES_DEVICE_ID_JOYPAD_R }, - { "input_player2_left", "input_player2_left_btn", "input_player2_left_axis", SNES_DEVICE_ID_JOYPAD_LEFT }, - { "input_player2_right", "input_player2_right_btn", "input_player2_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, - { "input_player2_up", "input_player2_up_btn", "input_player2_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, - { "input_player2_down", "input_player2_down_btn", "input_player2_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } + DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A) + DECLARE_BIND(player2_b, SNES_DEVICE_ID_JOYPAD_B) + DECLARE_BIND(player2_y, SNES_DEVICE_ID_JOYPAD_Y) + DECLARE_BIND(player2_x, SNES_DEVICE_ID_JOYPAD_X) + DECLARE_BIND(player2_start, SNES_DEVICE_ID_JOYPAD_START) + DECLARE_BIND(player2_select, SNES_DEVICE_ID_JOYPAD_SELECT) + DECLARE_BIND(player2_l, SNES_DEVICE_ID_JOYPAD_L) + DECLARE_BIND(player2_r, SNES_DEVICE_ID_JOYPAD_R) + DECLARE_BIND(player2_left, SNES_DEVICE_ID_JOYPAD_LEFT) + DECLARE_BIND(player2_right, SNES_DEVICE_ID_JOYPAD_RIGHT) + DECLARE_BIND(player2_up, SNES_DEVICE_ID_JOYPAD_UP) + DECLARE_BIND(player2_down, SNES_DEVICE_ID_JOYPAD_DOWN) + DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY) + DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY) + DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) + DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) + DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) } }; @@ -352,6 +358,7 @@ struct key_map }; // Edit: Not portable to different input systems atm. Might move this map into the driver itself or something. +// However, this should map nicely over to other systems aswell since the definition are mostly the same anyways. static const struct key_map sdlk_map[] = { { "left", SDLK_LEFT }, { "right", SDLK_RIGHT }, @@ -459,38 +466,5 @@ static void read_keybinds(config_file_t *conf) } } } - - char *tmp_str; - if (config_get_string(conf, "input_toggle_fullscreen", &tmp_str)) - { - int key = find_sdlk_key(tmp_str); - if (key >= 0) - g_settings.input.toggle_fullscreen_key = key; - free(tmp_str); - } - if (config_get_string(conf, "input_save_state", &tmp_str)) - { - int key = find_sdlk_key(tmp_str); - if (key >= 0) - g_settings.input.save_state_key = key; - free(tmp_str); - } - if (config_get_string(conf, "input_load_state", &tmp_str)) - { - int key = find_sdlk_key(tmp_str); - if (key >= 0) - g_settings.input.load_state_key = key; - free(tmp_str); - } - if (config_get_string(conf, "input_exit_emulator", &tmp_str)) - { - int key = find_sdlk_key(tmp_str); - if (key >= 0) - g_settings.input.exit_emulator_key = key; - free(tmp_str); - } } - - - diff --git a/ssnes.c b/ssnes.c index eab1ff97ad..4d3090607f 100644 --- a/ssnes.c +++ b/ssnes.c @@ -43,7 +43,7 @@ struct global g_extern = { #define AUDIO_CHUNK_SIZE_BLOCKING 64 #define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; -void set_fast_forward_button(bool new_button_state) +static void set_fast_forward_button(bool new_button_state) { static bool old_button_state = false; static bool syncing_state = false; @@ -426,21 +426,24 @@ int main(int argc, char *argv[]) } #endif + // Main loop for(;;) { - if (driver.input->key_pressed(driver.input_data, g_settings.input.exit_emulator_key) || + if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) || !driver.video->alive(driver.video_data)) break; - if (driver.input->key_pressed(driver.input_data, g_settings.input.save_state_key)) + set_fast_forward_button(driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY)); + + if (driver.input->key_pressed(driver.input_data, SSNES_SAVE_STATE_KEY)) { write_file(statefile_name, serial_data, serial_size); } - else if (driver.input->key_pressed(driver.input_data, g_settings.input.load_state_key)) + else if (driver.input->key_pressed(driver.input_data, SSNES_LOAD_STATE_KEY)) load_state(statefile_name, serial_data, serial_size); - else if (driver.input->key_pressed(driver.input_data, g_settings.input.toggle_fullscreen_key)) + else if (driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) { g_settings.video.fullscreen = !g_settings.video.fullscreen; uninit_drivers(); diff --git a/ssnes.cfg b/ssnes.cfg index 7f52b5961f..499b3cee7b 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -102,7 +102,9 @@ # input_player1_up_btn = 13 # input_player1_down_btn = 14 -# Axis for DPAD. Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number. +# Axis for DPAD. +# Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number. +# Do note that every other input option has the corresponding _btn and _axis binds as well; they are omitted here for clarity. # input_player1_left_axis = -0 # input_player1_right_axis = +0 # input_player1_up_axis = +1 @@ -138,8 +140,8 @@ # input_player2_left_axis = -0 # input_player2_right_axis = +0 -# input_player2_up_axis = +1 -# input_player2_down_axis = -1 +# input_player2_up_axis = -1 +# input_player2_down_axis = +1 # Toggles fullscreen. # input_toggle_fullscreen = f @@ -150,8 +152,7 @@ # Toggles between fast-forwarding and normal speed. # input_toggle_fast_forward = space -# Same, just mapping to a joypad button. -# input_toggle_fast_forward_btn = 10 -# Key to exit emulator cleanly. +# Key to exit emulator cleanly. +# Killing it in any hard way (SIGTERM, SIGKILL, etc, will terminate emulator without saving RAM, etc.) # input_exit_emulator = escape From b249c9d38f9f467a31516434441c3c47463ab196 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 19:15:18 +0100 Subject: [PATCH 03/50] Fixed savestate path handling. Added a -t/--savestate option as well. --- general.h | 4 +++- ssnes.c | 51 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/general.h b/general.h index 1fcda776e2..cacf072ea3 100644 --- a/general.h +++ b/general.h @@ -84,9 +84,11 @@ struct global bool video_active; FILE *rom_file; - char savefile_name_srm[256]; char config_path[256]; + char basename[256]; + char savefile_name_srm[256]; + char savestate_name[256]; #ifdef HAVE_FFMPEG ffemu_t *rec; diff --git a/ssnes.c b/ssnes.c index 4d3090607f..7dc6723dac 100644 --- a/ssnes.c +++ b/ssnes.c @@ -237,6 +237,7 @@ static void print_help(void) puts("Usage: ssnes [rom file] [-h/--help | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); + puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); #ifdef HAVE_FFMPEG @@ -261,6 +262,7 @@ static void parse_input(int argc, char *argv[]) #endif { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, + { "savestate", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; @@ -272,7 +274,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -287,8 +289,11 @@ static void parse_input(int argc, char *argv[]) exit(0); case 's': - strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm)); - g_extern.savefile_name_srm[sizeof(g_extern.savefile_name_srm)-1] = '\0'; + strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm) - 1); + break; + + case 't': + strncpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name) - 1); break; case 'v': @@ -332,12 +337,29 @@ static void parse_input(int argc, char *argv[]) SSNES_ERR("Could not open file: \"%s\"\n", argv[optind]); exit(1); } + // strl* would be nice :D if (strlen(g_extern.savefile_name_srm) == 0) - fill_pathname(g_extern.savefile_name_srm, argv[optind], ".srm"); + { + strcpy(g_extern.savefile_name_srm, g_extern.basename); + size_t len = strlen(g_extern.savefile_name_srm); + strncat(g_extern.savefile_name_srm, ".srm", sizeof(g_extern.savefile_name_srm) - len - 1); + } + if (strlen(g_extern.savestate_name) == 0) + { + strcpy(g_extern.savestate_name, g_extern.basename); + size_t len = strlen(g_extern.savestate_name); + strncat(g_extern.savestate_name, ".state", sizeof(g_extern.savestate_name) - len - 1); + } } else if (strlen(g_extern.savefile_name_srm) == 0) { - SSNES_ERR("Need savefile argument when reading rom from stdin.\n"); + SSNES_ERR("Need savefile path argument (--save) when reading rom from stdin.\n"); + print_help(); + exit(1); + } + else if (strlen(g_extern.savestate_name) == 0) + { + SSNES_ERR("Need savestate path argument (--savefile) when reading rom from stdin.\n"); print_help(); exit(1); } @@ -366,11 +388,9 @@ int main(int argc, char *argv[]) if (g_extern.rom_file != NULL) fclose(g_extern.rom_file); - char statefile_name[strlen(g_extern.savefile_name_srm)+strlen(".state")+1]; + // Infer .rtc save path from save ram path. char savefile_name_rtc[strlen(g_extern.savefile_name_srm)+strlen(".rtc")+1]; - - fill_pathname(statefile_name, argv[1], ".state"); - fill_pathname(savefile_name_rtc, argv[1], ".rtc"); + fill_pathname(savefile_name_rtc, g_extern.savefile_name_srm, ".rtc"); init_drivers(); @@ -379,6 +399,7 @@ int main(int argc, char *argv[]) psnes_set_input_poll(input_poll); psnes_set_input_state(input_state); + // TODO: Load other types of ROMs as well! if (!psnes_load_cartridge_normal(NULL, rom_buf, rom_len)) { SSNES_ERR("ROM file is not valid!\n"); @@ -429,20 +450,20 @@ int main(int argc, char *argv[]) // Main loop for(;;) { + // Time to drop? if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) || !driver.video->alive(driver.video_data)) break; set_fast_forward_button(driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY)); + // Save or load state here. if (driver.input->key_pressed(driver.input_data, SSNES_SAVE_STATE_KEY)) - { - write_file(statefile_name, serial_data, serial_size); - } - + write_file(g_extern.savestate_name, serial_data, serial_size); else if (driver.input->key_pressed(driver.input_data, SSNES_LOAD_STATE_KEY)) - load_state(statefile_name, serial_data, serial_size); + load_state(g_extern.savestate_name, serial_data, serial_size); + // If we go fullscreen we drop all drivers and reinit to be safe. else if (driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) { g_settings.video.fullscreen = !g_settings.video.fullscreen; @@ -450,6 +471,7 @@ int main(int argc, char *argv[]) init_drivers(); } + // Run libsnes for one frame. psnes_run(); } @@ -461,6 +483,7 @@ int main(int argc, char *argv[]) } #endif + // Flush out SRAM (and RTC) save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); From 08e1c5f0fd4e73fb02797a57ec4074f62e082726 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 19:22:58 +0100 Subject: [PATCH 04/50] Add a hack to "fix" endless loop when using axis to change fullscreen. --- input/sdl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/input/sdl.c b/input/sdl.c index aca92a9ae7..3fff9f2d0c 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -132,6 +132,10 @@ static void sdl_input_free(void *data) { if (data) { + // Flush out all pending events. + SDL_Event event; + while (SDL_PollEvent(&event)); + sdl_input_t *sdl = data; for (int i = 0; i < sdl->num_joysticks; i++) SDL_JoystickClose(sdl->joysticks[i]); From b302026b842ee84974b3b2aebd032b6bcfa3200b Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 19:28:49 +0100 Subject: [PATCH 05/50] Remove joypads from event queue. --- input/sdl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input/sdl.c b/input/sdl.c index 3fff9f2d0c..b506a0d92d 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -34,6 +34,7 @@ static void* sdl_input_init(void) if (SDL_Init(SDL_INIT_JOYSTICK) < 0) return NULL; + SDL_JoystickEventState(SDL_IGNORE); sdl->num_joysticks = SDL_NumJoysticks(); if (sdl->num_joysticks > 2) sdl->num_joysticks = 2; @@ -149,6 +150,7 @@ static void sdl_input_poll(void *data) { SDL_PumpEvents(); SDL_Event event; + SDL_JoystickUpdate(); sdl_input_t *sdl = data; // Search for events... From 56117b3f684b24fd113d020eeb31fa48eac767c6 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 19:44:15 +0100 Subject: [PATCH 06/50] Update win32 cflags and ldflags --- Makefile.win32 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index d66d1cc925..b189ef4979 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -11,7 +11,7 @@ libsnes ?= -lsnes LIBS = DEFINES = -I. -LDFLAGS = -L. -static-libgcc +LDFLAGS = -L. -static-libgcc -s SRC_LIBS = -lsamplerate-0 SDL_LIBS = -lSDLmain -lSDL @@ -36,7 +36,7 @@ endif LIBS += $(libsnes) -CFLAGS = -Wall -O3 -g -std=gnu99 -I. +CFLAGS = -Wall -O3 -std=gnu99 -I. all: $(TARGET) From 4b9c07428c5bb4fb7fbab28f7870513bc2dd7230 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 20:05:41 +0100 Subject: [PATCH 07/50] Add a dist target for win32. --- Makefile.win32 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.win32 b/Makefile.win32 index b189ef4979..d583e99f40 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -56,4 +56,7 @@ clean: rm -f hqflt/snes_ntsc/*.o rm -f $(TARGET) -.PHONY: all install uninstall clean +dist: all + zip -r ssnes-win32.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll + +.PHONY: all install uninstall clean dist From a3659895575455037cdd3aa43bce1c973302583a Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 8 Jan 2011 22:15:02 +0100 Subject: [PATCH 08/50] Add support for joypad "hats". --- config.def.h | 8 ----- driver.h | 17 +++++++++ input/sdl.c | 78 +++++++++++++++++++++++++++++++++-------- input/ssnes_sdl_input.h | 1 + settings.c | 35 +++++++++++++++--- 5 files changed, 112 insertions(+), 27 deletions(-) diff --git a/config.def.h b/config.def.h index fed2f9895e..ad46235515 100644 --- a/config.def.h +++ b/config.def.h @@ -139,15 +139,7 @@ static const bool audio_sync = true; // How far an axis must be tilted to result in a button press #define AXIS_THRESHOLD 0.5 -#define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF) -#define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U) -#define AXIS_NONE ((uint32_t)0xFFFFFFFFU) -#define NO_BTN 0xFFFF // I hope no joypad will ever have this many buttons ... ;) - // To figure out which joypad buttons to use, check jstest or similar. -// Axes are configured using the axis number for the positive (up, right) -// direction and the number's two's-complement (~) for negative directions. -// To use the axis, set the button to -1. // SDL sometimes reverses the axes for some odd reason, but hey. :D // Player 1 diff --git a/driver.h b/driver.h index 778bce9172..73ae142c48 100644 --- a/driver.h +++ b/driver.h @@ -64,9 +64,26 @@ typedef struct audio_driver const char *ident; } audio_driver_t; +#define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF) +#define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U) +#define AXIS_NONE ((uint32_t)0xFFFFFFFFU) + #define AXIS_NEG_GET(x) ((x >> 16) & 0xFFFF) #define AXIS_POS_GET(x) (x & 0xFFFF) #define AXIS_NONE ((uint32_t)0xFFFFFFFFU) + +#define NO_BTN 0xFFFF // I hope no joypad will ever have this many buttons ... ;) + +#define HAT_UP_MASK (1 << 15) +#define HAT_DOWN_MASK (1 << 14) +#define HAT_LEFT_MASK (1 << 13) +#define HAT_RIGHT_MASK (1 << 12) +#define HAT_MAP(x, hat) ((x & ((1 << 12) - 1)) | hat) + +#define HAT_MASK (HAT_UP_MASK | HAT_DOWN_MASK | HAT_LEFT_MASK | HAT_RIGHT_MASK) +#define GET_HAT_DIR(x) (x & HAT_MASK) +#define GET_HAT(x) (x & (~HAT_MASK)) + typedef struct input_driver { void* (*init)(void); diff --git a/input/sdl.c b/input/sdl.c index b506a0d92d..8968862d32 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -24,6 +24,7 @@ #include #include #include "ssnes_sdl_input.h" +#include "config.def.h" static void* sdl_input_init(void) { @@ -52,13 +53,14 @@ static void* sdl_input_init(void) SSNES_LOG("Opened Joystick: %s\n", SDL_JoystickName(i)); sdl->num_axes[i] = SDL_JoystickNumAxes(sdl->joysticks[i]); sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]); + sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]); } return sdl; } -static bool sdl_key_pressed(void *data, int key) +static bool sdl_key_pressed(int key) { int num_keys; Uint8 *keymap = SDL_GetKeyState(&num_keys); @@ -69,27 +71,60 @@ static bool sdl_key_pressed(void *data, int key) return keymap[key]; } -static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_keybind *key) +static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey) { - if (sdl_key_pressed(sdl, key->key)) - return true; - if (port_num >= sdl->num_joysticks) - return false; - if (key->joykey < sdl->num_buttons[port_num] && SDL_JoystickGetButton(sdl->joysticks[port_num], key->joykey)) - return true; - - if (key->joyaxis != AXIS_NONE) + // Check hat. + if (GET_HAT_DIR(joykey)) { - if (AXIS_NEG_GET(key->joyaxis) < sdl->num_axes[port_num]) + int hat = GET_HAT(joykey); + if (hat < sdl->num_hats[port_num]) { - Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(key->joyaxis)); + Uint8 dir = SDL_JoystickGetHat(sdl->joysticks[port_num], hat); + switch (GET_HAT_DIR(joykey)) + { + case HAT_UP_MASK: + if (dir == SDL_HAT_UP || dir == SDL_HAT_RIGHTUP || dir == SDL_HAT_LEFTUP) + return true; + break; + case HAT_DOWN_MASK: + if (dir == SDL_HAT_DOWN || dir == SDL_HAT_LEFTDOWN || dir == SDL_HAT_RIGHTDOWN) + return true; + break; + case HAT_LEFT_MASK: + if (dir == SDL_HAT_LEFT || dir == SDL_HAT_LEFTDOWN || dir == SDL_HAT_LEFTUP) + return true; + break; + case HAT_RIGHT_MASK: + if (dir == SDL_HAT_RIGHT || dir == SDL_HAT_RIGHTDOWN || dir == SDL_HAT_RIGHTUP) + return true; + break; + default: + break; + } + } + } + else // Check the button + { + if (joykey < sdl->num_buttons[port_num] && SDL_JoystickGetButton(sdl->joysticks[port_num], joykey)) + return true; + } + return false; +} + +static bool sdl_axis_pressed(sdl_input_t *sdl, int port_num, uint32_t joyaxis) +{ + if (joyaxis != AXIS_NONE) + { + if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num]) + { + Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis)); float scaled = (float)val / 0x8000; if (scaled < -g_settings.input.axis_threshold) return true; } - if (AXIS_POS_GET(key->joyaxis) < sdl->num_axes[port_num]) + if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num]) { - Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(key->joyaxis)); + Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis)); float scaled = (float)val / 0x8000; if (scaled > g_settings.input.axis_threshold) return true; @@ -99,8 +134,23 @@ static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_key return false; } +static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_keybind *key) +{ + if (sdl_key_pressed(key->key)) + return true; + if (port_num >= sdl->num_joysticks) + return false; + if (sdl_joykey_pressed(sdl, port_num, key->joykey)) + return true; + if (sdl_axis_pressed(sdl, port_num, key->joyaxis)) + return true; + + return false; +} + static bool sdl_bind_button_pressed(void *data, int key) { + // Only let player 1 use special binds called from main loop. const struct snes_keybind *binds = g_settings.input.binds[0]; for (int i = 0; binds[i].id != -1; i++) { diff --git a/input/ssnes_sdl_input.h b/input/ssnes_sdl_input.h index 5833396e3f..519477d64a 100644 --- a/input/ssnes_sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -24,6 +24,7 @@ typedef struct sdl_input SDL_Joystick *joysticks[2]; unsigned num_axes[2]; unsigned num_buttons[2]; + unsigned num_hats[2]; unsigned num_joysticks; // A video driver could pre-init with the SDL driver and have it handle resizing events... diff --git a/settings.c b/settings.c index 8e352cea4b..219b77119d 100644 --- a/settings.c +++ b/settings.c @@ -17,10 +17,10 @@ #include "general.h" #include "conf/config_file.h" -#include "config.def.h" #include #include #include "hqflt/filters.h" +#include "config.def.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -422,7 +422,7 @@ static int find_sdlk_key(const char *str) static void read_keybinds(config_file_t *conf) { char *tmp_key = NULL; - int tmp_btn; + char *tmp_btn = NULL; char *tmp_axis = NULL; for (int j = 0; j < 1; j++) @@ -444,10 +444,35 @@ static void read_keybinds(config_file_t *conf) tmp_key = NULL; } - if (bind_maps[j][i].btn && config_get_int(conf, bind_maps[j][i].btn, &tmp_btn)) + if (bind_maps[j][i].btn && config_get_string(conf, bind_maps[j][i].btn, &tmp_btn)) { - if (tmp_btn >= 0) - bind->joykey = tmp_btn; + const char *btn = tmp_btn; + if (*btn++ == 'h') + { + if (isdigit(*btn)) + { + char *dir = NULL; + int hat = strtol(btn, &dir, 0); + int hat_dir = 0; + if (dir) + { + if (strcasecmp(dir, "up") == 0) + hat_dir = HAT_UP_MASK; + else if (strcasecmp(dir, "down") == 0) + hat_dir = HAT_DOWN_MASK; + else if (strcasecmp(dir, "left") == 0) + hat_dir = HAT_LEFT_MASK; + else if (strcasecmp(dir, "right") == 0) + hat_dir = HAT_RIGHT_MASK; + + if (hat_dir) + bind->joykey = HAT_MAP(hat, hat_dir); + } + } + } + else + bind->joykey = strtol(tmp_btn, NULL, 0); + free(tmp_btn); } if (bind_maps[j][i].axis && config_get_string(conf, bind_maps[j][i].axis, &tmp_axis)) From 23f112385aee8d37634ae7faf6c3f33db7688ce8 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 00:28:10 +0100 Subject: [PATCH 09/50] Remove unneeded include --- input/sdl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/input/sdl.c b/input/sdl.c index 8968862d32..3cfc562cd6 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -24,7 +24,6 @@ #include #include #include "ssnes_sdl_input.h" -#include "config.def.h" static void* sdl_input_init(void) { From 9463a2aecf6f4beb6d4f83e9cb51b5a34877ea26 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 00:30:56 +0100 Subject: [PATCH 10/50] Add small instruction for using "hats" in config. --- ssnes.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssnes.cfg b/ssnes.cfg index 499b3cee7b..4eda4b7361 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -89,6 +89,8 @@ # input_player1_down = down # Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output. +# You can use joypad hats with hnxx, where n is the hat, and xx is a string representing direction. +# E.g. "h0up" # input_player1_a_btn = 1 # input_player1_b_btn = 0 # input_player1_y_btn = 2 From 924904c00704a70ed3a5a480b2dd92abb26ffbe2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 02:58:26 +0100 Subject: [PATCH 11/50] Add some "set config" APIs. --- conf/config_file.c | 91 +++++++++++++++++++++++++++++++++++++++++++--- conf/config_file.h | 10 ++++- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/conf/config_file.c b/conf/config_file.c index dc945c2e6a..0293bf9c44 100644 --- a/conf/config_file.c +++ b/conf/config_file.c @@ -21,7 +21,10 @@ #include #include #include + +#ifndef STANDALONE #include "general.h" +#endif struct entry_list { @@ -132,21 +135,32 @@ static void print_config(config_file_t *conf) struct entry_list *tmp = conf->entries; while (tmp != NULL) { +#ifdef STANDALONE + fprintf(stderr, "Config => Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); +#else SSNES_LOG("Config => Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); +#endif tmp = tmp->next; } } config_file_t *config_file_new(const char *path) { - FILE *file = fopen(path, "r"); - if (!file) - return NULL; struct config_file *conf = calloc(1, sizeof(*conf)); if (conf == NULL) return NULL; + if (path == NULL) + return conf; + + FILE *file = fopen(path, "r"); + if (!file) + { + free(conf); + return NULL; + } + struct entry_list *tail = conf->entries; while (!feof(file)) @@ -174,8 +188,7 @@ config_file_t *config_file_new(const char *path) } fclose(file); - if (g_extern.verbose) - print_config(conf); + print_config(conf); return conf; } @@ -290,4 +303,72 @@ bool config_get_bool(config_file_t *conf, const char *key, bool *in) return false; } +void config_set_string(config_file_t *conf, const char *key, const char *val) +{ + struct entry_list *list = conf->entries; + struct entry_list *last = list; + while (list != NULL) + { + if (strcmp(key, list->key) == 0) + { + free(list->value); + list->value = strdup(val); + return; + } + last = list; + list = list->next; + } + struct entry_list *elem = calloc(1, sizeof(*elem)); + elem->key = strdup(key); + elem->value = strdup(val); + + if (last) + last->next = elem; + else + conf->entries = elem; +} + +void config_set_double(config_file_t *conf, const char *key, double val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%lf", val); + config_set_string(conf, key, buf); +} + +void config_set_int(config_file_t *conf, const char *key, int val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%d", val); + config_set_string(conf, key, buf); +} + +void config_set_char(config_file_t *conf, const char *key, char val) +{ + char buf[2]; + snprintf(buf, sizeof(buf), "%c", val); + config_set_string(conf, key, buf); +} + +void config_set_bool(config_file_t *conf, const char *key, bool val) +{ + config_set_string(conf, key, val ? "true" : "false"); +} + +bool config_file_write(config_file_t *conf, const char *path) +{ + FILE *file = fopen(path, "w"); + if (!file) + return false; + + struct entry_list *list = conf->entries; + + while (list != NULL) + { + fprintf(file, "%s = \"%s\"\n", list->key, list->value); + list = list->next; + } + + fclose(file); + return true; +} diff --git a/conf/config_file.h b/conf/config_file.h index 1992127564..a893fdac18 100644 --- a/conf/config_file.h +++ b/conf/config_file.h @@ -30,7 +30,7 @@ typedef struct config_file config_file_t; // - Format is: key = value. There can be as many spaces as you like in-between. // - Value can be wrapped inside "" for multiword strings. (foo = "hai u") -// Loads a config file. Returns NULL if file doesn't exist. +// Loads a config file. Returns NULL if file doesn't exist. NULL path will create an empty config file. config_file_t *config_file_new(const char *path); // Frees config file. void config_file_free(config_file_t *conf); @@ -48,6 +48,14 @@ bool config_get_string(config_file_t *conf, const char *entry, char **in); // Extracts a boolean from config. Valid boolean true are "true" and "1". Valid false are "false" and "0". Other values will be treated as an error. bool config_get_bool(config_file_t *conf, const char *entry, bool *in); +// Setters. +void config_set_double(config_file_t *conf, const char *entry, double value); +void config_set_int(config_file_t *conf, const char *entry, int val); +void config_set_char(config_file_t *conf, const char *entry, char val); +void config_set_string(config_file_t *conf, const char *entry, const char *val); +void config_set_bool(config_file_t *conf, const char *entry, bool val); +bool config_file_write(config_file_t *conf, const char *path); + #endif From 74f118bb0a8cf2e2b47583b01f9550f3e230bc36 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 14:33:59 +0100 Subject: [PATCH 12/50] remove some stupid code --- input/sdl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/input/sdl.c b/input/sdl.c index 3cfc562cd6..1a89a5658e 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -82,19 +82,19 @@ static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey) switch (GET_HAT_DIR(joykey)) { case HAT_UP_MASK: - if (dir == SDL_HAT_UP || dir == SDL_HAT_RIGHTUP || dir == SDL_HAT_LEFTUP) + if (dir & SDL_HAT_UP) return true; break; case HAT_DOWN_MASK: - if (dir == SDL_HAT_DOWN || dir == SDL_HAT_LEFTDOWN || dir == SDL_HAT_RIGHTDOWN) + if (dir & SDL_HAT_DOWN) return true; break; case HAT_LEFT_MASK: - if (dir == SDL_HAT_LEFT || dir == SDL_HAT_LEFTDOWN || dir == SDL_HAT_LEFTUP) + if (dir & SDL_HAT_LEFT) return true; break; case HAT_RIGHT_MASK: - if (dir == SDL_HAT_RIGHT || dir == SDL_HAT_RIGHTDOWN || dir == SDL_HAT_RIGHTUP) + if (dir & SDL_HAT_RIGHT) return true; break; default: From 413a261f445fe5d56f3823a45d15ba21ad4cfc87 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 15:44:35 +0100 Subject: [PATCH 13/50] Add a joyconfig tool :) --- conf/config_file.c | 17 ++- tools/ssnes-joyconfig.c | 260 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 tools/ssnes-joyconfig.c 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); +} From 9a4158aac864eb2eb68ce61be94a383f2725e7fb Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 15:50:30 +0100 Subject: [PATCH 14/50] Add config_file_dump to avoid calling on SSNES stuff from config file. --- conf/config_file.c | 35 ++++++++++------------------------- conf/config_file.h | 8 +++++++- settings.c | 3 +++ 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/conf/config_file.c b/conf/config_file.c index eccbdddfa2..41e07187fe 100644 --- a/conf/config_file.c +++ b/conf/config_file.c @@ -22,10 +22,6 @@ #include #include -#ifndef STANDALONE -#include "general.h" -#endif - struct entry_list { char *key; @@ -130,20 +126,6 @@ static bool parse_line(struct entry_list *list, char *line) return true; } -static void print_config(config_file_t *conf) -{ - struct entry_list *tmp = conf->entries; - while (tmp != NULL) - { -#ifdef STANDALONE - fprintf(stderr, "Config => Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); -#else - SSNES_LOG("Config => Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); -#endif - tmp = tmp->next; - } -} - config_file_t *config_file_new(const char *path) { @@ -188,8 +170,6 @@ config_file_t *config_file_new(const char *path) } fclose(file); - print_config(conf); - return conf; } @@ -368,6 +348,16 @@ bool config_file_write(config_file_t *conf, const char *path) else file = stdout; + config_file_dump(conf, file); + + if (path) + fclose(file); + + return true; +} + +void config_file_dump(config_file_t *conf, FILE *file) +{ struct entry_list *list = conf->entries; while (list != NULL) @@ -375,9 +365,4 @@ bool config_file_write(config_file_t *conf, const char *path) fprintf(file, "%s = \"%s\"\n", list->key, list->value); list = list->next; } - - if (path) - fclose(file); - - return true; } diff --git a/conf/config_file.h b/conf/config_file.h index a893fdac18..1f19d6d177 100644 --- a/conf/config_file.h +++ b/conf/config_file.h @@ -21,6 +21,7 @@ #include #include +#include typedef struct config_file config_file_t; @@ -48,14 +49,19 @@ bool config_get_string(config_file_t *conf, const char *entry, char **in); // Extracts a boolean from config. Valid boolean true are "true" and "1". Valid false are "false" and "0". Other values will be treated as an error. bool config_get_bool(config_file_t *conf, const char *entry, bool *in); -// Setters. +// Setters. Similiar to the getters. void config_set_double(config_file_t *conf, const char *entry, double value); void config_set_int(config_file_t *conf, const char *entry, int val); void config_set_char(config_file_t *conf, const char *entry, char val); void config_set_string(config_file_t *conf, const char *entry, const char *val); void config_set_bool(config_file_t *conf, const char *entry, bool val); + +// Write the current config to a file. bool config_file_write(config_file_t *conf, const char *path); +// Dump the current config to an already opened file. Does not close the file. +void config_file_dump(config_file_t *conf, FILE *file); + #endif diff --git a/settings.c b/settings.c index 219b77119d..8a5c453103 100644 --- a/settings.c +++ b/settings.c @@ -160,6 +160,9 @@ void parse_config(void) if (conf == NULL) return; + if (g_extern.verbose) + config_file_dump(conf, stderr); + int tmp_int; double tmp_double; bool tmp_bool; From c5770039cc4bcd48f97cf3c10f6629ff77a094b2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 15:58:49 +0100 Subject: [PATCH 15/50] Update build system to include joyconfig --- Makefile | 12 +++++++++--- tools/ssnes-joyconfig.c | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1c42120fc7..5450c47918 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ include config.mk -TARGET = ssnes +TARGET = ssnes tools/ssnes-joyconfig OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o +JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o LIBS = DEFINES = -DHAVE_CONFIG_H @@ -85,6 +86,9 @@ config.mk: configure qb/* ssnes: $(OBJ) $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) +tools/ssnes-joyconfig: $(JOYCONFIG_OBJ) + $(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) + %.o: %.c config.h config.mk $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< @@ -92,8 +96,9 @@ install: $(TARGET) install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg -uninstall: $(TARGET) - rm -rf $(DESTDIR)/$(PREFIX)/bin/$(TARGET) +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/{ssnes,ssnes-joyconfig} + rm -f $(DESTDIR)/etc/ssnes.cfg clean: rm -f *.o @@ -103,6 +108,7 @@ clean: rm -f record/*.o rm -f hqflt/*.o rm -f hqflt/snes_ntsc/*.o + rm -f tools/*.o rm -f $(TARGET) .PHONY: all install uninstall clean diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index f6458df57d..bba3d1d455 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -15,6 +15,10 @@ * If not, see . */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef HAVE_SDL #include "SDL.h" #endif @@ -24,6 +28,7 @@ #include #include + static int g_player = 1; static int g_joypad = 1; static char *g_in_path = NULL; From 1e9decfa5c6ae281412a669fb8ce5d357ccb2550 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 16:08:06 +0100 Subject: [PATCH 16/50] Update win32 makefile --- Makefile.win32 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index d583e99f40..531eb189e4 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -38,11 +38,14 @@ LIBS += $(libsnes) CFLAGS = -Wall -O3 -std=gnu99 -I. -all: $(TARGET) +all: $(TARGET) ssnes-joyconfig.exe $(TARGET): $(OBJ) $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) +ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o + $(CC) -o ssnes-joyconfig.exe $(SDL_LIBS) $(LDFLAGS) conf/config_file.o tools/ssnes-joyconfig.o + %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< @@ -55,8 +58,10 @@ clean: rm -f hqflt/*.o rm -f hqflt/snes_ntsc/*.o rm -f $(TARGET) + rm -f ssnes-joyconfig.exe + rm -f tools/* dist: all - zip -r ssnes-win32.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll + zip -r ssnes-win32.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll ssnes-joyconfig.exe .PHONY: all install uninstall clean dist From 43316083d79f07d1983e89777a1a093446206426 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 17:24:13 +0100 Subject: [PATCH 17/50] Update Win32 makefile --- Makefile.win32 | 9 +++++---- tools/ssnes-joyconfig.c | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index 531eb189e4..b3f4c282fa 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -14,7 +14,7 @@ DEFINES = -I. LDFLAGS = -L. -static-libgcc -s SRC_LIBS = -lsamplerate-0 -SDL_LIBS = -lSDLmain -lSDL +SDL_LIBS = -lmingw32 -lSDLmain -lSDL SDL_CFLAGS = -ISDL ifeq ($(HAVE_SRC), 1) @@ -43,12 +43,13 @@ all: $(TARGET) ssnes-joyconfig.exe $(TARGET): $(OBJ) $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) -ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o - $(CC) -o ssnes-joyconfig.exe $(SDL_LIBS) $(LDFLAGS) conf/config_file.o tools/ssnes-joyconfig.o %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< +ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o + $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) + clean: rm -f *.o rm -f audio/*.o @@ -59,7 +60,7 @@ clean: rm -f hqflt/snes_ntsc/*.o rm -f $(TARGET) rm -f ssnes-joyconfig.exe - rm -f tools/* + rm -f tools/*.o dist: all zip -r ssnes-win32.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll ssnes-joyconfig.exe diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index bba3d1d455..5305729a54 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -252,7 +252,11 @@ static void parse_input(int argc, char *argv[]) int main(int argc, char *argv[]) { + fprintf(stderr, "hai\n"); + fflush(stderr); parse_input(argc, argv); + fprintf(stderr, "hai\n"); + fflush(stderr); config_file_t *conf = config_file_new(g_in_path); get_binds(conf, g_player - 1, g_joypad - 1); @@ -262,4 +266,5 @@ int main(int argc, char *argv[]) free(g_in_path); if (g_out_path) free(g_out_path); + return 0; } From 5e4cd817a6b015efce22db6026f58f44ccb5b351 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 19:22:49 +0100 Subject: [PATCH 18/50] Update makefile yet again... --- Makefile.win32 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.win32 b/Makefile.win32 index b3f4c282fa..6efc170f44 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -48,7 +48,7 @@ $(TARGET): $(OBJ) $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o - $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) + $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) -L. -static-libgcc clean: rm -f *.o From 03e132395266848596f5d43686e48aa2c326284b Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 19:37:55 +0100 Subject: [PATCH 19/50] Windows is RETARDED. --- Makefile.win32 | 6 +++--- tools/main-stub.c | 6 ++++++ tools/ssnes-joyconfig.c | 5 +++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tools/main-stub.c diff --git a/Makefile.win32 b/Makefile.win32 index 6efc170f44..1944110975 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -14,7 +14,7 @@ DEFINES = -I. LDFLAGS = -L. -static-libgcc -s SRC_LIBS = -lsamplerate-0 -SDL_LIBS = -lmingw32 -lSDLmain -lSDL +SDL_LIBS = -lSDL SDL_CFLAGS = -ISDL ifeq ($(HAVE_SRC), 1) @@ -47,8 +47,8 @@ $(TARGET): $(OBJ) %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< -ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o - $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) -L. -static-libgcc +ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o tools/main-stub.o + $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) -L. -static-libgcc tools/main-stub.o clean: rm -f *.o diff --git a/tools/main-stub.c b/tools/main-stub.c new file mode 100644 index 0000000000..0fb5bf8585 --- /dev/null +++ b/tools/main-stub.c @@ -0,0 +1,6 @@ +int real_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + return real_main(argc, argv); +} diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index 5305729a54..e1928dc68c 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -250,7 +250,12 @@ static void parse_input(int argc, char *argv[]) } +// Windows is being bitchy. +#ifdef _WIN32 +int real_main(int argc, char *argv[]) +#else int main(int argc, char *argv[]) +#endif { fprintf(stderr, "hai\n"); fflush(stderr); From 5a599d4cf2a4da6f27826fc7e5b4bf7572a1202d Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Jan 2011 19:50:23 +0100 Subject: [PATCH 20/50] Some additional cleanups. --- Makefile.win32 | 7 +++++-- tools/ssnes-joyconfig.c | 6 +----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index 1944110975..b631ebf4d0 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -1,5 +1,6 @@ TARGET = ssnes.exe OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o +JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o CC = gcc CXX = g++ @@ -47,8 +48,10 @@ $(TARGET): $(OBJ) %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< -ssnes-joyconfig.exe: conf/config_file.o tools/ssnes-joyconfig.o tools/main-stub.o - $(CC) -o ssnes-joyconfig.exe conf/config_file.o tools/ssnes-joyconfig.o $(SDL_LIBS) -L. -static-libgcc tools/main-stub.o + + +ssnes-joyconfig.exe: $(JOBJ) + $(CC) -o ssnes-joyconfig.exe $(JOBJ) $(SDL_LIBS) $(LDFLAGS) clean: rm -f *.o diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index e1928dc68c..f47919c5fe 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -250,18 +250,14 @@ static void parse_input(int argc, char *argv[]) } -// Windows is being bitchy. +// Windows is being bitchy. Cannot include SDL.h with a file that has main() it seems ... It simply won't run at all even with -lSDLmain. #ifdef _WIN32 int real_main(int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif { - fprintf(stderr, "hai\n"); - fflush(stderr); parse_input(argc, argv); - fprintf(stderr, "hai\n"); - fflush(stderr); config_file_t *conf = config_file_new(g_in_path); get_binds(conf, g_player - 1, g_joypad - 1); From b24bb82d0ce55a2f7c4fe32947438a018159a24b Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 07:58:11 +0100 Subject: [PATCH 21/50] Start adding mouse and scope support. --- dynamic.c | 3 +++ dynamic.h | 1 + general.h | 3 +++ ssnes.c | 20 +++++++++++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/dynamic.c b/dynamic.c index 54bf5a1177..22cbd2d5fe 100644 --- a/dynamic.c +++ b/dynamic.c @@ -52,6 +52,7 @@ unsigned (*psnes_library_revision_minor)(void); unsigned (*psnes_library_revision_major)(void); bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); +void (*psnes_set_controller_port_device)(bool, unsigned); unsigned (*psnes_serialize_size)(void); bool (*psnes_serialize)(uint8_t*, unsigned); @@ -85,6 +86,7 @@ static void load_dynamic(void) SYM(snes_library_revision_major); SYM(snes_run); SYM(snes_load_cartridge_normal); + SYM(snes_set_controller_port_device); SYM(snes_serialize_size); SYM(snes_serialize); SYM(snes_unserialize); @@ -112,6 +114,7 @@ static void set_statics(void) SSYM(snes_library_revision_major); SSYM(snes_run); SSYM(snes_load_cartridge_normal); + SSYM(snes_set_controller_port_device); SSYM(snes_serialize_size); SSYM(snes_serialize); SSYM(snes_unserialize); diff --git a/dynamic.h b/dynamic.h index a804da26bc..feb99c249e 100644 --- a/dynamic.h +++ b/dynamic.h @@ -35,6 +35,7 @@ extern unsigned (*psnes_library_revision_minor)(void); extern unsigned (*psnes_library_revision_major)(void); extern bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); +extern void (*psnes_set_controller_port_device)(bool, unsigned); extern unsigned (*psnes_serialize_size)(void); extern bool (*psnes_serialize)(uint8_t*, unsigned); diff --git a/general.h b/general.h index cacf072ea3..19a84af782 100644 --- a/general.h +++ b/general.h @@ -83,6 +83,9 @@ struct global bool audio_active; bool video_active; + bool has_mouse; + bool has_scope; + FILE *rom_file; char config_path[256]; diff --git a/ssnes.c b/ssnes.c index 7dc6723dac..c2971ca811 100644 --- a/ssnes.c +++ b/ssnes.c @@ -239,6 +239,8 @@ static void print_help(void) puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); + puts("\t-m/--mouse: Connect a virtual mouse into port 2 of the SNES."); + puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); #ifdef HAVE_FFMPEG puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); @@ -262,6 +264,8 @@ static void parse_input(int argc, char *argv[]) #endif { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, + { "mouse", 0, NULL, 'm' }, + { "scope", 0, NULL, 'p' }, { "savestate", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; @@ -274,7 +278,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:m" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -300,6 +304,14 @@ static void parse_input(int argc, char *argv[]) g_extern.verbose = true; break; + case 'm': + g_extern.has_mouse = true; + break; + + case 'p': + g_extern.has_scope = true; + break; + case 'c': strncpy(g_extern.config_path, optarg, sizeof(g_extern.config_path) - 1); break; @@ -408,6 +420,12 @@ int main(int argc, char *argv[]) free(rom_buf); + if (g_extern.has_mouse) + psnes_set_controller_port_device(1, SNES_DEVICE_MOUSE); + else if (g_extern.has_scope) + psnes_set_controller_port_device(1, SNES_DEVICE_SUPER_SCOPE); + + unsigned serial_size = psnes_serialize_size(); uint8_t *serial_data = malloc(serial_size); if (serial_data == NULL) From a7e35571cd1296a0295bd88565469bc647145de7 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 08:18:03 +0100 Subject: [PATCH 22/50] Add mapping support for joypads. --- general.h | 1 + input/sdl.c | 49 ++++++++++++++++++++++++++++++------------------- settings.c | 8 ++++++++ ssnes.cfg | 4 ++++ 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/general.h b/general.h index 19a84af782..35e5260117 100644 --- a/general.h +++ b/general.h @@ -71,6 +71,7 @@ struct settings char driver[32]; struct snes_keybind binds[MAX_PLAYERS][MAX_BINDS]; float axis_threshold; + unsigned joypad_map[2]; } input; char libsnes[256]; diff --git a/input/sdl.c b/input/sdl.c index 1a89a5658e..9c8827573f 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -36,23 +36,31 @@ static void* sdl_input_init(void) SDL_JoystickEventState(SDL_IGNORE); sdl->num_joysticks = SDL_NumJoysticks(); - if (sdl->num_joysticks > 2) - sdl->num_joysticks = 2; - for (unsigned i = 0; i < sdl->num_joysticks; i++) - { - sdl->joysticks[i] = SDL_JoystickOpen(i); - if (!sdl->joysticks[i]) - { - SSNES_ERR("Couldn't open SDL joystick %d\n", i); - free(sdl); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - return NULL; - } - SSNES_LOG("Opened Joystick: %s\n", SDL_JoystickName(i)); - sdl->num_axes[i] = SDL_JoystickNumAxes(sdl->joysticks[i]); - sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]); - sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]); + for (unsigned i = 0; i < 2; i++) + { + if (sdl->num_joysticks > g_settings.input.joypad_map[i]) + { + sdl->joysticks[i] = SDL_JoystickOpen(g_settings.input.joypad_map[i]); + if (!sdl->joysticks[i]) + { + SSNES_ERR("Couldn't open SDL joystick #%u on SNES port %u\n", g_settings.input.joypad_map[i], i + 1); + free(sdl); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + return NULL; + } + + SSNES_LOG("Opened Joystick: %s #%u on port %u\n", + SDL_JoystickName(g_settings.input.joypad_map[i]), g_settings.input.joypad_map[i], i + 1); + sdl->num_axes[i] = SDL_JoystickNumAxes(sdl->joysticks[i]); + sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]); + sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]); + } + else + { + SSNES_WARN("Desired SDL joystick #%u on port %u, but SDL can only detect %u joysticks ...\n", + g_settings.input.joypad_map[i], i + 1, sdl->num_joysticks); + } } return sdl; @@ -137,7 +145,7 @@ static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_key { if (sdl_key_pressed(key->key)) return true; - if (port_num >= sdl->num_joysticks) + if (sdl->joysticks[port_num] == NULL) return false; if (sdl_joykey_pressed(sdl, port_num, key->joykey)) return true; @@ -187,8 +195,11 @@ static void sdl_input_free(void *data) while (SDL_PollEvent(&event)); sdl_input_t *sdl = data; - for (int i = 0; i < sdl->num_joysticks; i++) - SDL_JoystickClose(sdl->joysticks[i]); + for (int i = 0; i < 2; i++) + { + if (sdl->joysticks[i]) + SDL_JoystickClose(sdl->joysticks[i]); + } free(data); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); diff --git a/settings.c b/settings.c index 8a5c453103..baa8b9c6e3 100644 --- a/settings.c +++ b/settings.c @@ -112,6 +112,8 @@ static void set_defaults(void) memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2)); g_settings.input.axis_threshold = AXIS_THRESHOLD; + g_settings.input.joypad_map[0] = 0; + g_settings.input.joypad_map[1] = 1; } void parse_config(void) @@ -245,6 +247,12 @@ void parse_config(void) if (config_get_double(conf, "input_axis_threshold", &tmp_double)) g_settings.input.axis_threshold = tmp_double; + if (config_get_int(conf, "input_player1_joypad_index", &tmp_int)) + g_settings.input.joypad_map[0] = tmp_int; + + if (config_get_int(conf, "input_player2_joypad_index", &tmp_int)) + g_settings.input.joypad_map[1] = tmp_int; + // Audio settings. if (config_get_bool(conf, "audio_enable", &tmp_bool)) g_settings.audio.enable = tmp_bool; diff --git a/ssnes.cfg b/ssnes.cfg index 4eda4b7361..d2e372aef2 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -88,6 +88,10 @@ # input_player1_up = up # input_player1_down = down +# If desired, it is possible to override which joypads are being used for player 1 and 2. First joypad available is 0. +# input_player1_joypad_index = 0 +# input_player2_joypad_index = 1 + # Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output. # You can use joypad hats with hnxx, where n is the hat, and xx is a string representing direction. # E.g. "h0up" From dad1ead3e01e9e8456ba628cf70f9074bf1f33c1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 08:40:44 +0100 Subject: [PATCH 23/50] Some crude mouse support, but it does not seem to work. :( --- input/sdl.c | 61 +++++++++++++++++++++++++++++++++++++---- input/ssnes_sdl_input.h | 1 + ssnes.c | 3 ++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/input/sdl.c b/input/sdl.c index 9c8827573f..4eb7188553 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -167,12 +167,9 @@ static bool sdl_bind_button_pressed(void *data, int key) return false; } -static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) +static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, + bool port, unsigned device, unsigned index, unsigned id) { - sdl_input_t *sdl = data; - if (device != SNES_DEVICE_JOYPAD) - return 0; - const struct snes_keybind *snes_keybinds = binds[port == SNES_PORT_1 ? 0 : 1]; // Checks if button is pressed. @@ -186,6 +183,60 @@ static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bo return false; } +static int16_t sdl_mouse_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, + bool port, unsigned device, unsigned index, unsigned id) +{ + int _x, _y; + Uint8 btn = SDL_GetRelativeMouseState(&_x, &_y); + sdl->mouse_x += _x; + sdl->mouse_y += _y; + SSNES_LOG("Mouse rel: %d %d, total %d %d\n", _x, _y, (int)sdl->mouse_x, (int)sdl->mouse_y); + + int16_t retval; + switch (id) + { + case SNES_DEVICE_ID_MOUSE_LEFT: + retval = SDL_BUTTON(1) & btn ? 1 : 0; + break; + case SNES_DEVICE_ID_MOUSE_RIGHT: + retval = SDL_BUTTON(3) & btn ? 1 : 0; + break; + case SNES_DEVICE_ID_MOUSE_X: + retval = sdl->mouse_x; + break; + case SNES_DEVICE_ID_MOUSE_Y: + retval = sdl->mouse_y; + break; + default: + retval = 0; + } + SSNES_LOG("Retval: %d\n", (int)retval); + return retval; +} + +// TODO: :D +static int16_t sdl_scope_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, + bool port, unsigned device, unsigned index, unsigned id) +{ + return 0; +} + +static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) +{ + switch (device) + { + case SNES_DEVICE_JOYPAD: + return sdl_joypad_device_state(data, binds, port, device, index, id); + case SNES_DEVICE_MOUSE: + return sdl_mouse_device_state(data, binds, port, device, index, id); + case SNES_DEVICE_SUPER_SCOPE: + return sdl_scope_device_state(data, binds, port, device, index, id); + + default: + return 0; + } +} + static void sdl_input_free(void *data) { if (data) diff --git a/input/ssnes_sdl_input.h b/input/ssnes_sdl_input.h index 519477d64a..d278b18169 100644 --- a/input/ssnes_sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -32,6 +32,7 @@ typedef struct sdl_input bool *should_resize; unsigned *new_width; unsigned *new_height; + int16_t mouse_x, mouse_y; } sdl_input_t; #endif diff --git a/ssnes.c b/ssnes.c index c2971ca811..848a8d8e9e 100644 --- a/ssnes.c +++ b/ssnes.c @@ -468,6 +468,9 @@ int main(int argc, char *argv[]) // Main loop for(;;) { + // Poll input. + driver.input->poll(driver.input_data); + // Time to drop? if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) || !driver.video->alive(driver.video_data)) From b8bb1b2814f1909645343abf73595fdcb011776d Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 08:55:34 +0100 Subject: [PATCH 24/50] Enable joyconfig to use more joypads. --- tools/ssnes-joyconfig.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index f47919c5fe..86d75411f1 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -216,11 +216,6 @@ static void parse_input(int argc, char *argv[]) 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': @@ -260,6 +255,15 @@ int main(int argc, char *argv[]) parse_input(argc, argv); config_file_t *conf = config_file_new(g_in_path); + if (!conf) + { + fprintf(stderr, "Couldn't open config file ...\n"); + return 1; + } + + config_set_int(conf, g_player == 1 ? "input_player1_joypad_index" : "input_player2_joypad_index", + g_joypad - 1); + get_binds(conf, g_player - 1, g_joypad - 1); config_file_write(conf, g_out_path); config_file_free(conf); From ca42b8ce4b334a32d5a1797c884cde2a41b314c4 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 09:08:39 +0100 Subject: [PATCH 25/50] Add a comment on mouse stuff. --- input/sdl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/input/sdl.c b/input/sdl.c index 4eb7188553..fe57c96b6f 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -183,6 +183,7 @@ static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybi return false; } +// TODO: Broken at the moment. Need more info. static int16_t sdl_mouse_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) { From a2a31e5944b9be56243357447e8d3a55b5f9f7b1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 09:13:22 +0100 Subject: [PATCH 26/50] Update help function in ssnes-joyconfig --- tools/ssnes-joyconfig.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index 86d75411f1..613e8991e0 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -39,9 +39,10 @@ static void print_help(void) puts("=================="); puts("ssnes-joyconfig"); puts("=================="); + puts("Usage: ssnes-joyconfig [ -p/--player <1|2> | -j/--joypad | -i/--input | -o/--output | -h/--help ]"); 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("-j/--joypad: Which joypad to use when configuring (first joypad is 1)."); 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."); From f9bd0ab672e4c7ddae26d37f7c8081a8a47f9d57 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 14:29:00 +0100 Subject: [PATCH 27/50] Fix mouse code for the most part. :) --- general.h | 4 +-- input/sdl.c | 35 ++++++++++++------------- input/ssnes_sdl_input.h | 1 + ssnes.c | 57 +++++++++++++++++++++++++++++++---------- 4 files changed, 63 insertions(+), 34 deletions(-) diff --git a/general.h b/general.h index 35e5260117..5bd362a09a 100644 --- a/general.h +++ b/general.h @@ -84,8 +84,8 @@ struct global bool audio_active; bool video_active; - bool has_mouse; - bool has_scope; + bool has_mouse[2]; + bool has_scope[2]; FILE *rom_file; char config_path[256]; diff --git a/input/sdl.c b/input/sdl.c index fe57c96b6f..6ca4b06d76 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -183,36 +183,22 @@ static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybi return false; } -// TODO: Broken at the moment. Need more info. static int16_t sdl_mouse_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) { - int _x, _y; - Uint8 btn = SDL_GetRelativeMouseState(&_x, &_y); - sdl->mouse_x += _x; - sdl->mouse_y += _y; - SSNES_LOG("Mouse rel: %d %d, total %d %d\n", _x, _y, (int)sdl->mouse_x, (int)sdl->mouse_y); - - int16_t retval; switch (id) { case SNES_DEVICE_ID_MOUSE_LEFT: - retval = SDL_BUTTON(1) & btn ? 1 : 0; - break; + return sdl->mouse_l; case SNES_DEVICE_ID_MOUSE_RIGHT: - retval = SDL_BUTTON(3) & btn ? 1 : 0; - break; + return sdl->mouse_r; case SNES_DEVICE_ID_MOUSE_X: - retval = sdl->mouse_x; - break; + return sdl->mouse_x; case SNES_DEVICE_ID_MOUSE_Y: - retval = sdl->mouse_y; - break; + return sdl->mouse_y; default: - retval = 0; + return 0; } - SSNES_LOG("Retval: %d\n", (int)retval); - return retval; } // TODO: :D @@ -258,11 +244,22 @@ static void sdl_input_free(void *data) } } +static void sdl_poll_mouse(sdl_input_t *sdl) +{ + int _x, _y; + Uint8 btn = SDL_GetRelativeMouseState(&_x, &_y); + sdl->mouse_x = _x; + sdl->mouse_y = _y; + sdl->mouse_l = SDL_BUTTON(1) & btn ? 1 : 0; + sdl->mouse_r = SDL_BUTTON(3) & btn ? 1 : 0; +} + static void sdl_input_poll(void *data) { SDL_PumpEvents(); SDL_Event event; SDL_JoystickUpdate(); + sdl_poll_mouse(data); sdl_input_t *sdl = data; // Search for events... diff --git a/input/ssnes_sdl_input.h b/input/ssnes_sdl_input.h index d278b18169..b1c0cc07c3 100644 --- a/input/ssnes_sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -33,6 +33,7 @@ typedef struct sdl_input unsigned *new_width; unsigned *new_height; int16_t mouse_x, mouse_y; + int16_t mouse_l, mouse_r; } sdl_input_t; #endif diff --git a/ssnes.c b/ssnes.c index 848a8d8e9e..837d4c5fcb 100644 --- a/ssnes.c +++ b/ssnes.c @@ -234,13 +234,15 @@ static void print_help(void) puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -s/--save" FFMPEG_HELP_QUARK "]"); + puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -t/--savestate | -m/--mouse | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); - puts("\t-m/--mouse: Connect a virtual mouse into port 2 of the SNES."); - puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); + puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); + puts("\tThis argument can be specified several times to connect more mice."); + puts("\t-p/--scope: Connect a virtual SuperScope into designated port of the SNES (1 or 2)."); + puts("\tThis argument can be specified several times to connect more scopes."); #ifdef HAVE_FFMPEG puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); @@ -264,8 +266,8 @@ static void parse_input(int argc, char *argv[]) #endif { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, - { "mouse", 0, NULL, 'm' }, - { "scope", 0, NULL, 'p' }, + { "mouse", 1, NULL, 'm' }, + { "scope", 1, NULL, 'p' }, { "savestate", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; @@ -278,10 +280,11 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:m" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:m:p:" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); + int port; if (c == -1) break; @@ -305,11 +308,25 @@ static void parse_input(int argc, char *argv[]) break; case 'm': - g_extern.has_mouse = true; + port = strtol(optarg, NULL, 0); + if (port < 1 || port > 2) + { + SSNES_ERR("Connect mouse to port 1 or 2.\n"); + print_help(); + exit(1); + } + g_extern.has_mouse[port - 1] = true; break; case 'p': - g_extern.has_scope = true; + port = strtol(optarg, NULL, 0); + if (port < 1 || port > 2) + { + SSNES_ERR("Connect scope to port 1 or 2.\n"); + print_help(); + exit(1); + } + g_extern.has_scope[port - 1] = true; break; case 'c': @@ -377,6 +394,24 @@ static void parse_input(int argc, char *argv[]) } } +// TODO: Add rest of the controllers. +static void init_controllers(void) +{ + for (int i = 0; i < 2; i++) + { + if (g_extern.has_mouse[i]) + { + SSNES_LOG("Connecting mouse to port %d\n", i + 1); + psnes_set_controller_port_device(i, SNES_DEVICE_MOUSE); + } + else if (g_extern.has_scope[i]) + { + SSNES_LOG("Connecting scope to port %d\n", i + 1); + psnes_set_controller_port_device(i, SNES_DEVICE_SUPER_SCOPE); + } + } +} + int main(int argc, char *argv[]) { parse_input(argc, argv); @@ -420,11 +455,7 @@ int main(int argc, char *argv[]) free(rom_buf); - if (g_extern.has_mouse) - psnes_set_controller_port_device(1, SNES_DEVICE_MOUSE); - else if (g_extern.has_scope) - psnes_set_controller_port_device(1, SNES_DEVICE_SUPER_SCOPE); - + init_controllers(); unsigned serial_size = psnes_serialize_size(); uint8_t *serial_data = malloc(serial_size); From f61e0d49fed82ca3f74ca8712d165096adb0f6a3 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 14:39:14 +0100 Subject: [PATCH 28/50] Super Scope can only be plugged in port 2. --- ssnes.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/ssnes.c b/ssnes.c index 837d4c5fcb..78d08125c7 100644 --- a/ssnes.c +++ b/ssnes.c @@ -241,8 +241,7 @@ static void print_help(void) puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); puts("\tThis argument can be specified several times to connect more mice."); - puts("\t-p/--scope: Connect a virtual SuperScope into designated port of the SNES (1 or 2)."); - puts("\tThis argument can be specified several times to connect more scopes."); + puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); #ifdef HAVE_FFMPEG puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); @@ -267,7 +266,7 @@ static void parse_input(int argc, char *argv[]) { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, { "mouse", 1, NULL, 'm' }, - { "scope", 1, NULL, 'p' }, + { "scope", 0, NULL, 'p' }, { "savestate", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; @@ -280,7 +279,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:m:p:" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:m:p" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -319,14 +318,7 @@ static void parse_input(int argc, char *argv[]) break; case 'p': - port = strtol(optarg, NULL, 0); - if (port < 1 || port > 2) - { - SSNES_ERR("Connect scope to port 1 or 2.\n"); - print_help(); - exit(1); - } - g_extern.has_scope[port - 1] = true; + g_extern.has_scope[1] = true; break; case 'c': From 080c07601906aa8e883821fc46b8f997b7ecaa26 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 14:39:48 +0100 Subject: [PATCH 29/50] Divide audio samples by 0x8000 as INT64_min is -0x8000. :p --- ssnes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssnes.c b/ssnes.c index 78d08125c7..cb322e68d2 100644 --- a/ssnes.c +++ b/ssnes.c @@ -163,8 +163,8 @@ static void audio_sample(uint16_t left, uint16_t right) static float data[AUDIO_CHUNK_SIZE_NONBLOCKING]; static int data_ptr = 0; - data[data_ptr++] = (float)(*(int16_t*)&left)/0x7FFF; - data[data_ptr++] = (float)(*(int16_t*)&right)/0x7FFF; + data[data_ptr++] = (float)(*(int16_t*)&left)/0x8000; + data[data_ptr++] = (float)(*(int16_t*)&right)/0x8000; if ( data_ptr >= audio_chunk_size ) { From dbd15a1d09c3ff0d87798cb3331aaa93498733d2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 15:01:34 +0100 Subject: [PATCH 30/50] Add more super scope stuff. Crude fix in Makefile. --- Makefile | 3 ++- input/sdl.c | 35 +++++++++++++++++++++++++---------- input/ssnes_sdl_input.h | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 5450c47918..e18bd045f2 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ TARGET = ssnes tools/ssnes-joyconfig OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o +HEADERS = $(wildcard */*.h) $(wildcard *.h) LIBS = DEFINES = -DHAVE_CONFIG_H @@ -89,7 +90,7 @@ ssnes: $(OBJ) tools/ssnes-joyconfig: $(JOYCONFIG_OBJ) $(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) -%.o: %.c config.h config.mk +%.o: %.c config.h config.mk $(HEADERS) $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< install: $(TARGET) diff --git a/input/sdl.c b/input/sdl.c index 6ca4b06d76..d4d67b78cd 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -183,9 +183,10 @@ static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybi return false; } -static int16_t sdl_mouse_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, - bool port, unsigned device, unsigned index, unsigned id) +static int16_t sdl_mouse_device_state(sdl_input_t *sdl, bool port, unsigned id) { + // Might implement support for joypad mapping later. + (void)port; switch (id) { case SNES_DEVICE_ID_MOUSE_LEFT: @@ -201,11 +202,24 @@ static int16_t sdl_mouse_device_state(sdl_input_t *sdl, const struct snes_keybin } } -// TODO: :D -static int16_t sdl_scope_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, - bool port, unsigned device, unsigned index, unsigned id) +// TODO: Missing some controllers, but hey :) +static int16_t sdl_scope_device_state(sdl_input_t *sdl, unsigned id) { - return 0; + switch (id) + { + case SNES_DEVICE_ID_SUPER_SCOPE_X: + return sdl->mouse_x; + case SNES_DEVICE_ID_SUPER_SCOPE_Y: + return sdl->mouse_y; + case SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER: + return sdl->mouse_l; + case SNES_DEVICE_ID_SUPER_SCOPE_CURSOR: + return sdl->mouse_m; + case SNES_DEVICE_ID_SUPER_SCOPE_TURBO: + return sdl->mouse_r; + default: + return 0; + } } static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) @@ -215,9 +229,9 @@ static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bo case SNES_DEVICE_JOYPAD: return sdl_joypad_device_state(data, binds, port, device, index, id); case SNES_DEVICE_MOUSE: - return sdl_mouse_device_state(data, binds, port, device, index, id); + return sdl_mouse_device_state(data, port, id); case SNES_DEVICE_SUPER_SCOPE: - return sdl_scope_device_state(data, binds, port, device, index, id); + return sdl_scope_device_state(data, id); default: return 0; @@ -250,8 +264,9 @@ static void sdl_poll_mouse(sdl_input_t *sdl) Uint8 btn = SDL_GetRelativeMouseState(&_x, &_y); sdl->mouse_x = _x; sdl->mouse_y = _y; - sdl->mouse_l = SDL_BUTTON(1) & btn ? 1 : 0; - sdl->mouse_r = SDL_BUTTON(3) & btn ? 1 : 0; + sdl->mouse_l = SDL_BUTTON(SDL_BUTTON_LEFT) & btn ? 1 : 0; + sdl->mouse_r = SDL_BUTTON(SDL_BUTTON_RIGHT) & btn ? 1 : 0; + sdl->mouse_m = SDL_BUTTON(SDL_BUTTON_MIDDLE) & btn ? 1 : 0; } static void sdl_input_poll(void *data) diff --git a/input/ssnes_sdl_input.h b/input/ssnes_sdl_input.h index b1c0cc07c3..c87d6e3ca5 100644 --- a/input/ssnes_sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -33,7 +33,7 @@ typedef struct sdl_input unsigned *new_width; unsigned *new_height; int16_t mouse_x, mouse_y; - int16_t mouse_l, mouse_r; + int16_t mouse_l, mouse_r, mouse_m; } sdl_input_t; #endif From 422ba8a80149ff478e4d77927b7ada78b45522cc Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 16:53:37 +0100 Subject: [PATCH 31/50] Add support for justifiers and multitap. --- config.def.h | 54 +++++++++++++++++++++++++ general.h | 8 +++- input/sdl.c | 43 ++++++++++++++++---- input/ssnes_sdl_input.h | 9 +++-- settings.c | 87 ++++++++++++++++++++++++++++++++++++++--- ssnes.c | 61 +++++++++++++++++++++++------ tools/ssnes-joyconfig.c | 22 +++++++---- 7 files changed, 247 insertions(+), 37 deletions(-) diff --git a/config.def.h b/config.def.h index ad46235515..1550fd3f47 100644 --- a/config.def.h +++ b/config.def.h @@ -183,5 +183,59 @@ static const struct snes_keybind snes_keybinds_2[] = { { -1 } }; +// Player 3 +static const struct snes_keybind snes_keybinds_3[] = { + // SNES button | keyboard key | js btn | js axis | + { SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) }, + { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) }, + { SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE }, + { -1 } +}; + +// Player 4 +static const struct snes_keybind snes_keybinds_4[] = { + // SNES button | keyboard key | js btn | js axis | + { SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) }, + { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) }, + { SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE }, + { -1 } +}; + +// Player 5 +static const struct snes_keybind snes_keybinds_5[] = { + // SNES button | keyboard key | js btn | js axis | + { SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) }, + { SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) }, + { SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE }, + { -1 } +}; + #endif diff --git a/general.h b/general.h index 5bd362a09a..c95fb0f495 100644 --- a/general.h +++ b/general.h @@ -33,8 +33,9 @@ #endif -#define MAX_PLAYERS 2 // Should be 5 when multi-tap stuff is added ... +#define MAX_PLAYERS 5 #define MAX_BINDS 18 // Needs to be increased every time there are new binds added. +#define SSNES_NO_JOYPAD 0xFFFF struct settings { struct @@ -71,7 +72,7 @@ struct settings char driver[32]; struct snes_keybind binds[MAX_PLAYERS][MAX_BINDS]; float axis_threshold; - unsigned joypad_map[2]; + unsigned joypad_map[MAX_PLAYERS]; } input; char libsnes[256]; @@ -86,6 +87,9 @@ struct global bool has_mouse[2]; bool has_scope[2]; + bool has_justifier; + bool has_justifiers; + bool has_multitap; FILE *rom_file; char config_path[256]; diff --git a/input/sdl.c b/input/sdl.c index d4d67b78cd..5899fc46dd 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -37,8 +37,11 @@ static void* sdl_input_init(void) SDL_JoystickEventState(SDL_IGNORE); sdl->num_joysticks = SDL_NumJoysticks(); - for (unsigned i = 0; i < 2; i++) + for (unsigned i = 0; i < MAX_PLAYERS; i++) { + if (g_settings.input.joypad_map[i] == SSNES_NO_JOYPAD) + continue; + if (sdl->num_joysticks > g_settings.input.joypad_map[i]) { sdl->joysticks[i] = SDL_JoystickOpen(g_settings.input.joypad_map[i]); @@ -168,12 +171,10 @@ static bool sdl_bind_button_pressed(void *data, int key) } static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybind **binds, - bool port, unsigned device, unsigned index, unsigned id) + int port_num, unsigned device, unsigned index, unsigned id) { - const struct snes_keybind *snes_keybinds = binds[port == SNES_PORT_1 ? 0 : 1]; + const struct snes_keybind *snes_keybinds = binds[port_num]; - // Checks if button is pressed. - int port_num = port == SNES_PORT_1 ? 0 : 1; for (int i = 0; snes_keybinds[i].id != -1; i++) { if (snes_keybinds[i].id == (int)id) @@ -222,16 +223,44 @@ static int16_t sdl_scope_device_state(sdl_input_t *sdl, unsigned id) } } +// TODO: Support two players. +static int16_t sdl_justifier_device_state(sdl_input_t *sdl, unsigned index, unsigned id) +{ + if (index == 0) + { + switch (id) + { + case SNES_DEVICE_ID_JUSTIFIER_X: + return sdl->mouse_x; + case SNES_DEVICE_ID_JUSTIFIER_Y: + return sdl->mouse_y; + case SNES_DEVICE_ID_JUSTIFIER_TRIGGER: + return sdl->mouse_l; + case SNES_DEVICE_ID_JUSTIFIER_START: + return sdl->mouse_r; + default: + return 0; + } + } + else + return 0; +} + static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) { switch (device) { case SNES_DEVICE_JOYPAD: - return sdl_joypad_device_state(data, binds, port, device, index, id); + return sdl_joypad_device_state(data, binds, port == SNES_PORT_1 ? 0 : 1, device, index, id); + case SNES_DEVICE_MULTITAP: + return sdl_joypad_device_state(data, binds, (port == SNES_PORT_2) ? 1 + index : 0, device, index, id); case SNES_DEVICE_MOUSE: return sdl_mouse_device_state(data, port, id); case SNES_DEVICE_SUPER_SCOPE: return sdl_scope_device_state(data, id); + case SNES_DEVICE_JUSTIFIER: + case SNES_DEVICE_JUSTIFIERS: + return sdl_justifier_device_state(data, index, id); default: return 0; @@ -247,7 +276,7 @@ static void sdl_input_free(void *data) while (SDL_PollEvent(&event)); sdl_input_t *sdl = data; - for (int i = 0; i < 2; i++) + for (int i = 0; i < MAX_PLAYERS; i++) { if (sdl->joysticks[i]) SDL_JoystickClose(sdl->joysticks[i]); diff --git a/input/ssnes_sdl_input.h b/input/ssnes_sdl_input.h index c87d6e3ca5..7e92749ceb 100644 --- a/input/ssnes_sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -19,12 +19,13 @@ #define __SSNES_SDL_INPUT_H #include "SDL.h" +#include "general.h" typedef struct sdl_input { - SDL_Joystick *joysticks[2]; - unsigned num_axes[2]; - unsigned num_buttons[2]; - unsigned num_hats[2]; + SDL_Joystick *joysticks[MAX_PLAYERS]; + unsigned num_axes[MAX_PLAYERS]; + unsigned num_buttons[MAX_PLAYERS]; + unsigned num_hats[MAX_PLAYERS]; unsigned num_joysticks; // A video driver could pre-init with the SDL driver and have it handle resizing events... diff --git a/settings.c b/settings.c index baa8b9c6e3..e9a36e88be 100644 --- a/settings.c +++ b/settings.c @@ -108,12 +108,20 @@ static void set_defaults(void) assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1)); assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_2)); + assert(sizeof(g_settings.input.binds[2]) >= sizeof(snes_keybinds_3)); + assert(sizeof(g_settings.input.binds[3]) >= sizeof(snes_keybinds_4)); + assert(sizeof(g_settings.input.binds[4]) >= sizeof(snes_keybinds_5)); memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1)); memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2)); + memcpy(g_settings.input.binds[2], snes_keybinds_3, sizeof(snes_keybinds_3)); + memcpy(g_settings.input.binds[3], snes_keybinds_4, sizeof(snes_keybinds_4)); + memcpy(g_settings.input.binds[4], snes_keybinds_5, sizeof(snes_keybinds_5)); g_settings.input.axis_threshold = AXIS_THRESHOLD; - g_settings.input.joypad_map[0] = 0; - g_settings.input.joypad_map[1] = 1; + for (int i = 0; i < 2; i++) + g_settings.input.joypad_map[i] = i; + for (int i = 2; i < MAX_PLAYERS; i++) + g_settings.input.joypad_map[i] = SSNES_NO_JOYPAD; } void parse_config(void) @@ -247,12 +255,22 @@ void parse_config(void) if (config_get_double(conf, "input_axis_threshold", &tmp_double)) g_settings.input.axis_threshold = tmp_double; + // Joypad mapping. if (config_get_int(conf, "input_player1_joypad_index", &tmp_int)) g_settings.input.joypad_map[0] = tmp_int; if (config_get_int(conf, "input_player2_joypad_index", &tmp_int)) g_settings.input.joypad_map[1] = tmp_int; + if (config_get_int(conf, "input_player3_joypad_index", &tmp_int)) + g_settings.input.joypad_map[2] = tmp_int; + + if (config_get_int(conf, "input_player4_joypad_index", &tmp_int)) + g_settings.input.joypad_map[3] = tmp_int; + + if (config_get_int(conf, "input_player5_joypad_index", &tmp_int)) + g_settings.input.joypad_map[4] = tmp_int; + // Audio settings. if (config_get_bool(conf, "audio_enable", &tmp_bool)) g_settings.audio.enable = tmp_bool; @@ -321,7 +339,7 @@ struct bind_map #define DECLARE_BIND(x, bind) { "input_" #x, "input_" #x "_btn", "input_" #x "_axis", bind }, // Big and nasty bind map... :) -static const struct bind_map bind_maps[2][MAX_BINDS - 1] = { +static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { { DECLARE_BIND(player1_a, SNES_DEVICE_ID_JOYPAD_A) DECLARE_BIND(player1_b, SNES_DEVICE_ID_JOYPAD_B) @@ -359,7 +377,64 @@ static const struct bind_map bind_maps[2][MAX_BINDS - 1] = { DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) - } + }, + { + DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A) + DECLARE_BIND(player3_b, SNES_DEVICE_ID_JOYPAD_B) + DECLARE_BIND(player3_y, SNES_DEVICE_ID_JOYPAD_Y) + DECLARE_BIND(player3_x, SNES_DEVICE_ID_JOYPAD_X) + DECLARE_BIND(player3_start, SNES_DEVICE_ID_JOYPAD_START) + DECLARE_BIND(player3_select, SNES_DEVICE_ID_JOYPAD_SELECT) + DECLARE_BIND(player3_l, SNES_DEVICE_ID_JOYPAD_L) + DECLARE_BIND(player3_r, SNES_DEVICE_ID_JOYPAD_R) + DECLARE_BIND(player3_left, SNES_DEVICE_ID_JOYPAD_LEFT) + DECLARE_BIND(player3_right, SNES_DEVICE_ID_JOYPAD_RIGHT) + DECLARE_BIND(player3_up, SNES_DEVICE_ID_JOYPAD_UP) + DECLARE_BIND(player3_down, SNES_DEVICE_ID_JOYPAD_DOWN) + DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY) + DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY) + DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) + DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) + DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) + }, + { + DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A) + DECLARE_BIND(player4_b, SNES_DEVICE_ID_JOYPAD_B) + DECLARE_BIND(player4_y, SNES_DEVICE_ID_JOYPAD_Y) + DECLARE_BIND(player4_x, SNES_DEVICE_ID_JOYPAD_X) + DECLARE_BIND(player4_start, SNES_DEVICE_ID_JOYPAD_START) + DECLARE_BIND(player4_select, SNES_DEVICE_ID_JOYPAD_SELECT) + DECLARE_BIND(player4_l, SNES_DEVICE_ID_JOYPAD_L) + DECLARE_BIND(player4_r, SNES_DEVICE_ID_JOYPAD_R) + DECLARE_BIND(player4_left, SNES_DEVICE_ID_JOYPAD_LEFT) + DECLARE_BIND(player4_right, SNES_DEVICE_ID_JOYPAD_RIGHT) + DECLARE_BIND(player4_up, SNES_DEVICE_ID_JOYPAD_UP) + DECLARE_BIND(player4_down, SNES_DEVICE_ID_JOYPAD_DOWN) + DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY) + DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY) + DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) + DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) + DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) + }, + { + DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A) + DECLARE_BIND(player5_b, SNES_DEVICE_ID_JOYPAD_B) + DECLARE_BIND(player5_y, SNES_DEVICE_ID_JOYPAD_Y) + DECLARE_BIND(player5_x, SNES_DEVICE_ID_JOYPAD_X) + DECLARE_BIND(player5_start, SNES_DEVICE_ID_JOYPAD_START) + DECLARE_BIND(player5_select, SNES_DEVICE_ID_JOYPAD_SELECT) + DECLARE_BIND(player5_l, SNES_DEVICE_ID_JOYPAD_L) + DECLARE_BIND(player5_r, SNES_DEVICE_ID_JOYPAD_R) + DECLARE_BIND(player5_left, SNES_DEVICE_ID_JOYPAD_LEFT) + DECLARE_BIND(player5_right, SNES_DEVICE_ID_JOYPAD_RIGHT) + DECLARE_BIND(player5_up, SNES_DEVICE_ID_JOYPAD_UP) + DECLARE_BIND(player5_down, SNES_DEVICE_ID_JOYPAD_DOWN) + DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY) + DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY) + DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY) + DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) + DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) + }, }; struct key_map @@ -436,9 +511,9 @@ static void read_keybinds(config_file_t *conf) char *tmp_btn = NULL; char *tmp_axis = NULL; - for (int j = 0; j < 1; j++) + for (int j = 0; j < MAX_PLAYERS; j++) { - for (int i = 0; i < sizeof(bind_maps[j])/sizeof(struct bind_map); i++) + for (int i = 0; i < sizeof(bind_maps[0])/sizeof(struct bind_map); i++) { struct snes_keybind *bind = find_snes_bind(j, bind_maps[j][i].snes_key); if (!bind) diff --git a/ssnes.c b/ssnes.c index cb322e68d2..17c4ceb42b 100644 --- a/ssnes.c +++ b/ssnes.c @@ -201,7 +201,10 @@ static void input_poll(void) static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id) { - const struct snes_keybind *binds[] = { g_settings.input.binds[0], g_settings.input.binds[1] }; + const struct snes_keybind *binds[MAX_PLAYERS]; + for (int i = 0; i < MAX_PLAYERS; i++) + binds[i] = g_settings.input.binds[i]; + return driver.input->input_state(driver.input_data, binds, port, device, index, id); } @@ -242,6 +245,9 @@ static void print_help(void) puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); puts("\tThis argument can be specified several times to connect more mice."); puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); + puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES."); + puts("\t-k/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES."); + puts("\t-4/--multitap: Connect a multitap to port 2 of the SNES."); #ifdef HAVE_FFMPEG puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); @@ -268,6 +274,9 @@ static void parse_input(int argc, char *argv[]) { "mouse", 1, NULL, 'm' }, { "scope", 0, NULL, 'p' }, { "savestate", 1, NULL, 't' }, + { "justifier", 0, NULL, 'j' }, + { "justifiers", 0, NULL, 'k' }, + { "multitap", 0, NULL, '4' }, { NULL, 0, NULL, 0 } }; @@ -279,7 +288,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:m:p" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:m:p4jk" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -294,6 +303,18 @@ static void parse_input(int argc, char *argv[]) print_help(); exit(0); + case '4': + g_extern.has_multitap = true; + break; + + case 'j': + g_extern.has_justifier = true; + break; + + case 'k': + g_extern.has_justifiers = true; + break; + case 's': strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm) - 1); break; @@ -389,17 +410,35 @@ static void parse_input(int argc, char *argv[]) // TODO: Add rest of the controllers. static void init_controllers(void) { - for (int i = 0; i < 2; i++) + if (g_extern.has_justifier) { - if (g_extern.has_mouse[i]) + SSNES_LOG("Connecting Justifier to port 2.\n"); + psnes_set_controller_port_device(SNES_PORT_2, SNES_DEVICE_JUSTIFIER); + } + else if (g_extern.has_justifiers) + { + SSNES_LOG("Connecting Justifiers to port 2.\n"); + psnes_set_controller_port_device(SNES_PORT_2, SNES_DEVICE_JUSTIFIERS); + } + else if (g_extern.has_multitap) + { + SSNES_LOG("Connecting multitap to port 2.\n"); + psnes_set_controller_port_device(SNES_PORT_2, SNES_DEVICE_MULTITAP); + } + else + { + for (int i = 0; i < 2; i++) { - SSNES_LOG("Connecting mouse to port %d\n", i + 1); - psnes_set_controller_port_device(i, SNES_DEVICE_MOUSE); - } - else if (g_extern.has_scope[i]) - { - SSNES_LOG("Connecting scope to port %d\n", i + 1); - psnes_set_controller_port_device(i, SNES_DEVICE_SUPER_SCOPE); + if (g_extern.has_mouse[i]) + { + SSNES_LOG("Connecting mouse to port %d\n", i + 1); + psnes_set_controller_port_device(i, SNES_DEVICE_MOUSE); + } + else if (g_extern.has_scope[i]) + { + SSNES_LOG("Connecting scope to port %d\n", i + 1); + psnes_set_controller_port_device(i, SNES_DEVICE_SUPER_SCOPE); + } } } } diff --git a/tools/ssnes-joyconfig.c b/tools/ssnes-joyconfig.c index 613e8991e0..b5c0ee8c23 100644 --- a/tools/ssnes-joyconfig.c +++ b/tools/ssnes-joyconfig.c @@ -27,6 +27,7 @@ #include #include #include +#include "general.h" static int g_player = 1; @@ -52,11 +53,11 @@ static void print_help(void) struct bind { char *keystr; - char *confbtn[2]; - char *confaxis[2]; + char *confbtn[MAX_PLAYERS]; + char *confaxis[MAX_PLAYERS]; }; -#define BIND(x, k) { x, { "input_player1_" #k "_btn", "input_player2_" #k "_btn" }, {"input_player1_" #k "_axis", "input_player2_" #k "_axis"}}, +#define BIND(x, k) { x, { "input_player1_" #k "_btn", "input_player2_" #k "_btn", "input_player3_" #k "_btn", "input_player4_" #k "_btn", "input_player5_" #k "_btn" }, {"input_player1_" #k "_axis", "input_player2_" #k "_axis", "input_player3_" #k "_axis", "input_player4_" #k "_axis", "input_player5_" #k "_axis"}}, static struct bind binds[] = { BIND("A button (right)", a) BIND("B button (down)", b) @@ -226,9 +227,9 @@ static void parse_input(int argc, char *argv[]) fprintf(stderr, "Player number must be at least 1!\n"); exit(1); } - else if (g_player > 2) + else if (g_player > MAX_PLAYERS) { - fprintf(stderr, "Player number must be 1 or 2.\n"); + fprintf(stderr, "Player number must be from 1 to %d.\n", MAX_PLAYERS); exit(1); } break; @@ -262,8 +263,15 @@ int main(int argc, char *argv[]) return 1; } - config_set_int(conf, g_player == 1 ? "input_player1_joypad_index" : "input_player2_joypad_index", - g_joypad - 1); + const char *index_list[] = { + "input_player1_joypad_index", + "input_player2_joypad_index", + "input_player3_joypad_index", + "input_player4_joypad_index", + "input_player5_joypad_index" + }; + + config_set_int(conf, index_list[g_player - 1], g_joypad - 1); get_binds(conf, g_player - 1, g_joypad - 1); config_file_write(conf, g_out_path); From 220f07d7da3bee3ceffb34d5170493832725db6a Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 17:15:27 +0100 Subject: [PATCH 32/50] Pretty print makefiles (again). --- Makefile | 13 ++++++++++--- Makefile.win32 | 16 ++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index e18bd045f2..812232cf58 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,10 @@ else LIBS += $(libsnes) endif +ifneq ($(V),1) + Q := @ +endif + CFLAGS = -Wall -O3 -g -std=gnu99 -I. all: $(TARGET) config.mk @@ -85,13 +89,16 @@ config.mk: configure qb/* @exit 1 ssnes: $(OBJ) - $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + $(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + @$(if $(Q), $(shell echo echo LD $@),) tools/ssnes-joyconfig: $(JOYCONFIG_OBJ) - $(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) + $(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) + @$(if $(Q), $(shell echo echo LD $@),) %.o: %.c config.h config.mk $(HEADERS) - $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + @$(if $(Q), $(shell echo echo CC $<),) install: $(TARGET) install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin diff --git a/Makefile.win32 b/Makefile.win32 index b631ebf4d0..bf0132ed9b 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -35,6 +35,10 @@ ifeq ($(HAVE_XML), 1) LIBS += -lxml2 endif +ifneq $($(V),1) + Q := @ +endif + LIBS += $(libsnes) CFLAGS = -Wall -O3 -std=gnu99 -I. @@ -42,16 +46,16 @@ CFLAGS = -Wall -O3 -std=gnu99 -I. all: $(TARGET) ssnes-joyconfig.exe $(TARGET): $(OBJ) - $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) - + $(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + @$(if $(Q), $(shell echo echo LD $@),) %.o: %.c - $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< - - + $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + @$(if $(Q), $(shell echo echo CC $<),) ssnes-joyconfig.exe: $(JOBJ) - $(CC) -o ssnes-joyconfig.exe $(JOBJ) $(SDL_LIBS) $(LDFLAGS) + $(Q)$(CC) -o ssnes-joyconfig.exe $(JOBJ) $(SDL_LIBS) $(LDFLAGS) + @$(if $(Q), $(shell echo echo LD $@),) clean: rm -f *.o From 9f122f14adec2cd116b8920cf2c2f331278222b8 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 17:20:08 +0100 Subject: [PATCH 33/50] Fix makefile issues --- Makefile | 1 + Makefile.win32 | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 812232cf58..a7b9aaa91b 100644 --- a/Makefile +++ b/Makefile @@ -116,6 +116,7 @@ clean: rm -f record/*.o rm -f hqflt/*.o rm -f hqflt/snes_ntsc/*.o + rm -f input/*.o rm -f tools/*.o rm -f $(TARGET) diff --git a/Makefile.win32 b/Makefile.win32 index bf0132ed9b..348b99859f 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -35,7 +35,7 @@ ifeq ($(HAVE_XML), 1) LIBS += -lxml2 endif -ifneq $($(V),1) +ifneq ($(V),1) Q := @ endif @@ -64,6 +64,7 @@ clean: rm -f gfx/*.o rm -f record/*.o rm -f hqflt/*.o + rm -f input/*.o rm -f hqflt/snes_ntsc/*.o rm -f $(TARGET) rm -f ssnes-joyconfig.exe From aad1d8798d0c25f76b64570a1850bf4f589bf20a Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 10 Jan 2011 17:34:26 +0100 Subject: [PATCH 34/50] Update --help. --- ssnes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssnes.c b/ssnes.c index 17c4ceb42b..6113b77eb4 100644 --- a/ssnes.c +++ b/ssnes.c @@ -237,13 +237,13 @@ static void print_help(void) puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -t/--savestate | -m/--mouse | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); + puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -k/--justifiers | -t/--savestate | -m/--mouse | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); - puts("\tThis argument can be specified several times to connect more mice."); + puts("\t\tThis argument can be specified several times to connect more mice."); puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES."); puts("\t-k/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES."); From 9e09b55e89bcb6514c3537a3560c03e2c3dd3fc4 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 16:53:31 +0100 Subject: [PATCH 35/50] Add psnes_get_region which was missing. --- dynamic.c | 4 ++++ dynamic.h | 2 ++ ssnes.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dynamic.c b/dynamic.c index 22cbd2d5fe..7769390b78 100644 --- a/dynamic.c +++ b/dynamic.c @@ -54,6 +54,8 @@ unsigned (*psnes_library_revision_major)(void); bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); void (*psnes_set_controller_port_device)(bool, unsigned); +bool (*psnes_get_region)(void); + unsigned (*psnes_serialize_size)(void); bool (*psnes_serialize)(uint8_t*, unsigned); bool (*psnes_unserialize)(const uint8_t*, unsigned); @@ -85,6 +87,7 @@ static void load_dynamic(void) SYM(snes_library_revision_minor); SYM(snes_library_revision_major); SYM(snes_run); + SYM(snes_get_region); SYM(snes_load_cartridge_normal); SYM(snes_set_controller_port_device); SYM(snes_serialize_size); @@ -113,6 +116,7 @@ static void set_statics(void) SSYM(snes_library_revision_minor); SSYM(snes_library_revision_major); SSYM(snes_run); + SSYM(snes_get_region); SSYM(snes_load_cartridge_normal); SSYM(snes_set_controller_port_device); SSYM(snes_serialize_size); diff --git a/dynamic.h b/dynamic.h index feb99c249e..a1fe282316 100644 --- a/dynamic.h +++ b/dynamic.h @@ -37,6 +37,8 @@ extern unsigned (*psnes_library_revision_major)(void); extern bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); extern void (*psnes_set_controller_port_device)(bool, unsigned); +extern bool (*psnes_get_region)(void); + extern unsigned (*psnes_serialize_size)(void); extern bool (*psnes_serialize)(uint8_t*, unsigned); extern bool (*psnes_unserialize)(const uint8_t*, unsigned); diff --git a/ssnes.c b/ssnes.c index 6113b77eb4..161a977b78 100644 --- a/ssnes.c +++ b/ssnes.c @@ -514,7 +514,7 @@ int main(int argc, char *argv[]) .channels = 2, .samplerate = 32040, .filename = g_extern.record_path, - .fps = snes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps, + .fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps, .aspect_ratio = 4.0/3 }; SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path); From dda574581777cddf7b4914dbad1386c72f81cf3a Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 18:22:47 +0100 Subject: [PATCH 36/50] Shouldn't poll an extra time since it messed up mouse handling. --- ssnes.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/ssnes.c b/ssnes.c index 161a977b78..94999743e3 100644 --- a/ssnes.c +++ b/ssnes.c @@ -530,9 +530,6 @@ int main(int argc, char *argv[]) // Main loop for(;;) { - // Poll input. - driver.input->poll(driver.input_data); - // Time to drop? if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) || !driver.video->alive(driver.video_data)) From d9f75d3a6a546be4156cf8dac549ba14af2f220d Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 19:23:21 +0100 Subject: [PATCH 37/50] Add message when shader init fails. --- gfx/gl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gfx/gl.c b/gfx/gl.c index fd77397af9..aa1679e5e4 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -318,6 +318,13 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i // Remove that ugly mouse :D SDL_ShowCursor(SDL_DISABLE); + if (!gl_shader_init()) + { + SSNES_ERR("Shader init failed.\n"); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + return NULL; + } + gl_t *gl = calloc(1, sizeof(gl_t)); if (!gl) return NULL; @@ -371,7 +378,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i gl->last_width = gl->tex_w; gl->last_height = gl->tex_h; - gl_shader_init(); // Hook up SDL input driver to get SDL_QUIT events and RESIZE. sdl_input_t *sdl_input = input_sdl.init(); From d30ce0e86752753206d760568585f80b1624c32e Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 21:16:57 +0100 Subject: [PATCH 38/50] Add accelerated visual sdl attribute --- gfx/gl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index aa1679e5e4..0da8603efc 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -302,6 +302,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); if (!SDL_SetVideoMode(video->width, video->height, 32, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) return NULL; @@ -315,20 +316,22 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i if (attr <= 0) SSNES_WARN("GL double buffer has not been enabled!\n"); + gl_t *gl = calloc(1, sizeof(gl_t)); + if (!gl) + return NULL; + // Remove that ugly mouse :D SDL_ShowCursor(SDL_DISABLE); + set_viewport(gl); if (!gl_shader_init()) { SSNES_ERR("Shader init failed.\n"); SDL_QuitSubSystem(SDL_INIT_VIDEO); + free(gl); return NULL; } - gl_t *gl = calloc(1, sizeof(gl_t)); - if (!gl) - return NULL; - gl->win_width = video->width; gl->win_height = video->height; gl->vsync = video->vsync; @@ -339,7 +342,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i else gl->tex_filter = GL_NEAREST; - set_viewport(gl); glEnable(GL_TEXTURE_2D); glDisable(GL_DITHER); From 7b5969cb558c40139961b5cc105081223e12b694 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 22:13:55 +0100 Subject: [PATCH 39/50] some common error routine, and more stringent error checking. --- gfx/gl.c | 2 ++ gfx/gl_common.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++ gfx/shader_glsl.c | 26 ++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 gfx/gl_common.h diff --git a/gfx/gl.c b/gfx/gl.c index 0da8603efc..01212a2294 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -49,6 +49,8 @@ #include "shader_glsl.h" #endif +#include "gl_common.h" + static const GLfloat vertexes[] = { 0, 0, 0, 0, 1, 0, diff --git a/gfx/gl_common.h b/gfx/gl_common.h new file mode 100644 index 0000000000..ac315ea10c --- /dev/null +++ b/gfx/gl_common.h @@ -0,0 +1,59 @@ +/* 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 . + */ + +#ifndef __GL_COMMON_H +#define __GL_COMMON_H + +#include "general.h" + +static inline bool gl_check_error(void) +{ + int error = glGetError(); + switch (error) + { + case GL_INVALID_ENUM: + SSNES_ERR("GL: Invalid enum.\n"); + break; + case GL_INVALID_VALUE: + SSNES_ERR("GL: Invalid value.\n"); + break; + case GL_INVALID_OPERATION: + SSNES_ERR("GL: Invalid operation.\n"); + break; + case GL_STACK_OVERFLOW: + SSNES_ERR("GL: Stack overflow. (wtf)\n"); + break; + case GL_STACK_UNDERFLOW: + SSNES_ERR("GL: Stack underflow. (:v)\n"); + break; + case GL_OUT_OF_MEMORY: + SSNES_ERR("GL: Out of memory. Harhar.\n"); + break; + case GL_TABLE_TOO_LARGE: + SSNES_ERR("GL: Table too large. Big tables scare you! :(\n"); + break; + case GL_NO_ERROR: + return true; + break; + default: + SSNES_ERR("Non specified error :v\n"); + } + + return false; +} + +#endif diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index a9e93a47e6..45510a0ff8 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -34,6 +34,8 @@ #define GL_GLEXT_PROTOTYPES #include +#include "gl_common.h" + static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL; static PFNGLUSEPROGRAMPROC pglUseProgram = NULL; static PFNGLCREATESHADERPROC pglCreateShader = NULL; @@ -49,6 +51,8 @@ static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL; static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL; static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL; static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog = NULL; +static PFNGLGETPROGRAMIVPROC pglGetProgramiv = NULL; +static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog = NULL; static bool glsl_enable = false; static GLuint gl_program; @@ -154,6 +158,20 @@ static void print_shader_log(GLuint obj) SSNES_LOG("Shader log: %s\n", info_log); } +static void print_linker_log(GLuint obj) +{ + int info_len = 0; + int max_len; + + pglGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len); + + char info_log[max_len]; + pglGetProgramInfoLog(obj, max_len, &info_len, info_log); + + if (info_len > 0) + SSNES_LOG("Linker log: %s\n", info_log); +} + bool gl_glsl_init(const char *path) { // Load shader functions. @@ -172,13 +190,15 @@ bool gl_glsl_init(const char *path) pglUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); pglGetShaderiv = SDL_GL_GetProcAddress("glGetShaderiv"); pglGetShaderInfoLog = SDL_GL_GetProcAddress("glGetShaderInfoLog"); + pglGetProgramiv = SDL_GL_GetProcAddress("glGetProgramiv"); + pglGetProgramInfoLog = SDL_GL_GetProcAddress("glGetProgramInfoLog"); SSNES_LOG("Checking GLSL shader support ...\n"); bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader && pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader && pglDetachShader && pglLinkProgram && pglGetUniformLocation && pglUniform1i && pglUniform2fv && pglUniform4fv - && pglGetShaderiv && pglGetShaderInfoLog; + && pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog; if (!shader_support) { @@ -218,8 +238,12 @@ bool gl_glsl_init(const char *path) { pglLinkProgram(gl_program); pglUseProgram(gl_program); + print_linker_log(gl_program); } + if (!gl_check_error()) + return false; + glsl_enable = true; return true; } From 90a5059b5bcaa0cb63dc857da91a65b4889ad25e Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 11 Jan 2011 22:33:28 +0100 Subject: [PATCH 40/50] More error checking... --- gfx/gl.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 01212a2294..add71c252c 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -306,9 +306,9 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - if (!SDL_SetVideoMode(video->width, video->height, 32, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) + if (!SDL_SetVideoMode(video->width, video->height, 16, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) return NULL; - + int attr = 0; SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr); if (attr <= 0 && video->vsync) @@ -318,13 +318,13 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i if (attr <= 0) SSNES_WARN("GL double buffer has not been enabled!\n"); + gl_t *gl = calloc(1, sizeof(gl_t)); if (!gl) return NULL; // Remove that ugly mouse :D SDL_ShowCursor(SDL_DISABLE); - set_viewport(gl); if (!gl_shader_init()) { @@ -337,6 +337,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i gl->win_width = video->width; gl->win_height = video->height; gl->vsync = video->vsync; + set_viewport(gl); keep_aspect = video->force_aspect; if ( video->smooth ) @@ -397,6 +398,13 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i else *input = NULL; + if (!gl_check_error()) + { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + free(gl); + return NULL; + } + return gl; } From e6aa065781f82be41c5832d22d526e4cd0edb7a9 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 18:05:57 +0100 Subject: [PATCH 41/50] Add some more abstractions for file loading. Also add initial SGB rom loading! --- dynamic.c | 6 +++ dynamic.h | 4 ++ file.c | 104 +++++++++++++++++++++++++++++++++++++++++ file.h | 3 ++ general.h | 11 +++++ ssnes.c | 137 ++++++++++++++++++++++++++++++------------------------ 6 files changed, 203 insertions(+), 62 deletions(-) diff --git a/dynamic.c b/dynamic.c index 7769390b78..598b1c8ac2 100644 --- a/dynamic.c +++ b/dynamic.c @@ -52,6 +52,10 @@ unsigned (*psnes_library_revision_minor)(void); unsigned (*psnes_library_revision_major)(void); bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); +bool (*psnes_load_cartridge_super_game_boy)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); + void (*psnes_set_controller_port_device)(bool, unsigned); bool (*psnes_get_region)(void); @@ -89,6 +93,7 @@ static void load_dynamic(void) SYM(snes_run); SYM(snes_get_region); SYM(snes_load_cartridge_normal); + SYM(snes_load_cartridge_super_game_boy); SYM(snes_set_controller_port_device); SYM(snes_serialize_size); SYM(snes_serialize); @@ -118,6 +123,7 @@ static void set_statics(void) SSYM(snes_run); SSYM(snes_get_region); SSYM(snes_load_cartridge_normal); + SSYM(snes_load_cartridge_super_game_boy); SSYM(snes_set_controller_port_device); SSYM(snes_serialize_size); SSYM(snes_serialize); diff --git a/dynamic.h b/dynamic.h index a1fe282316..7a51eff183 100644 --- a/dynamic.h +++ b/dynamic.h @@ -35,6 +35,10 @@ extern unsigned (*psnes_library_revision_minor)(void); extern unsigned (*psnes_library_revision_major)(void); extern bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); +extern bool (*psnes_load_cartridge_super_game_boy)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); + extern void (*psnes_set_controller_port_device)(bool, unsigned); extern bool (*psnes_get_region)(void); diff --git a/file.c b/file.c index 262eb4983e..0f4b684026 100644 --- a/file.c +++ b/file.c @@ -165,3 +165,107 @@ void save_file(const char* path, int type) if ( data && size > 0 ) write_file(path, data, size); } + +static bool load_sgb_rom(void) +{ + void *rom_buf; + ssize_t rom_len = 0; + + FILE *extra_rom; + void *extra_rom_buf; + ssize_t extra_rom_len = 0; + + if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) + { + SSNES_ERR("Could not read ROM file.\n"); + goto error; + } + + extra_rom = fopen(g_extern.gb_rom_path, "rb"); + if (!extra_rom) + { + SSNES_ERR("Couldn't open GameBoy ROM!\n"); + goto error; + } + + if ((extra_rom_len = read_file(extra_rom, &extra_rom_buf)) == -1) + { + SSNES_ERR("Cannot read GameBoy rom.\n"); + goto error; + } + + if (!psnes_load_cartridge_super_game_boy( + NULL, rom_buf, rom_len, + NULL, extra_rom_buf, extra_rom_len)) + { + SSNES_ERR("Cannot load SGB/GameBoy rom.\n"); + goto error; + } + + if (g_extern.rom_file) + fclose(g_extern.rom_file); + if (extra_rom) + fclose(extra_rom); + free(rom_buf); + free(extra_rom_buf); + return true; + +error: + if (g_extern.rom_file) + fclose(g_extern.rom_file); + if (extra_rom) + fclose(extra_rom); + free(rom_buf); + free(extra_rom_buf); + return false; +} + +static bool load_normal_rom(void) +{ + void *rom_buf; + ssize_t rom_len = 0; + + if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) + { + SSNES_ERR("Could not read ROM file.\n"); + return false; + } + + if (g_extern.rom_file != NULL) + fclose(g_extern.rom_file); + + SSNES_LOG("ROM size: %d bytes\n", (int)rom_len); + + if (!psnes_load_cartridge_normal(NULL, rom_buf, rom_len)) + { + SSNES_ERR("ROM file is not valid!\n"); + free(rom_buf); + return false; + } + + free(rom_buf); + return true; +} + +bool init_rom_file(void) +{ + switch (g_extern.game_type) + { + case SSNES_CART_SGB: + if (!load_sgb_rom()) + return false; + break; + + case SSNES_CART_NORMAL: + if (!load_normal_rom()) + return false; + break; + + default: + SSNES_ERR("Invalid ROM type!\n"); + return false; + } + + return true; +} + diff --git a/file.h b/file.h index 5d4add33a1..aa6b78cf5c 100644 --- a/file.h +++ b/file.h @@ -19,6 +19,7 @@ #ifndef __SSNES_FILE_H #define __SSNES_FILE_H +#include #include #include #include @@ -31,4 +32,6 @@ void write_file(const char* path, uint8_t* data, size_t size); void load_save_file(const char* path, int type); void save_file(const char* path, int type); +bool init_rom_file(void); + #endif diff --git a/general.h b/general.h index c95fb0f495..a40996159b 100644 --- a/general.h +++ b/general.h @@ -78,6 +78,12 @@ struct settings char libsnes[256]; }; +enum ssnes_game_type +{ + SSNES_CART_NORMAL = 0, + SSNES_CART_SGB +}; + struct global { bool verbose; @@ -92,10 +98,15 @@ struct global bool has_multitap; FILE *rom_file; + enum ssnes_game_type game_type; + + char gb_rom_path[256]; + char config_path[256]; char basename[256]; char savefile_name_srm[256]; + char savefile_name_rtc[260]; // Make sure that fill_pathname has space. char savestate_name[256]; #ifdef HAVE_FFMPEG diff --git a/ssnes.c b/ssnes.c index 94999743e3..54b5069640 100644 --- a/ssnes.c +++ b/ssnes.c @@ -36,6 +36,7 @@ struct global g_extern = { .video_active = true, .audio_active = true, + .game_type = SSNES_CART_NORMAL, }; // To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then. @@ -237,11 +238,12 @@ static void print_help(void) puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -k/--justifiers | -t/--savestate | -m/--mouse | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); + puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -k/--justifiers | -t/--savestate | -m/--mouse | -g/--gameboy | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); + puts("\t-g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom."); puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); puts("\t\tThis argument can be specified several times to connect more mice."); puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); @@ -270,6 +272,7 @@ static void parse_input(int argc, char *argv[]) { "record", 1, NULL, 'r' }, #endif { "verbose", 0, NULL, 'v' }, + { "gameboy", 1, NULL, 'g' }, { "config", 0, NULL, 'c' }, { "mouse", 1, NULL, 'm' }, { "scope", 0, NULL, 'p' }, @@ -288,7 +291,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:m:p4jk" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:t:m:p4jkg:" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -319,6 +322,11 @@ static void parse_input(int argc, char *argv[]) strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm) - 1); break; + case 'g': + strncpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path) - 1); + g_extern.game_type = SSNES_CART_SGB; + break; + case 't': strncpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name) - 1); break; @@ -443,63 +451,21 @@ static void init_controllers(void) } } -int main(int argc, char *argv[]) +static inline void load_save_files(void) { - parse_input(argc, argv); - parse_config(); - init_dlsym(); - - psnes_init(); - if (strlen(g_extern.basename) > 0) - psnes_set_cartridge_basename(g_extern.basename); - - SSNES_LOG("Version of libsnes API: %u.%u\n", psnes_library_revision_major(), psnes_library_revision_minor()); - void *rom_buf; - ssize_t rom_len = 0; - if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) - { - SSNES_ERR("Could not read ROM file.\n"); - exit(1); - } - SSNES_LOG("ROM size: %d bytes\n", (int)rom_len); - - if (g_extern.rom_file != NULL) - fclose(g_extern.rom_file); - - // Infer .rtc save path from save ram path. - char savefile_name_rtc[strlen(g_extern.savefile_name_srm)+strlen(".rtc")+1]; - fill_pathname(savefile_name_rtc, g_extern.savefile_name_srm, ".rtc"); - - init_drivers(); - - psnes_set_video_refresh(video_frame); - psnes_set_audio_sample(audio_sample); - psnes_set_input_poll(input_poll); - psnes_set_input_state(input_state); - - // TODO: Load other types of ROMs as well! - if (!psnes_load_cartridge_normal(NULL, rom_buf, rom_len)) - { - SSNES_ERR("ROM file is not valid!\n"); - goto error; - } - - free(rom_buf); - - init_controllers(); - - unsigned serial_size = psnes_serialize_size(); - uint8_t *serial_data = malloc(serial_size); - if (serial_data == NULL) - { - SSNES_ERR("Failed to allocate memory for states!\n"); - goto error; - } - load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); - load_save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + load_save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); +} + +static inline void save_files(void) +{ + save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); + save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); +} #ifdef HAVE_FFMPEG +static void init_recording(void) +{ // Hardcode these options at the moment. Should be specificed in the config file later on. if (g_extern.recording) { @@ -525,6 +491,58 @@ int main(int argc, char *argv[]) g_extern.recording = false; } } +} + +static void deinit_recording(void) +{ + if (g_extern.recording) + { + ffemu_finalize(g_extern.rec); + ffemu_free(g_extern.rec); + } +} +#endif + + +int main(int argc, char *argv[]) +{ + parse_input(argc, argv); + parse_config(); + init_dlsym(); + + psnes_init(); + if (strlen(g_extern.basename) > 0) + psnes_set_cartridge_basename(g_extern.basename); + + SSNES_LOG("Version of libsnes API: %u.%u\n", psnes_library_revision_major(), psnes_library_revision_minor()); + + // Infer .rtc save path from save ram path. + fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc"); + + if (!init_rom_file()) + goto error; + + init_drivers(); + + psnes_set_video_refresh(video_frame); + psnes_set_audio_sample(audio_sample); + psnes_set_input_poll(input_poll); + psnes_set_input_state(input_state); + + init_controllers(); + + unsigned serial_size = psnes_serialize_size(); + uint8_t *serial_data = malloc(serial_size); + if (serial_data == NULL) + { + SSNES_ERR("Failed to allocate memory for states!\n"); + goto error; + } + + load_save_files(); + +#ifdef HAVE_FFMPEG + init_recording(); #endif // Main loop @@ -556,16 +574,11 @@ int main(int argc, char *argv[]) } #ifdef HAVE_FFMPEG - if (g_extern.recording) - { - ffemu_finalize(g_extern.rec); - ffemu_free(g_extern.rec); - } + deinit_recording(); #endif // Flush out SRAM (and RTC) - save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); - save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + save_files(); psnes_unload_cartridge(); psnes_term(); From 2fb3f89f184340800af45ba370d2780d2313a41f Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 18:07:31 +0100 Subject: [PATCH 42/50] Add more sensible flags for some options. --- ssnes.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ssnes.c b/ssnes.c index 54b5069640..7803171128 100644 --- a/ssnes.c +++ b/ssnes.c @@ -241,14 +241,14 @@ static void print_help(void) puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -k/--justifiers | -t/--savestate | -m/--mouse | -g/--gameboy | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); - puts("\t-t/--savestate: Path to use for save states. If not selected, *.state will be assumed."); + puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); puts("\t-g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom."); puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); puts("\t\tThis argument can be specified several times to connect more mice."); puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES."); - puts("\t-k/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES."); + puts("\t-J/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES."); puts("\t-4/--multitap: Connect a multitap to port 2 of the SNES."); #ifdef HAVE_FFMPEG @@ -276,9 +276,9 @@ static void parse_input(int argc, char *argv[]) { "config", 0, NULL, 'c' }, { "mouse", 1, NULL, 'm' }, { "scope", 0, NULL, 'p' }, - { "savestate", 1, NULL, 't' }, + { "savestate", 1, NULL, 'S' }, { "justifier", 0, NULL, 'j' }, - { "justifiers", 0, NULL, 'k' }, + { "justifiers", 0, NULL, 'J' }, { "multitap", 0, NULL, '4' }, { NULL, 0, NULL, 0 } }; @@ -314,7 +314,7 @@ static void parse_input(int argc, char *argv[]) g_extern.has_justifier = true; break; - case 'k': + case 'J': g_extern.has_justifiers = true; break; @@ -327,7 +327,7 @@ static void parse_input(int argc, char *argv[]) g_extern.game_type = SSNES_CART_SGB; break; - case 't': + case 'S': strncpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name) - 1); break; From a73ab097dd100312c2a853971820985321037b0b Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 19:09:24 +0100 Subject: [PATCH 43/50] Add support for Gameboy SRAM and RTC. --- ssnes.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/ssnes.c b/ssnes.c index 7803171128..f9b3ef6680 100644 --- a/ssnes.c +++ b/ssnes.c @@ -451,16 +451,46 @@ static void init_controllers(void) } } +static int get_sram_type(enum ssnes_game_type type) +{ + switch (type) + { + case SSNES_CART_SGB: + return SNES_MEMORY_GAME_BOY_RAM; + case SSNES_CART_NORMAL: + return SNES_MEMORY_CARTRIDGE_RAM; + } + return 0; +} + +static int get_rtc_type(enum ssnes_game_type type) +{ + switch (type) + { + case SSNES_CART_SGB: + return SNES_MEMORY_GAME_BOY_RTC; + case SSNES_CART_NORMAL: + return SNES_MEMORY_CARTRIDGE_RTC; + } + return 0; +} + static inline void load_save_files(void) { - load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); - load_save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + int ram_type = get_sram_type(g_extern.game_type); + int rtc_type = get_rtc_type(g_extern.game_type); + + load_save_file(g_extern.savefile_name_srm, ram_type); + load_save_file(g_extern.savefile_name_rtc, rtc_type); } static inline void save_files(void) { - save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); - save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + int ram_type = get_sram_type(g_extern.game_type); + int rtc_type = get_rtc_type(g_extern.game_type); + + save_file(g_extern.savefile_name_srm, ram_type); + save_file(g_extern.savefile_name_rtc, rtc_type); } #ifdef HAVE_FFMPEG From c36608b6db5c2ad1c0820d3ae4d9c2822b3545ac Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 19:24:20 +0100 Subject: [PATCH 44/50] Update print_help() and add dynamics for rest of rom loading. --- dynamic.c | 16 ++++++++++++++++ dynamic.h | 11 +++++++++++ ssnes.c | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dynamic.c b/dynamic.c index 598b1c8ac2..44e02a1e97 100644 --- a/dynamic.c +++ b/dynamic.c @@ -55,6 +55,16 @@ bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned); bool (*psnes_load_cartridge_super_game_boy)( const char*, const uint8_t*, unsigned, const char*, const uint8_t*, unsigned); +bool (*psnes_load_cartridge_bsx)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); +bool (*psnes_load_cartridge_bsx_slotted)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); +bool (*psnes_load_cartridge_sufami_turbo)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); void (*psnes_set_controller_port_device)(bool, unsigned); @@ -94,6 +104,9 @@ static void load_dynamic(void) SYM(snes_get_region); SYM(snes_load_cartridge_normal); SYM(snes_load_cartridge_super_game_boy); + SYM(snes_load_cartridge_bsx); + SYM(snes_load_cartridge_bsx_slotted); + SYM(snes_load_cartridge_sufami_turbo); SYM(snes_set_controller_port_device); SYM(snes_serialize_size); SYM(snes_serialize); @@ -124,6 +137,9 @@ static void set_statics(void) SSYM(snes_get_region); SSYM(snes_load_cartridge_normal); SSYM(snes_load_cartridge_super_game_boy); + SSYM(snes_load_cartridge_bsx); + SSYM(snes_load_cartridge_bsx_slotted); + SSYM(snes_load_cartridge_sufami_turbo); SSYM(snes_set_controller_port_device); SSYM(snes_serialize_size); SSYM(snes_serialize); diff --git a/dynamic.h b/dynamic.h index 7a51eff183..8fcaa3ddce 100644 --- a/dynamic.h +++ b/dynamic.h @@ -38,6 +38,17 @@ extern bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned extern bool (*psnes_load_cartridge_super_game_boy)( const char*, const uint8_t*, unsigned, const char*, const uint8_t*, unsigned); +extern bool (*psnes_load_cartridge_bsx)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); +extern bool (*psnes_load_cartridge_bsx_slotted)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); +extern bool (*psnes_load_cartridge_sufami_turbo)( + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned, + const char*, const uint8_t*, unsigned); + extern void (*psnes_set_controller_port_device)(bool, unsigned); diff --git a/ssnes.c b/ssnes.c index f9b3ef6680..93e3e50f8d 100644 --- a/ssnes.c +++ b/ssnes.c @@ -238,7 +238,7 @@ static void print_help(void) puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -k/--justifiers | -t/--savestate | -m/--mouse | -g/--gameboy | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); + puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -J/--justifiers | -S/--savestate | -m/--mouse | -g/--gameboy | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed."); From f9893b6c1b95473173e42f1f8233e1da6f0ddad7 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 19:28:49 +0100 Subject: [PATCH 45/50] Do not process frame when CPU filter is not activated. --- hqflt/filters.h | 1 + settings.c | 1 + ssnes.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hqflt/filters.h b/hqflt/filters.h index 0e73b294f2..d8b766b584 100644 --- a/hqflt/filters.h +++ b/hqflt/filters.h @@ -30,6 +30,7 @@ #include "bleed.h" #include "ntsc.h" +#define FILTER_NONE 0 #define FILTER_HQ2X 1 #define FILTER_HQ4X 2 #define FILTER_GRAYSCALE 3 diff --git a/settings.c b/settings.c index e9a36e88be..e44f751225 100644 --- a/settings.c +++ b/settings.c @@ -96,6 +96,7 @@ static void set_defaults(void) g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; g_settings.video.aspect_ratio = SNES_ASPECT_RATIO; + g_settings.video.filter = FILTER_NONE; g_settings.audio.enable = audio_enable; g_settings.audio.out_rate = out_rate; diff --git a/ssnes.c b/ssnes.c index 93e3e50f8d..6a3a539d3c 100644 --- a/ssnes.c +++ b/ssnes.c @@ -103,7 +103,9 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) #ifdef HAVE_FILTER uint16_t output_filter[width * height * 4 * 4]; uint16_t output[width * height]; - process_frame(output, data, width, height); + + if (g_settings.video.filter != FILTER_NONE) + process_frame(output, data, width, height); switch (g_settings.video.filter) { From 907c371a926b294838dd19fcb44787613caefb74 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 12 Jan 2011 21:57:55 +0100 Subject: [PATCH 46/50] Add sufami... Now it should be pretty complete. :) --- file.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- file.h | 3 +- general.h | 12 +++- ssnes.c | 135 ++++++++++++++++++++++++++++++++------------ 4 files changed, 270 insertions(+), 46 deletions(-) diff --git a/file.c b/file.c index 0f4b684026..bf06e89d6f 100644 --- a/file.c +++ b/file.c @@ -168,11 +168,11 @@ void save_file(const char* path, int type) static bool load_sgb_rom(void) { - void *rom_buf; + void *rom_buf = NULL; ssize_t rom_len = 0; - FILE *extra_rom; - void *extra_rom_buf; + FILE *extra_rom = NULL; + void *extra_rom_buf = NULL; ssize_t extra_rom_len = 0; if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) @@ -220,9 +220,147 @@ error: return false; } +static bool load_bsx_rom(bool slotted) +{ + void *rom_buf = NULL; + ssize_t rom_len = 0; + + FILE *extra_rom = NULL; + void *extra_rom_buf = NULL; + ssize_t extra_rom_len = 0; + + if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) + { + SSNES_ERR("Could not read ROM file.\n"); + goto error; + } + + extra_rom = fopen(g_extern.bsx_rom_path, "rb"); + if (!extra_rom) + { + SSNES_ERR("Couldn't open BSX game rom!\n"); + goto error; + } + + if ((extra_rom_len = read_file(extra_rom, &extra_rom_buf)) == -1) + { + SSNES_ERR("Cannot read BSX game rom.\n"); + goto error; + } + + if (slotted) + { + if (!psnes_load_cartridge_bsx_slotted( + NULL, rom_buf, rom_len, + NULL, extra_rom_buf, extra_rom_len)) + { + SSNES_ERR("Cannot load BSX slotted rom.\n"); + goto error; + } + + } + else + { + if (!psnes_load_cartridge_bsx( + NULL, rom_buf, rom_len, + NULL, extra_rom_buf, extra_rom_len)) + { + SSNES_ERR("Cannot load BSX rom.\n"); + goto error; + } + } + + if (g_extern.rom_file) + fclose(g_extern.rom_file); + if (extra_rom) + fclose(extra_rom); + free(rom_buf); + free(extra_rom_buf); + return true; + +error: + if (g_extern.rom_file) + fclose(g_extern.rom_file); + if (extra_rom) + fclose(extra_rom); + free(rom_buf); + free(extra_rom_buf); + return false; +} + +static bool load_sufami_rom(void) +{ + void *rom_buf = NULL; + ssize_t rom_len = 0; + + FILE *extra_rom[2] = {NULL}; + void *extra_rom_buf[2] = {NULL}; + ssize_t extra_rom_len[2] = {0}; + + if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) + { + SSNES_ERR("Could not read ROM file.\n"); + goto error; + } + + const char *roms[2] = { g_extern.sufami_rom_path[0], g_extern.sufami_rom_path[1] }; + + for (int i = 0; i < 2; i++) + { + if (strlen(roms[i]) > 0) + { + extra_rom[i] = fopen(roms[i], "rb"); + if (!extra_rom[i]) + { + SSNES_ERR("Couldn't open BSX game rom!\n"); + goto error; + } + + if ((extra_rom_len[i] = read_file(extra_rom[i], &extra_rom_buf[i])) == -1) + { + SSNES_ERR("Cannot read BSX game rom.\n"); + goto error; + } + } + } + + if (!psnes_load_cartridge_sufami_turbo( + NULL, rom_buf, rom_len, + NULL, extra_rom_buf[0], extra_rom_len[0], + NULL, extra_rom_buf[1], extra_rom_len[1])) + { + SSNES_ERR("Cannot load Sufami Turbo rom.\n"); + goto error; + } + + + if (g_extern.rom_file) + fclose(g_extern.rom_file); + for (int i = 0; i < 2; i++) + { + if (extra_rom[i]) + fclose(extra_rom[i]); + free(extra_rom_buf[i]); + } + free(rom_buf); + return true; + +error: + if (g_extern.rom_file) + fclose(g_extern.rom_file); + for (int i = 0; i < 2; i++) + { + if (extra_rom[i]) + fclose(extra_rom[i]); + free(extra_rom_buf[i]); + } + free(rom_buf); + return false; +} + static bool load_normal_rom(void) { - void *rom_buf; + void *rom_buf = NULL; ssize_t rom_len = 0; if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1) @@ -247,9 +385,10 @@ static bool load_normal_rom(void) return true; } -bool init_rom_file(void) + +bool init_rom_file(enum ssnes_game_type type) { - switch (g_extern.game_type) + switch (type) { case SSNES_CART_SGB: if (!load_sgb_rom()) @@ -260,6 +399,21 @@ bool init_rom_file(void) if (!load_normal_rom()) return false; break; + + case SSNES_CART_BSX: + if (!load_bsx_rom(false)) + return false; + break; + + case SSNES_CART_BSX_SLOTTED: + if (!load_bsx_rom(true)) + return false; + break; + + case SSNES_CART_SUFAMI: + if (!load_sufami_rom()) + return false; + break; default: SSNES_ERR("Invalid ROM type!\n"); diff --git a/file.h b/file.h index aa6b78cf5c..d55e1c377e 100644 --- a/file.h +++ b/file.h @@ -24,6 +24,7 @@ #include #include #include +#include "general.h" ssize_t read_file(FILE *file, void **buf); @@ -32,6 +33,6 @@ void write_file(const char* path, uint8_t* data, size_t size); void load_save_file(const char* path, int type); void save_file(const char* path, int type); -bool init_rom_file(void); +bool init_rom_file(enum ssnes_game_type type); #endif diff --git a/general.h b/general.h index a40996159b..e835948c1a 100644 --- a/general.h +++ b/general.h @@ -81,7 +81,10 @@ struct settings enum ssnes_game_type { SSNES_CART_NORMAL = 0, - SSNES_CART_SGB + SSNES_CART_SGB, + SSNES_CART_BSX, + SSNES_CART_BSX_SLOTTED, + SSNES_CART_SUFAMI, }; struct global @@ -101,12 +104,17 @@ struct global enum ssnes_game_type game_type; char gb_rom_path[256]; + char bsx_rom_path[256]; + char sufami_rom_path[2][256]; char config_path[256]; char basename[256]; char savefile_name_srm[256]; - char savefile_name_rtc[260]; // Make sure that fill_pathname has space. + char savefile_name_rtc[512]; // Make sure that fill_pathname has space. + char savefile_name_psrm[512]; + char savefile_name_asrm[512]; + char savefile_name_bsrm[512]; char savestate_name[256]; #ifdef HAVE_FFMPEG diff --git a/ssnes.c b/ssnes.c index 6a3a539d3c..979a789b6d 100644 --- a/ssnes.c +++ b/ssnes.c @@ -240,12 +240,16 @@ static void print_help(void) puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -J/--justifiers | -S/--savestate | -m/--mouse | -g/--gameboy | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); + puts("Usage: ssnes [rom file] [-h/--help | -c/--config | -v/--verbose | -4/--multitap | -j/--justifier | -J/--justifiers | -S/--savestate | -m/--mouse | -g/--gameboy | -b/--bsx | -B/--bsxslot | --sufamiA | --sufamiB | -p/--scope | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed."); puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); puts("\t-g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom."); + puts("\t-b/--bsx: Path to BSX rom. Load BSX BIOS as the regular rom."); + puts("\t-B/--bsxslot: Path to BSX slotted rom. Load BSX BIOS as the regular rom."); + puts("\t--sufamiA: Path to A slot of Sufami Turbo. Load Sufami base cart as regular rom."); + puts("\t--sufamiB: Path to B slot of Sufami Turbo."); puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2)."); puts("\t\tThis argument can be specified several times to connect more mice."); puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES."); @@ -279,9 +283,13 @@ static void parse_input(int argc, char *argv[]) { "mouse", 1, NULL, 'm' }, { "scope", 0, NULL, 'p' }, { "savestate", 1, NULL, 'S' }, + { "bsx", 1, NULL, 'b' }, + { "bsxslot", 1, NULL, 'B' }, { "justifier", 0, NULL, 'j' }, { "justifiers", 0, NULL, 'J' }, { "multitap", 0, NULL, '4' }, + { "sufamiA", 1, NULL, 'Y' }, + { "sufamiB", 1, NULL, 'Z' }, { NULL, 0, NULL, 0 } }; @@ -293,7 +301,7 @@ static void parse_input(int argc, char *argv[]) #define FFMPEG_RECORD_ARG #endif - char optstring[] = "hs:vc:t:m:p4jkg:" FFMPEG_RECORD_ARG; + char optstring[] = "hs:vc:S:m:p4jJg:b:B:Y:Z:" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -329,6 +337,26 @@ static void parse_input(int argc, char *argv[]) g_extern.game_type = SSNES_CART_SGB; break; + case 'b': + strncpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path) - 1); + g_extern.game_type = SSNES_CART_BSX; + break; + + case 'B': + strncpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path) - 1); + g_extern.game_type = SSNES_CART_BSX_SLOTTED; + break; + + case 'Y': + strncpy(g_extern.sufami_rom_path[0], optarg, sizeof(g_extern.sufami_rom_path[0]) - 1); + g_extern.game_type = SSNES_CART_SUFAMI; + break; + + case 'Z': + strncpy(g_extern.sufami_rom_path[1], optarg, sizeof(g_extern.sufami_rom_path[1]) - 1); + g_extern.game_type = SSNES_CART_SUFAMI; + break; + case 'S': strncpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name) - 1); break; @@ -453,48 +481,59 @@ static void init_controllers(void) } } -static int get_sram_type(enum ssnes_game_type type) -{ - switch (type) - { - case SSNES_CART_SGB: - return SNES_MEMORY_GAME_BOY_RAM; - case SSNES_CART_NORMAL: - return SNES_MEMORY_CARTRIDGE_RAM; - } - return 0; -} - -static int get_rtc_type(enum ssnes_game_type type) -{ - switch (type) - { - case SSNES_CART_SGB: - return SNES_MEMORY_GAME_BOY_RTC; - case SSNES_CART_NORMAL: - return SNES_MEMORY_CARTRIDGE_RTC; - } - return 0; -} - static inline void load_save_files(void) { - int ram_type = get_sram_type(g_extern.game_type); - int rtc_type = get_rtc_type(g_extern.game_type); + switch (g_extern.game_type) + { + case SSNES_CART_NORMAL: + case SSNES_CART_SGB: + load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); + load_save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + break; - load_save_file(g_extern.savefile_name_srm, ram_type); - load_save_file(g_extern.savefile_name_rtc, rtc_type); + case SSNES_CART_BSX: + case SSNES_CART_BSX_SLOTTED: + load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_BSX_RAM); + load_save_file(g_extern.savefile_name_psrm, SNES_MEMORY_BSX_PRAM); + break; + + case SSNES_CART_SUFAMI: + load_save_file(g_extern.savefile_name_asrm, SNES_MEMORY_SUFAMI_TURBO_A_RAM); + load_save_file(g_extern.savefile_name_bsrm, SNES_MEMORY_SUFAMI_TURBO_B_RAM); + break; + + default: + break; + } } static inline void save_files(void) { - int ram_type = get_sram_type(g_extern.game_type); - int rtc_type = get_rtc_type(g_extern.game_type); + switch (g_extern.game_type) + { + case SSNES_CART_NORMAL: + case SSNES_CART_SGB: + save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); + save_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + break; - save_file(g_extern.savefile_name_srm, ram_type); - save_file(g_extern.savefile_name_rtc, rtc_type); + case SSNES_CART_BSX: + case SSNES_CART_BSX_SLOTTED: + save_file(g_extern.savefile_name_srm, SNES_MEMORY_BSX_RAM); + save_file(g_extern.savefile_name_psrm, SNES_MEMORY_BSX_PRAM); + break; + + case SSNES_CART_SUFAMI: + save_file(g_extern.savefile_name_asrm, SNES_MEMORY_SUFAMI_TURBO_A_RAM); + save_file(g_extern.savefile_name_bsrm, SNES_MEMORY_SUFAMI_TURBO_B_RAM); + break; + + default: + break; + } } + #ifdef HAVE_FFMPEG static void init_recording(void) { @@ -535,6 +574,29 @@ static void deinit_recording(void) } #endif +static void fill_pathnames(void) +{ + switch (g_extern.game_type) + { + case SSNES_CART_BSX: + case SSNES_CART_BSX_SLOTTED: + // BSX PSRM + fill_pathname(g_extern.savefile_name_psrm, g_extern.savefile_name_srm, ".psrm"); + break; + + case SSNES_CART_SUFAMI: + // SUFAMI ARAM + fill_pathname(g_extern.savefile_name_asrm, g_extern.savefile_name_srm, ".asrm"); + // SUFAMI BRAM + fill_pathname(g_extern.savefile_name_bsrm, g_extern.savefile_name_srm, ".bsrm"); + break; + + default: + // Infer .rtc save path from save ram path. + fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc"); + } +} + int main(int argc, char *argv[]) { @@ -548,10 +610,9 @@ int main(int argc, char *argv[]) SSNES_LOG("Version of libsnes API: %u.%u\n", psnes_library_revision_major(), psnes_library_revision_minor()); - // Infer .rtc save path from save ram path. - fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc"); + fill_pathnames(); - if (!init_rom_file()) + if (!init_rom_file(g_extern.game_type)) goto error; init_drivers(); From d82d8ac2a47565cf4132ca9befc0f79b5b840a33 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 13 Jan 2011 20:18:27 +0100 Subject: [PATCH 47/50] Use desktop bpp. --- gfx/gl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/gl.c b/gfx/gl.c index add71c252c..902f8a6369 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -306,7 +306,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - if (!SDL_SetVideoMode(video->width, video->height, 16, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) + if (!SDL_SetVideoMode(video->width, video->height, 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) return NULL; int attr = 0; From becfeb57eb0092f5170df1ceb2c95b4a2f17c6cc Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 13 Jan 2011 21:25:52 +0100 Subject: [PATCH 48/50] Oh dear... I hate this bugs that plague you for days, but are so stupid. --- gfx/gl.c | 24 +++++++++++------------- gfx/shader_glsl.c | 4 ++++ hqflt/cg/crt.cg | 3 ++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 902f8a6369..7b3bf25a00 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -66,7 +66,6 @@ static const GLfloat tex_coords[] = { }; static bool keep_aspect = true; -static GLuint gl_width = 0, gl_height = 0; typedef struct gl { bool vsync; @@ -220,19 +219,19 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i if (gl->should_resize) { gl->should_resize = false; - SDL_SetVideoMode(gl->win_width, gl->win_height, 32, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); + SDL_SetVideoMode(gl->win_width, gl->win_height, 0, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); set_viewport(gl); } glClear(GL_COLOR_BUFFER_BIT); - gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl_width, gl_height); + gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height); if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture. { gl->last_width = width; gl->last_height = height; - glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w); uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t)); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gl->tex_w, gl->tex_h, GL_BGRA, @@ -323,8 +322,10 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i if (!gl) return NULL; - // Remove that ugly mouse :D - SDL_ShowCursor(SDL_DISABLE); + gl->win_width = video->width; + gl->win_height = video->height; + gl->vsync = video->vsync; + set_viewport(gl); if (!gl_shader_init()) { @@ -334,10 +335,8 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i return NULL; } - gl->win_width = video->width; - gl->win_height = video->height; - gl->vsync = video->vsync; - set_viewport(gl); + // Remove that ugly mouse :D + SDL_ShowCursor(SDL_DISABLE); keep_aspect = video->force_aspect; if ( video->smooth ) @@ -345,7 +344,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i else gl->tex_filter = GL_NEAREST; - glEnable(GL_TEXTURE_2D); glDisable(GL_DITHER); glDisable(GL_DEPTH_TEST); @@ -359,6 +357,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i glGenTextures(1, &gl->texture); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gl->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -383,7 +382,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i gl->last_width = gl->tex_w; gl->last_height = gl->tex_h; - // Hook up SDL input driver to get SDL_QUIT events and RESIZE. sdl_input_t *sdl_input = input_sdl.init(); if (sdl_input) @@ -397,7 +395,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i } else *input = NULL; - + if (!gl_check_error()) { SDL_QuitSubSystem(SDL_INIT_VIDEO); diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 45510a0ff8..5a454c9815 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -239,6 +239,9 @@ bool gl_glsl_init(const char *path) pglLinkProgram(gl_program); pglUseProgram(gl_program); print_linker_log(gl_program); + + GLint location = pglGetUniformLocation(gl_program, "rubyTexture"); + pglUniform1i(location, 0); } if (!gl_check_error()) @@ -270,6 +273,7 @@ void gl_glsl_set_params(unsigned width, unsigned height, float textureSize[2] = {tex_width, tex_height}; location = pglGetUniformLocation(gl_program, "rubyTextureSize"); pglUniform2fv(location, 1, textureSize); + } } diff --git a/hqflt/cg/crt.cg b/hqflt/cg/crt.cg index bee618ad38..bb62d1da05 100644 --- a/hqflt/cg/crt.cg +++ b/hqflt/cg/crt.cg @@ -91,7 +91,8 @@ output main_fragment(float2 texCoord : TEXCOORD0, uniform sampler2D decal : TEXU else mcol.rb = 0.7; - OUT.color = pow(mcol*(col * weights + col2 * weights2), 1.0/2.2); + //OUT.color = pow(mcol*(col * weights + col2 * weights2), 1.0/2.2); + OUT.color = 1.0; return OUT; } From 6b21e6b8a3ca0d6e1b295df13c67a54bd14b5bf4 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 14 Jan 2011 15:34:38 +0100 Subject: [PATCH 49/50] Clean up audio a bit. Less use of nasty statics and VLAs. --- audio/jack.c | 32 ++++++-------------------------- driver.c | 26 ++++++++++++++++++++++++++ driver.h | 1 + general.h | 12 ++++++++++++ ssnes.c | 52 +++++++++++++++++++++++++++------------------------- 5 files changed, 72 insertions(+), 51 deletions(-) diff --git a/audio/jack.c b/audio/jack.c index 01ae1c9ef6..d4fb440cb5 100644 --- a/audio/jack.c +++ b/audio/jack.c @@ -29,8 +29,7 @@ #include #include -#define FRAMES(x) (x / (sizeof(int16_t) * 2)) -#define SAMPLES(x) (x / sizeof(int16_t)) +#define FRAMES(x) (x / (sizeof(float) * 2)) typedef struct jack { @@ -61,13 +60,6 @@ static int process_cb(jack_nframes_t nframes, void *data) if (min_avail > nframes) min_avail = nframes; - //static int underrun = 0; - //if (min_avail < nframes) - //{ - // SSNES_LOG("JACK: Underrun count: %d\n", underrun++); - // fprintf(stderr, "required %d frames, got %d.\n", (int)nframes, (int)min_avail); - //} - for (int i = 0; i < 2; i++) { jack_default_audio_sample_t *out = jack_port_get_buffer(jd->ports[i], nframes); @@ -90,12 +82,6 @@ static void shutdown_cb(void *data) pthread_cond_signal(&jd->cond); } -static inline void s16_to_float(jack_default_audio_sample_t * restrict out, const int16_t * restrict in, size_t samples) -{ - for (int i = 0; i < samples; i++) - out[i] = (float)in[i] / 0x8000; -} - static void parse_ports(const char **dest_ports, const char **jports) { int parsed = 0; @@ -142,7 +128,7 @@ static void* __jack_init(const char* device, int rate, int latency) bufsize = (latency * g_settings.audio.out_rate / 1000) > jack_bufsize * 2 ? (latency * g_settings.audio.out_rate / 1000) : jack_bufsize * 2; bufsize *= sizeof(jack_default_audio_sample_t); - //fprintf(stderr, "jack buffer size: %d\n", (int)bufsize); + SSNES_LOG("Jack buffer size: %d bytes: (~%.2f msec latency)\n", (int)bufsize, (float)bufsize * 1000 / (g_settings.audio.out_rate * sizeof(jack_default_audio_sample_t))); for (int i = 0; i < 2; i++) { jd->buffer[i] = jack_ringbuffer_create(bufsize); @@ -181,7 +167,6 @@ static void* __jack_init(const char* device, int rate, int latency) pthread_cond_init(&jd->cond, NULL); pthread_mutex_init(&jd->cond_lock, NULL); - jack_free(jports); return jd; @@ -191,17 +176,13 @@ error: return NULL; } -static size_t write_buffer(jack_t *jd, const void *buf, size_t size) +static size_t write_buffer(jack_t *jd, const float *buf, size_t size) { - //fprintf(stderr, "write_buffer: size: %zu\n", size); - // Convert our data to float, deinterleave and write. - jack_default_audio_sample_t out_buffer[size / sizeof(int16_t)]; jack_default_audio_sample_t out_deinterleaved_buffer[2][FRAMES(size)]; - s16_to_float(out_buffer, buf, SAMPLES(size)); for (int i = 0; i < 2; i++) for (size_t j = 0; j < FRAMES(size); j++) - out_deinterleaved_buffer[i][j] = out_buffer[j * 2 + i]; + out_deinterleaved_buffer[i][j] = buf[j * 2 + i]; for(;;) { @@ -216,13 +197,11 @@ static size_t write_buffer(jack_t *jd, const void *buf, size_t size) if (jd->nonblock) { if (min_avail < FRAMES(size) * sizeof(jack_default_audio_sample_t)) - size = min_avail * 2 * sizeof(int16_t) / sizeof(jack_default_audio_sample_t); + size = min_avail * 2; break; } - else { - //fprintf(stderr, "Write avail is: %d\n", (int)min_avail); if (min_avail >= FRAMES(size) * sizeof(jack_default_audio_sample_t)) break; } @@ -290,6 +269,7 @@ const audio_driver_t audio_jack = { .start = __jack_start, .set_nonblock_state = __jack_set_nonblock_state, .free = __jack_free, + .float_samples = true, .ident = "jack" }; diff --git a/driver.c b/driver.c index 67ce93c661..4e78e6439b 100644 --- a/driver.c +++ b/driver.c @@ -21,6 +21,7 @@ #include #include #include "hqflt/filters.h" +#include #ifdef HAVE_CONFIG_H #include "config.h" @@ -128,6 +129,9 @@ void uninit_drivers(void) uninit_audio(); } +#define AUDIO_CHUNK_SIZE_BLOCKING 64 +#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. +#define AUDIO_MAX_RATIO 16 void init_audio(void) { if (!g_settings.audio.enable) @@ -138,17 +142,35 @@ void init_audio(void) find_audio_driver(); + g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; + g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING; + driver.audio_data = driver.audio->init(strlen(g_settings.audio.device) ? g_settings.audio.device : NULL, g_settings.audio.out_rate, g_settings.audio.latency); if ( driver.audio_data == NULL ) g_extern.audio_active = false; + if (!g_settings.audio.sync && g_extern.audio_active) + { driver.audio->set_nonblock_state(driver.audio_data, true); + g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size; + } + else + g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size; int err; g_extern.source = src_new(g_settings.audio.src_quality, 2, &err); if (!g_extern.source) g_extern.audio_active = false; + + size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ? + g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size; + + assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO); + assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float)))); + g_extern.audio_data.data_ptr = 0; + assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO))); + assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO))); } void uninit_audio(void) @@ -164,6 +186,10 @@ void uninit_audio(void) if ( g_extern.source ) src_delete(g_extern.source); + + free(g_extern.audio_data.data); g_extern.audio_data.data = NULL; + free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL; + free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL; } void init_video_input(void) diff --git a/driver.h b/driver.h index 73ae142c48..7b8303b1be 100644 --- a/driver.h +++ b/driver.h @@ -61,6 +61,7 @@ typedef struct audio_driver bool (*start)(void* data); void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding. void (*free)(void* data); + bool float_samples; // Defines if driver will take standard floating point samples, or int16_t samples. const char *ident; } audio_driver_t; diff --git a/general.h b/general.h index e835948c1a..174257b3f5 100644 --- a/general.h +++ b/general.h @@ -117,6 +117,18 @@ struct global char savefile_name_bsrm[512]; char savestate_name[256]; + struct + { + float *data; + size_t data_ptr; + size_t chunk_size; + size_t nonblock_chunk_size; + size_t block_chunk_size; + + float *outsamples; + int16_t *conv_outsamples; + } audio_data; + #ifdef HAVE_FFMPEG ffemu_t *rec; char record_path[256]; diff --git a/ssnes.c b/ssnes.c index 979a789b6d..ab6201ceb3 100644 --- a/ssnes.c +++ b/ssnes.c @@ -40,10 +40,6 @@ struct global g_extern = { }; // To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then. - -#define AUDIO_CHUNK_SIZE_BLOCKING 64 -#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. -static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; static void set_fast_forward_button(bool new_button_state) { static bool old_button_state = false; @@ -55,10 +51,11 @@ static void set_fast_forward_button(bool new_button_state) driver.video->set_nonblock_state(driver.video_data, syncing_state); if (g_extern.audio_active) driver.audio->set_nonblock_state(driver.audio_data, (g_settings.audio.sync) ? syncing_state : true); + if (syncing_state) - audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING; + g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size; else - audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING; + g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size; } old_button_state = new_button_state; } @@ -163,37 +160,42 @@ static void audio_sample(uint16_t left, uint16_t right) } #endif - static float data[AUDIO_CHUNK_SIZE_NONBLOCKING]; - static int data_ptr = 0; + g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&left)/0x8000; + g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&right)/0x8000; - data[data_ptr++] = (float)(*(int16_t*)&left)/0x8000; - data[data_ptr++] = (float)(*(int16_t*)&right)/0x8000; - - if ( data_ptr >= audio_chunk_size ) + if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size) { - float outsamples[audio_chunk_size * 16]; - int16_t temp_outsamples[audio_chunk_size * 16]; - SRC_DATA src_data; - src_data.data_in = data; - src_data.data_out = outsamples; - src_data.input_frames = audio_chunk_size / 2; - src_data.output_frames = audio_chunk_size * 8; + src_data.data_in = g_extern.audio_data.data; + src_data.data_out = g_extern.audio_data.outsamples; + src_data.input_frames = g_extern.audio_data.chunk_size / 2; + src_data.output_frames = g_extern.audio_data.chunk_size * 8; src_data.end_of_input = 0; src_data.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate; src_process(g_extern.source, &src_data); - src_float_to_short_array(outsamples, temp_outsamples, src_data.output_frames_gen * 2); - - if ( driver.audio->write(driver.audio_data, temp_outsamples, src_data.output_frames_gen * 4) < 0 ) + if (driver.audio->float_samples) { - fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); - g_extern.audio_active = false; + if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0) + { + fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); + g_extern.audio_active = false; + } + } + else + { + src_float_to_short_array(g_extern.audio_data.outsamples, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * 2); + + if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0) + { + fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); + g_extern.audio_active = false; + } } - data_ptr = 0; + g_extern.audio_data.data_ptr = 0; } } From eceac3106021381ff5f821d5948a551d57a3c289 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 14 Jan 2011 16:32:12 +0100 Subject: [PATCH 50/50] Some rework in Jack. Better buffer handling and should reach lower latencies more stable. --- audio/jack.c | 93 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/audio/jack.c b/audio/jack.c index d4fb440cb5..4e1edae821 100644 --- a/audio/jack.c +++ b/audio/jack.c @@ -82,19 +82,38 @@ static void shutdown_cb(void *data) pthread_cond_signal(&jd->cond); } -static void parse_ports(const char **dest_ports, const char **jports) +static int parse_ports(char **dest_ports, const char **jports) { int parsed = 0; const char *con = strtok(g_settings.audio.device, ","); if (con) - dest_ports[parsed++] = con; + dest_ports[parsed++] = strdup(con); con = strtok(NULL, ","); if (con) - dest_ports[parsed++] = con; + dest_ports[parsed++] = strdup(con); for (int i = parsed; i < 2; i++) - dest_ports[i] = jports[i]; + dest_ports[i] = strdup(jports[i]); + + return 2; +} + +static size_t find_buffersize(jack_t *jd, int latency) +{ + int frames = latency * g_settings.audio.out_rate / 1000; + + int jack_latency = jack_port_get_total_latency(jd->client, jd->ports[0]); + SSNES_LOG("JACK: Jack latency is %d frames.\n", jack_latency); + + int buffer_frames = frames - jack_latency; + int min_buffer_frames = jack_get_buffer_size(jd->client) * 2; + SSNES_LOG("JACK: Minimum buffer size is %d frames.\n", min_buffer_frames); + + if (buffer_frames < min_buffer_frames) + buffer_frames = min_buffer_frames; + + return buffer_frames * sizeof(jack_default_audio_sample_t); } static void* __jack_init(const char* device, int rate, int latency) @@ -122,24 +141,8 @@ static void* __jack_init(const char* device, int rate, int latency) goto error; } - jack_nframes_t bufsize; - jack_nframes_t jack_bufsize = jack_get_buffer_size(jd->client); - bufsize = (latency * g_settings.audio.out_rate / 1000) > jack_bufsize * 2 ? (latency * g_settings.audio.out_rate / 1000) : jack_bufsize * 2; - bufsize *= sizeof(jack_default_audio_sample_t); - - SSNES_LOG("Jack buffer size: %d bytes: (~%.2f msec latency)\n", (int)bufsize, (float)bufsize * 1000 / (g_settings.audio.out_rate * sizeof(jack_default_audio_sample_t))); - for (int i = 0; i < 2; i++) - { - jd->buffer[i] = jack_ringbuffer_create(bufsize); - if (jd->buffer[i] == NULL) - { - SSNES_ERR("Failed to create buffers.\n"); - goto error; - } - } - - const char *dest_ports[2]; + char *dest_ports[2]; jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if (jports == NULL) { @@ -147,7 +150,7 @@ static void* __jack_init(const char* device, int rate, int latency) goto error; } - parse_ports(dest_ports, jports); + int parsed = parse_ports(dest_ports, jports); if (jack_activate(jd->client) < 0) { @@ -164,6 +167,22 @@ static void* __jack_init(const char* device, int rate, int latency) } } + for (int i = 0; i < parsed; i++) + free(dest_ports[i]); + + size_t bufsize = find_buffersize(jd, latency); + + SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t))); + for (int i = 0; i < 2; i++) + { + jd->buffer[i] = jack_ringbuffer_create(bufsize); + if (jd->buffer[i] == NULL) + { + SSNES_ERR("Failed to create buffers.\n"); + goto error; + } + } + pthread_cond_init(&jd->cond, NULL); pthread_mutex_init(&jd->cond_lock, NULL); @@ -184,7 +203,10 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size) for (size_t j = 0; j < FRAMES(size); j++) out_deinterleaved_buffer[i][j] = buf[j * 2 + i]; - for(;;) + size_t frames = FRAMES(size); + + size_t written = 0; + while (written < frames) { if (jd->shutdown) return 0; @@ -193,27 +215,28 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size) avail[0] = jack_ringbuffer_write_space(jd->buffer[0]); avail[1] = jack_ringbuffer_write_space(jd->buffer[1]); size_t min_avail = avail[0] < avail[1] ? avail[0] : avail[1]; + min_avail /= sizeof(float); - if (jd->nonblock) + size_t write_frames = frames - written > min_avail ? min_avail : frames - written; + + if (write_frames > 0) { - if (min_avail < FRAMES(size) * sizeof(jack_default_audio_sample_t)) - size = min_avail * 2; - break; + for (int i = 0; i < 2; i++) + jack_ringbuffer_write(jd->buffer[i], (const char*)&out_deinterleaved_buffer[i][written], write_frames * sizeof(jack_default_audio_sample_t)); + written += write_frames; } else { - if (min_avail >= FRAMES(size) * sizeof(jack_default_audio_sample_t)) - break; + pthread_mutex_lock(&jd->cond_lock); + pthread_cond_wait(&jd->cond, &jd->cond_lock); + pthread_mutex_unlock(&jd->cond_lock); } - pthread_mutex_lock(&jd->cond_lock); - pthread_cond_wait(&jd->cond, &jd->cond_lock); - pthread_mutex_unlock(&jd->cond_lock); + if (jd->nonblock) + break; } - for (int i = 0; i < 2; i++) - jack_ringbuffer_write(jd->buffer[i], (const char*)out_deinterleaved_buffer[i], FRAMES(size) * sizeof(jack_default_audio_sample_t)); - return size; + return written * sizeof(float) * 2; } static ssize_t __jack_write(void* data, const void* buf, size_t size)