From 17663bbe468bb1363e137a2fd256818ee1607ce4 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 23 Jan 2011 23:09:54 +0100 Subject: [PATCH] Add save state slots. --- config.def.h | 2 + driver.h | 2 + file.c | 19 +++++--- file.h | 4 +- general.h | 4 +- gfx/gl.c | 25 +++++----- settings.c | 12 ++++- ssnes.c | 129 ++++++++++++++++++++++++++++++++++++++------------- ssnes.cfg | 7 ++- 9 files changed, 150 insertions(+), 54 deletions(-) diff --git a/config.def.h b/config.def.h index 5625875e53..d0aa845668 100644 --- a/config.def.h +++ b/config.def.h @@ -169,6 +169,8 @@ static const struct snes_keybind snes_keybinds_1[] = { { 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 }, + { SSNES_STATE_SLOT_MINUS, SDLK_F6, NO_BTN, AXIS_NONE }, + { SSNES_STATE_SLOT_PLUS, SDLK_F7, NO_BTN, AXIS_NONE }, { -1 } }; diff --git a/driver.h b/driver.h index da6bd9e814..dd92eb8a52 100644 --- a/driver.h +++ b/driver.h @@ -32,6 +32,8 @@ enum SSNES_SAVE_STATE_KEY, SSNES_FULLSCREEN_TOGGLE_KEY, SSNES_QUIT_KEY, + SSNES_STATE_SLOT_PLUS, + SSNES_STATE_SLOT_MINUS, }; struct snes_keybind diff --git a/file.c b/file.c index 1405d3f02b..f435dbaa3d 100644 --- a/file.c +++ b/file.c @@ -135,47 +135,53 @@ error: } // Dump stuff to file. -static void dump_to_file(const char *path, const void *data, size_t size) +static bool dump_to_file(const char *path, const void *data, size_t size) { FILE *file = fopen(path, "wb"); if (!file) { SSNES_ERR("Couldn't dump to file %s\n", path); + return false; } else { fwrite(data, 1, size, file); fclose(file); + return true; } } -void save_state(const char* path) +bool save_state(const char* path) { SSNES_LOG("Saving state: \"%s\".\n", path); size_t size = psnes_serialize_size(); if (size == 0) - return; + return false; void *data = malloc(size); if (!data) { SSNES_ERR("Failed to allocate memory for save state buffer.\n"); - return; + return false; } SSNES_LOG("State size: %d bytes.\n", (int)size); psnes_serialize(data, size); - dump_to_file(path, data, size); + bool ret = dump_to_file(path, data, size); free(data); + return ret; } -void load_state(const char* path) +bool load_state(const char* path) { SSNES_LOG("Loading state: \"%s\".\n", path); void *buf = NULL; ssize_t size = read_file(path, &buf); if (size < 0) + { SSNES_ERR("Failed to load state.\n"); + return false; + } else { SSNES_LOG("State size: %d bytes.\n", (int)size); @@ -183,6 +189,7 @@ void load_state(const char* path) } free(buf); + return true; } void load_ram_file(const char* path, int type) diff --git a/file.h b/file.h index 859b462094..df24d0da15 100644 --- a/file.h +++ b/file.h @@ -26,8 +26,8 @@ #include #include "general.h" -void load_state(const char* path); -void save_state(const char* path); +bool load_state(const char* path); +bool save_state(const char* path); void load_ram_file(const char* path, int type); void save_ram_file(const char* path, int type); diff --git a/general.h b/general.h index 37f001c30c..e2c9424f30 100644 --- a/general.h +++ b/general.h @@ -35,7 +35,7 @@ #define MAX_PLAYERS 5 -#define MAX_BINDS 18 // Needs to be increased every time there are new binds added. +#define MAX_BINDS 20 // Needs to be increased every time there are new binds added. #define SSNES_NO_JOYPAD 0xFFFF struct settings { @@ -126,6 +126,8 @@ struct global char savefile_name_bsrm[512]; char savestate_name[256]; + unsigned state_slot; + struct { float *data; diff --git a/gfx/gl.c b/gfx/gl.c index d3bcc03914..ae9189d300 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -176,19 +176,22 @@ static inline void gl_shader_set_params(unsigned width, unsigned height, static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_size) { #ifdef HAVE_FREETYPE - gl->font = font_renderer_new(font_path, font_size); - if (gl->font) + if (strlen(font_path) > 0) { - glGenTextures(1, &gl->font_tex); - glBindTexture(GL_TEXTURE_2D, gl->font_tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, gl->texture); + gl->font = font_renderer_new(font_path, font_size); + if (gl->font) + { + glGenTextures(1, &gl->font_tex); + glBindTexture(GL_TEXTURE_2D, gl->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, gl->texture); + } + else + SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path); } - else - SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path); #endif } diff --git a/settings.c b/settings.c index beb46b4c65..2ccdba3fbf 100644 --- a/settings.c +++ b/settings.c @@ -103,8 +103,6 @@ static void set_defaults(void) #endif #ifdef HAVE_FREETYPE - // Just grab one font path for now... :) - strncpy(g_settings.video.font_path, "/usr/share/fonts/TTF/DejaVuSans.ttf", sizeof(g_settings.video.font_path) - 1); g_settings.video.font_size = font_size; g_settings.video.msg_pos_x = message_pos_offset_x; g_settings.video.msg_pos_y = message_pos_offset_y; @@ -399,6 +397,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { 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(state_slot_increase, SSNES_STATE_SLOT_PLUS) + DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) }, @@ -418,6 +418,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { 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(state_slot_increase, SSNES_STATE_SLOT_PLUS) + DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) }, @@ -437,6 +439,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { 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(state_slot_increase, SSNES_STATE_SLOT_PLUS) + DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) }, @@ -456,6 +460,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { 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(state_slot_increase, SSNES_STATE_SLOT_PLUS) + DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) }, @@ -475,6 +481,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { 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(state_slot_increase, SSNES_STATE_SLOT_PLUS) + DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS) DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY) DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY) }, diff --git a/ssnes.c b/ssnes.c index d414dc209a..6a3161e6c9 100644 --- a/ssnes.c +++ b/ssnes.c @@ -626,6 +626,102 @@ static void fill_pathnames(void) } } +// Save or load state here. +static void check_savestates(void) +{ + static bool old_should_savestate = false; + bool should_savestate = driver.input->key_pressed(driver.input_data, SSNES_SAVE_STATE_KEY); + if (should_savestate && !old_should_savestate) + { + char save_path[strlen(g_extern.savestate_name) * 2]; + snprintf(save_path, sizeof(save_path), g_extern.state_slot > 0 ? "%s%u" : "%s", g_extern.savestate_name, g_extern.state_slot); + if(!save_state(save_path)) + { + msg_queue_clear(g_extern.msg_queue); + char msg[512]; + snprintf(msg, sizeof(msg), "Failed to save state to \"%s\"", save_path); + msg_queue_push(g_extern.msg_queue, msg, 2, 180); + } + else + { + msg_queue_clear(g_extern.msg_queue); + msg_queue_push(g_extern.msg_queue, "Saved state!", 1, 180); + } + } + old_should_savestate = should_savestate; + + static bool old_should_loadstate = false; + bool should_loadstate = driver.input->key_pressed(driver.input_data, SSNES_LOAD_STATE_KEY); + if (!should_savestate && should_loadstate && !old_should_loadstate) + { + char load_path[strlen(g_extern.savestate_name) * 2]; + snprintf(load_path, sizeof(load_path), g_extern.state_slot ? "%s%u" : "%s", g_extern.savestate_name, g_extern.state_slot); + + if(!load_state(load_path)) + { + msg_queue_clear(g_extern.msg_queue); + char msg[512]; + snprintf(msg, sizeof(msg), "Failed to load state from \"%s\"", load_path); + msg_queue_push(g_extern.msg_queue, msg, 2, 180); + } + else + { + msg_queue_clear(g_extern.msg_queue); + msg_queue_push(g_extern.msg_queue, "Loaded state!", 1, 180); + } + } + old_should_loadstate = should_loadstate; +} + +static void check_fullscreen(void) +{ + // If we go fullscreen we drop all drivers and reinit to be safe. + if (driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) + { + g_settings.video.fullscreen = !g_settings.video.fullscreen; + uninit_drivers(); + init_drivers(); + } +} + +static void check_stateslots(void) +{ + // Save state slots + static bool old_should_slot_increase = false; + bool should_slot_increase = driver.input->key_pressed(driver.input_data, SSNES_STATE_SLOT_PLUS); + if (should_slot_increase && !old_should_slot_increase) + { + g_extern.state_slot++; + msg_queue_clear(g_extern.msg_queue); + char msg[256]; + snprintf(msg, sizeof(msg), "Save state slot: %u", g_extern.state_slot); + msg_queue_push(g_extern.msg_queue, msg, 1, 180); + } + old_should_slot_increase = should_slot_increase; + + static bool old_should_slot_decrease = false; + bool should_slot_decrease = driver.input->key_pressed(driver.input_data, SSNES_STATE_SLOT_MINUS); + if (should_slot_decrease && !old_should_slot_decrease) + { + if (g_extern.state_slot > 0) + g_extern.state_slot--; + msg_queue_clear(g_extern.msg_queue); + char msg[256]; + snprintf(msg, sizeof(msg), "Save state slot: %u", g_extern.state_slot); + msg_queue_push(g_extern.msg_queue, msg, 1, 180); + } + old_should_slot_decrease = should_slot_decrease; +} + +static void do_state_checks(void) +{ + set_fast_forward_button(driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY)); + + check_stateslots(); + check_savestates(); + check_fullscreen(); +} + int main(int argc, char *argv[]) { @@ -669,38 +765,9 @@ int main(int argc, char *argv[]) !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. + // Checks for stuff like fullscreen, save states, etc. + do_state_checks(); - static bool old_should_savestate = false; - bool should_savestate = driver.input->key_pressed(driver.input_data, SSNES_SAVE_STATE_KEY); - if (should_savestate && !old_should_savestate) - { - msg_queue_clear(g_extern.msg_queue); - msg_queue_push(g_extern.msg_queue, "Saving state!", 1, 180); - save_state(g_extern.savestate_name); - } - old_should_savestate = should_savestate; - - static bool old_should_loadstate = false; - bool should_loadstate = driver.input->key_pressed(driver.input_data, SSNES_LOAD_STATE_KEY); - if (!should_savestate && should_loadstate && !old_should_loadstate) - { - msg_queue_clear(g_extern.msg_queue); - msg_queue_push(g_extern.msg_queue, "Loading state!", 1, 180); - load_state(g_extern.savestate_name); - } - old_should_loadstate = should_loadstate; - - // If we go fullscreen we drop all drivers and reinit to be safe. - if (driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) - { - g_settings.video.fullscreen = !g_settings.video.fullscreen; - uninit_drivers(); - init_drivers(); - } - // Run libsnes for one frame. psnes_run(); } diff --git a/ssnes.cfg b/ssnes.cfg index 399e570959..df29d8284a 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -37,7 +37,7 @@ # CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc. # video_filter = ntsc -# Path to a TTF font used for rendering messages. +# Path to a TTF font used for rendering messages. This path must be defined to enable fonts. # video_font_path = # Size of the TTF font rendered. @@ -166,6 +166,11 @@ # Loads state. # input_load_state = f4 +# State slots. With slot set to 0, save state name is *.state (or whatever defined on commandline). +# When slot is != 0, path will be $path%d, where %d is slot number. +# input_state_slot_increase = f7 +# input_state_slot_decrease = f6 + # Toggles between fast-forwarding and normal speed. # input_toggle_fast_forward = space