changeset 955:229c23b3ab73

Switch to storing SRAM/EEPROM and save states in a per-game directory rather than next to the ROM (for SRAM/EEPROM) or in the current working directory (for save states)
author Michael Pavone <pavone@retrodev.com>
date Fri, 15 Apr 2016 18:29:39 -0700
parents cbc5b39e5518
children f5550cdffe49
files blastem.c romdb.h util.c util.h
diffstat 4 files changed, 102 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Tue Apr 12 22:50:31 2016 -0700
+++ b/blastem.c	Fri Apr 15 18:29:39 2016 -0700
@@ -192,6 +192,7 @@
 
 int break_on_sync = 0;
 int save_state = 0;
+char *save_state_path;
 
 //#define DO_DEBUG_PRINT
 #ifdef DO_DEBUG_PRINT
@@ -309,8 +310,8 @@
 			{
 				sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80);
 			}
-			save_gst(gen, "savestate.gst", address);
-			puts("Saved state to savestate.gst");
+			save_gst(gen, save_state_path, address);
+			printf("Saved state to %s\n", save_state_path);
 		} else if(save_state) {
 			context->sync_cycle = context->current_cycle + 1;
 		}
@@ -974,6 +975,25 @@
 	}
 }
 
+void setup_saves(char *fname, rom_info *info)
+{
+	char * barename = basename_no_extension(fname);
+	char const * parts[3] = {get_save_dir(), "/", barename};
+	char *save_dir = alloc_concat_m(3, parts);
+	if (!ensure_dir_exists(save_dir)) {
+		warning("Failed to create save directory %s\n", save_dir);
+	}
+	parts[0] = save_dir;
+	parts[2] = info->save_type == SAVE_I2C ? "save.eeprom" : "save.sram";
+	free(save_filename);
+	save_filename = alloc_concat_m(3, parts);
+	parts[2] = "quicksave.gst";
+	free(save_state_path);
+	save_state_path = alloc_concat_m(3, parts);
+	info->save_dir = save_dir;
+	free(barename);
+}
+
 int main(int argc, char ** argv)
 {
 	set_exe_str(argv[0]);
@@ -1150,21 +1170,7 @@
 	if (!headless) {
 		render_init(width, height, title, fps, fullscreen);
 	}
-	int fname_size = strlen(romfname);
-	char * ext = info.save_type == SAVE_I2C ? "eeprom" : "sram";
-	save_filename = malloc(fname_size+strlen(ext) + 2);
-	memcpy(save_filename, romfname, fname_size);
-	int i;
-	for (i = fname_size-1; fname_size >= 0; --i) {
-		if (save_filename[i] == '.') {
-			strcpy(save_filename + i + 1, ext);
-			break;
-		}
-	}
-	if (i < 0) {
-		save_filename[fname_size] = '.';
-		strcpy(save_filename + fname_size + 1, ext);
-	}
+	setup_saves(romfname, &info);
 
 	genesis = alloc_init_genesis(&info, fps, (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0);
 	if (menu) {
@@ -1189,7 +1195,7 @@
 					genesis = menu_context;
 				}
 				free(game_context->cart);
-				free(save_filename);
+				free(info.save_dir);
 				base_map[0].buffer = ram = game_context->work_ram;
 			} else {
 				base_map[0].buffer = ram = malloc(RAM_WORDS * sizeof(uint16_t));
@@ -1202,20 +1208,7 @@
 			byteswap_rom(rom_size);
 			set_region(&info, force_version);
 			update_title(info.name);
-			fname_size = strlen(menu_context->next_rom);
-			ext = info.save_type == SAVE_I2C ? "eeprom" : "sram";
-			save_filename = malloc(fname_size+strlen(ext) + 2);
-			memcpy(save_filename, menu_context->next_rom, fname_size);
-			for (i = fname_size-1; fname_size >= 0; --i) {
-				if (save_filename[i] == '.') {
-					strcpy(save_filename + i + 1, ext);
-					break;
-				}
-			}
-			if (i < 0) {
-				save_filename[fname_size] = '.';
-				strcpy(save_filename + fname_size + 1, ext);
-			}
+			setup_saves(menu_context->next_rom, &info);
 			if (!game_context) {
 				//start a new arena and save old one in suspended genesis context
 				genesis->arena = start_new_arena();
--- a/romdb.h	Tue Apr 12 22:50:31 2016 -0700
+++ b/romdb.h	Fri Apr 15 18:29:39 2016 -0700
@@ -37,6 +37,7 @@
 
 typedef struct {
 	char          *name;
+	char          *save_dir;
 	memmap_chunk  *map;
 	uint8_t       *save_buffer;
 	eeprom_map    *eeprom_map;
--- a/util.c	Tue Apr 12 22:50:31 2016 -0700
+++ b/util.c	Fri Apr 15 18:29:39 2016 -0700
@@ -8,6 +8,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <errno.h>
 
 #ifdef __ANDROID__
 #include <android/log.h>
@@ -93,6 +94,32 @@
 	return text+1;
 }
 
+char * basename_no_extension(char *path)
+{
+	char *lastdot = NULL;
+	char *lastslash = NULL;
+	char *cur;
+	for (cur = path; *cur; cur++)
+	{
+		if (*cur == '.') {
+			lastdot = cur;
+		} else if (*cur == '/') {
+			lastslash = cur + 1;
+		}
+	}
+	if (!lastdot) {
+		lastdot = cur;
+	}
+	if (!lastslash) {
+		lastslash = path;
+	}
+	char *barename = malloc(lastdot-lastslash+1);
+	memcpy(barename, lastslash, lastdot-lastslash);
+	barename[lastdot-lastslash] = 0;
+	
+	return barename;
+}
+
 uint32_t nearest_pow2(uint32_t val)
 {
 	uint32_t ret = 1;
@@ -339,6 +366,32 @@
 	free(list);
 }
 
+int ensure_dir_exists(char *path)
+{
+	struct stat st;
+	if (stat(path, &st)) {
+		if (errno == ENOENT) {
+			char *parent = strdup(path);
+			char *sep = strrchr(parent, '/');
+			if (sep && sep != parent) {
+				*sep = 0;
+				if (!ensure_dir_exists(parent)) {
+					free(parent);
+					return 0;
+				}
+				free(parent);
+			}
+			return mkdir(path, 0777) == 0;
+		} else {
+			char buf[80];
+			strerror_r(errno, buf, sizeof(buf));
+			warning("stat failed with error: %s", buf);
+			return 0;
+		}
+	}
+	return S_ISDIR(st.st_mode);
+}
+
 #endif
 
 #ifdef __ANDROID__
@@ -377,6 +430,11 @@
 	return SDL_AndroidGetInternalStoragePath();
 }
 
+char const *get_save_dir()
+{
+	return SDL_AndroidGetInternalStoragePath();
+}
+
 #else
 
 char *read_bundled_file(char *name, long *sizeret)
@@ -430,4 +488,16 @@
 	return confdir;
 }
 
+char const *get_save_dir()
+{
+	static char* savedir;
+	if (!savedir) {
+		char *homedir = get_home_dir();
+		if (homedir) {
+			savedir = alloc_concat(homedir, "/.local/share/blastem");
+		}
+	}
+	return savedir;
+}
+
 #endif
--- a/util.h	Tue Apr 12 22:50:31 2016 -0700
+++ b/util.h	Fri Apr 15 18:29:39 2016 -0700
@@ -20,6 +20,8 @@
 char * strip_ws(char * text);
 //Inserts a null after the first word, returns a pointer to the second word
 char * split_keyval(char * text);
+//Returns the basename of a path with th extension (if any) stripped
+char * basename_no_extension(char *path);
 //Gets the smallest power of two that is >= a certain value, won't work for values > 0x80000000
 uint32_t nearest_pow2(uint32_t val);
 //Should be called by main with the value of argv[0] for use by get_exe_dir
@@ -30,12 +32,16 @@
 char * get_home_dir();
 //Returns an appropriate path for storing config files
 char const *get_config_dir();
+//Returns an appropriate path for saving non-config data like savestates
+char const *get_save_dir();
 //Reads a file bundled with the executable
 char *read_bundled_file(char *name, long *sizeret);
 //Retunrs an array of normal files and directories residing in a directory
 dir_entry *get_dir_list(char *path, size_t *numret);
 //Frees a dir list returned by get_dir_list
 void free_dir_list(dir_entry *list, size_t numentries);
+//Recusrively creates a directory if it does not exist
+int ensure_dir_exists(char *path);
 //Returns the contents of a symlink in a newly allocated string
 char * readlink_alloc(char * path);
 //Prints an error message to stderr and to a message box if not in headless mode and then exits