changeset 2158:bdd83b47d78a

Implement config file migrations and add iso and cue to extension list
author Michael Pavone <pavone@retrodev.com>
date Tue, 24 May 2022 09:10:54 -0700
parents da7890445962
children 2ed402b4c1fb
files config.c default.cfg menu.c util.c util.h
diffstat 5 files changed, 113 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/config.c	Thu Apr 28 18:44:50 2022 -0700
+++ b/config.c	Tue May 24 09:10:54 2022 -0700
@@ -250,6 +250,88 @@
 	return ret;
 }
 
+static tern_node *dupe_tree(tern_node *head)
+{
+	if (!head) {
+		return head;
+	}
+	tern_node *out = calloc(1, sizeof(tern_node));
+	out->left = dupe_tree(head->left);
+	out->right = dupe_tree(head->right);
+	out->el = head->el;
+	out->valtype = head->valtype;
+	if (out->el) {
+		out->straight.next = dupe_tree(head->straight.next);
+	} else if (out->valtype == TVAL_NODE) {
+		out->straight.value.ptrval = dupe_tree(head->straight.value.ptrval);
+	} else if (out->valtype == TVAL_PTR) {
+		out->straight.value.ptrval = strdup(head->straight.value.ptrval);
+	} else {
+		out->straight.value = head->straight.value;
+	}
+	return out;
+}
+
+static void migrate_pads(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	tern_node **pads = data;
+	if (valtype != TVAL_NODE) {
+		return;
+	}
+	tern_node *existing = tern_find_node(*pads, key);
+	if (existing) {
+		return;
+	}
+	*pads = tern_insert_node(*pads, key, dupe_tree(val.ptrval));
+}
+
+#define CONFIG_VERSION 1
+static tern_node *migrate_config(tern_node *config, int from_version)
+{
+	tern_node *def_config = parse_bundled_config("default.cfg");
+	switch(from_version)
+	{
+	case 0: {
+		//Add CD image formats to ui.extensions
+		uint32_t num_exts;
+		char **ext_list = get_extension_list(config, &num_exts);
+		char *old = num_exts ? ext_list[0] : NULL;
+		uint32_t new_size = num_exts + 2;
+		uint8_t need_cue = 1, need_iso = 1;
+		for (uint32_t i = 0; i < num_exts; i++)
+		{
+			if (!strcmp(ext_list[i], "cue")) {
+				need_cue = 0;
+				new_size--;
+			} else if (!strcmp(ext_list[i], "iso")) {
+				need_iso = 0;
+				new_size--;
+			}
+		}
+		if (new_size != num_exts) {
+			ext_list = realloc(ext_list, sizeof(char*) * new_size);
+			if (need_cue) {
+				ext_list[num_exts++] = "cue";
+			}
+			if (need_iso) {
+				ext_list[num_exts++] = "iso";
+			}
+		}
+		char *combined = alloc_join(new_size, (char const **)ext_list, ' ');
+		config = tern_insert_path(config, "ui\0extensions\0", (tern_val){.ptrval = combined}, TVAL_PTR);
+		//Copy default pad configs if missing
+		tern_node *pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
+		tern_node *def_pads = tern_find_path(def_config, "bindings\0pads\0", TVAL_NODE).ptrval;
+		tern_foreach(def_pads, migrate_pads, &pads);
+		config = tern_insert_path(config, "bindings\0pads\0", (tern_val){.ptrval = pads}, TVAL_NODE);
+		break;
+	}
+	}
+	char buffer[16];
+	sprintf(buffer, "%d", CONFIG_VERSION);
+	return tern_insert_ptr(config, "version", strdup(buffer));
+}
+
 static uint8_t app_config_in_config_dir;
 tern_node *load_config()
 {
@@ -262,6 +344,10 @@
 			fatal_error("Failed to find a config file in the BlastEm executable directory and the config directory path could not be determined\n");
 		}
 	}
+	int config_version = atoi(tern_find_ptr_default(ret, "version", "0"));
+	if (config_version < CONFIG_VERSION) {
+		migrate_config(ret, config_version);
+	}
 	return ret;
 }
 
@@ -315,7 +401,7 @@
 
 char **get_extension_list(tern_node *config, uint32_t *num_exts_out)
 {
-	char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg"}, TVAL_PTR).ptrval);
+	char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg cue iso"}, TVAL_PTR).ptrval);
 	uint32_t num_exts = 0, ext_storage = 5;
 	char **ext_list = malloc(sizeof(char *) * ext_storage);
 	char *cur_filter = ext_filter;
--- a/default.cfg	Thu Apr 28 18:44:50 2022 -0700
+++ b/default.cfg	Tue May 24 09:10:54 2022 -0700
@@ -1,4 +1,3 @@
-
 bindings {
 	keys {
 		up gamepads.1.up
@@ -378,7 +377,7 @@
 	#accepts special variables $HOME, $EXEDIR, $USERDATA, $ROMNAME
 	save_path $USERDATA/blastem/$ROMNAME
 	#space delimited list of file extensions to filter against in menu
-	extensions bin gen md smd sms gg zip gz
+	extensions bin gen md smd sms gg zip gz cue iso
 	#specifies the preferred save-state format, set to gst for Genecyst compatible states
 	state_format native
 }
@@ -398,5 +397,5 @@
 	#Model of the emulated Gen/MD system, see systems.cfg for a list of options
 	model md1va3
 }
-
-
+#Don't manually edit `version`, it's used for automatic config migration
+version 1
--- a/menu.c	Thu Apr 28 18:44:50 2022 -0700
+++ b/menu.c	Tue May 24 09:10:54 2022 -0700
@@ -150,7 +150,7 @@
 			for (size_t i = 0; dst && i < num_entries; i++)
 			{
 				if (num_exts && !entries[i].is_dir) {
-					if (!path_matches_extensions(entries[i].name, ext_list, num_exts)) {
+					if (!path_matches_extensions(entries[i].name, (const char **)ext_list, num_exts)) {
 						continue;
 					}
 				}
--- a/util.c	Thu Apr 28 18:44:50 2022 -0700
+++ b/util.c	Tue May 24 09:10:54 2022 -0700
@@ -56,6 +56,26 @@
 	return ret;
 }
 
+char * alloc_join(int num_parts, char const **parts, char sep)
+{
+	int total = num_parts ? num_parts - 1 : 0;
+	for (int i = 0; i < num_parts; i++) {
+		total += strlen(parts[i]);
+	}
+	char * ret = malloc(total + 1);
+	char *cur = ret;
+	for (int i = 0; i < num_parts; i++) {
+		size_t s = strlen(parts[i]);
+		if (i) {
+			*(cur++) = sep;
+		}
+		memcpy(cur, parts[i], s);
+		cur += s;
+	}
+	*cur = 0;
+	return ret;
+}
+
 typedef struct {
 	uint32_t start;
 	uint32_t end;
--- a/util.h	Thu Apr 28 18:44:50 2022 -0700
+++ b/util.h	Tue May 24 09:10:54 2022 -0700
@@ -22,6 +22,8 @@
 char * alloc_concat(char const * first, char const * second);
 //Allocates a new string containing the concatenation of the strings pointed to by parts
 char * alloc_concat_m(int num_parts, char const ** parts);
+//Allocates a new string containing the concatenation of the strings pointed to by parts separated by sep
+char * alloc_join(int num_parts, char const **parts, char sep);
 //Returns a newly allocated string in which all variables in based are replaced with values from vars or the environment
 char *replace_vars(char *base, tern_node *vars, uint8_t allow_env);
 //Byteswaps a ROM image in memory