# HG changeset patch # User Michael Pavone # Date 1653408654 25200 # Node ID bdd83b47d78af722e75f89108724e4c6988551cb # Parent da789044596208b19cf9a9142e49c258ff81f2b6 Implement config file migrations and add iso and cue to extension list diff -r da7890445962 -r bdd83b47d78a config.c --- 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; diff -r da7890445962 -r bdd83b47d78a default.cfg --- 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 diff -r da7890445962 -r bdd83b47d78a menu.c --- 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; } } diff -r da7890445962 -r bdd83b47d78a util.c --- 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; diff -r da7890445962 -r bdd83b47d78a util.h --- 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