X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Flibgame%2Fmisc.c;h=6432523e41ce5c5755a6e385c28abd30995b5aee;hp=dbc0b3072d24eda9f839f44221f980ed79b196ce;hb=1c9456dab9709209a2462afe72c65373a1294de5;hpb=cb528019c5229698c6e4af7998c34bc45cb6b124 diff --git a/src/libgame/misc.c b/src/libgame/misc.c index dbc0b307..6432523e 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -222,6 +222,18 @@ void Print(char *format, ...) va_end(ap); } +void PrintNoLog(char *format, ...) +{ + FILE *file = program.log_file_default[LOG_OUT_ID]; + va_list ap; + + va_start(ap, format); + vfprintf(file, format, ap); + va_end(ap); + + fflush(file); +} + void PrintLine(char *line_chars, int line_length) { int i; @@ -559,7 +571,7 @@ char *getLoginName() if (GetUserName(login_name, &buffer_size) == 0) strcpy(login_name, ANONYMOUS_NAME); } -#else +#elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID) if (login_name == NULL) { struct passwd *pwd; @@ -569,6 +581,8 @@ char *getLoginName() else login_name = getStringCopy(pwd->pw_name); } +#else + login_name = ANONYMOUS_NAME; #endif return login_name; @@ -668,42 +682,18 @@ char *getBasePath(char *filename) char *basepath = getStringCopy(filename); char *last_separator = getLastPathSeparatorPtr(basepath); - if (last_separator != NULL) - *last_separator = '\0'; /* separator found: strip basename */ - else - basepath = "."; /* no separator found: use current path */ - - return basepath; -} - -static char *getProgramMainDataPath() -{ - char *main_data_path = getStringCopy(program.command_basepath); - -#if defined(PLATFORM_MACOSX) - static char *main_data_binary_subdir = NULL; - - if (main_data_binary_subdir == NULL) + /* if no separator was found, use current directory */ + if (last_separator == NULL) { - main_data_binary_subdir = checked_malloc(strlen(program.program_title) + 1 + - strlen("app") + 1 + - strlen(MAC_APP_BINARY_SUBDIR) + 1); + free(basepath); - sprintf(main_data_binary_subdir, "%s.app/%s", - program.program_title, MAC_APP_BINARY_SUBDIR); + return getStringCopy("."); } - // cut relative path to Mac OS X application binary directory from path - if (strSuffix(main_data_path, main_data_binary_subdir)) - main_data_path[strlen(main_data_path) - - strlen(main_data_binary_subdir)] = '\0'; - - // cut trailing path separator from path (but not if path is root directory) - if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/")) - main_data_path[strlen(main_data_path) - 1] = '\0'; -#endif + /* separator found: strip basename */ + *last_separator = '\0'; - return main_data_path; + return basepath; } @@ -776,46 +766,27 @@ char *getPath3(char *path1, char *path2, char *path3) return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR); } -char *getImg2(char *path1, char *path2) +static char *getPngOrPcxIfNotExists(char *filename) { - char *filename = getPath2(path1, path2); + // switch from PNG to PCX file and vice versa, if file does not exist + // (backwards compatibility with PCX files used in previous versions) - if (!fileExists(filename) && strSuffix(path2, ".png")) - { - // backward compatibility: if PNG file not found, check for PCX file - char *path2pcx = getStringCopy(path2); - - strcpy(&path2pcx[strlen(path2pcx) - 3], "pcx"); - - free(filename); - - filename = getPath2(path1, path2pcx); - - free(path2pcx); - } + if (!fileExists(filename) && strSuffix(filename, ".png")) + strcpy(&filename[strlen(filename) - 3], "pcx"); + else if (!fileExists(filename) && strSuffix(filename, ".pcx")) + strcpy(&filename[strlen(filename) - 3], "png"); return filename; } -char *getImg3(char *path1, char *path2, char *path3) +char *getImg2(char *path1, char *path2) { - char *filename = getPath3(path1, path2, path3); - - if (!fileExists(filename) && strSuffix(path3, ".png")) - { - // backward compatibility: if PNG file not found, check for PCX file - char *path3pcx = getStringCopy(path3); - - strcpy(&path3pcx[strlen(path3pcx) - 3], "pcx"); - - free(filename); - - filename = getPath3(path1, path2, path3pcx); - - free(path3pcx); - } + return getPngOrPcxIfNotExists(getPath2(path1, path2)); +} - return filename; +char *getImg3(char *path1, char *path2, char *path3) +{ + return getPngOrPcxIfNotExists(getPath3(path1, path2, path3)); } char *getStringCopy(const char *s) @@ -938,8 +909,8 @@ void GetOptions(int argc, char *argv[], void (*print_usage_function)(void), void (*print_version_function)(void)) { - char *ro_base_path = RO_BASE_PATH; - char *rw_base_path = RW_BASE_PATH; + char *ro_base_path = getProgramMainDataPath(argv[0], RO_BASE_PATH); + char *rw_base_path = getProgramMainDataPath(argv[0], RW_BASE_PATH); char **argvplus = checked_calloc((argc + 1) * sizeof(char **)); char **options_left = &argvplus[1]; @@ -947,18 +918,7 @@ void GetOptions(int argc, char *argv[], while (argc--) argvplus[argc] = argv[argc]; - /* if the program is configured to start from current directory (default), - determine program package directory from program binary (some versions - of KDE/Konqueror and Mac OS X (especially "Mavericks") apparently do not - set the current working directory to the program package directory) */ - - if (strEqual(ro_base_path, ".")) - ro_base_path = getProgramMainDataPath(); - if (strEqual(rw_base_path, ".")) - rw_base_path = getProgramMainDataPath(); - /* initialize global program options */ - options.display_name = NULL; options.server_host = NULL; options.server_port = 0; @@ -969,10 +929,12 @@ void GetOptions(int argc, char *argv[], options.sounds_directory = getPath2(ro_base_path, SOUNDS_DIRECTORY); options.music_directory = getPath2(ro_base_path, MUSIC_DIRECTORY); options.docs_directory = getPath2(ro_base_path, DOCS_DIRECTORY); + options.conf_directory = getPath2(ro_base_path, CONF_DIRECTORY); options.execute_command = NULL; options.special_flags = NULL; + options.mytapes = FALSE; options.serveronly = FALSE; options.network = FALSE; options.verbose = FALSE; @@ -985,6 +947,12 @@ void GetOptions(int argc, char *argv[], if (*options_left == NULL) /* no options given -- enable verbose mode */ options.verbose = TRUE; #endif +#endif + +#if DEBUG +#if defined(PLATFORM_ANDROID) + options.debug = TRUE; +#endif #endif while (*options_left) @@ -1029,15 +997,6 @@ void GetOptions(int argc, char *argv[], exit(0); } - else if (strncmp(option, "-display", option_len) == 0) - { - if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); - - options.display_name = option_arg; - if (option_arg == next_option) - options_left++; - } else if (strncmp(option, "-basepath", option_len) == 0) { if (option_arg == NULL) @@ -1055,6 +1014,7 @@ void GetOptions(int argc, char *argv[], options.sounds_directory = getPath2(ro_base_path, SOUNDS_DIRECTORY); options.music_directory = getPath2(ro_base_path, MUSIC_DIRECTORY); options.docs_directory = getPath2(ro_base_path, DOCS_DIRECTORY); + options.conf_directory = getPath2(ro_base_path, CONF_DIRECTORY); } else if (strncmp(option, "-levels", option_len) == 0) { @@ -1092,6 +1052,10 @@ void GetOptions(int argc, char *argv[], if (option_arg == next_option) options_left++; } + else if (strncmp(option, "-mytapes", option_len) == 0) + { + options.mytapes = TRUE; + } else if (strncmp(option, "-network", option_len) == 0) { options.network = TRUE; @@ -1198,6 +1162,10 @@ void Error(int mode, char *format, ...) ANDROID_LOG_UNKNOWN); #endif + /* display debug messages only when running in debug mode */ + if (mode & ERR_DEBUG && !options.debug) + return; + /* display warnings only when running in verbose mode */ if (mode & ERR_WARN && !options.verbose) return; @@ -1474,28 +1442,28 @@ int putFileChunk(FILE *file, char *chunk_name, int chunk_size, int getFileVersion(File *file) { + int version_super = getByteFromFile(file); int version_major = getByteFromFile(file); int version_minor = getByteFromFile(file); int version_patch = getByteFromFile(file); - int version_build = getByteFromFile(file); - return VERSION_IDENT(version_major, version_minor, version_patch, - version_build); + return VERSION_IDENT(version_super, version_major, version_minor, + version_patch); } int putFileVersion(FILE *file, int version) { if (file != NULL) { + int version_super = VERSION_SUPER(version); int version_major = VERSION_MAJOR(version); int version_minor = VERSION_MINOR(version); int version_patch = VERSION_PATCH(version); - int version_build = VERSION_BUILD(version); + fputc(version_super, file); fputc(version_major, file); fputc(version_minor, file); fputc(version_patch, file); - fputc(version_build, file); } return 4; @@ -1599,8 +1567,14 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) { KSYM_Page_Down, "XK_Page_Down", "page down" }, #if defined(TARGET_SDL2) + { KSYM_Select, "XK_Select", "select" }, { KSYM_Menu, "XK_Menu", "menu" }, /* menu key */ { KSYM_Back, "XK_Back", "back" }, /* back key */ + { KSYM_PlayPause, "XK_PlayPause", "play/pause" }, +#if defined(PLATFORM_ANDROID) + { KSYM_Rewind, "XK_Rewind", "rewind" }, + { KSYM_FastForward, "XK_FastForward", "fast forward" }, +#endif #endif /* ASCII 0x20 to 0x40 keys (except numbers) */ @@ -2097,20 +2071,33 @@ void deleteNodeFromList(ListNode **node_first, char *key, if (strEqual((*node_first)->key, key)) { - checked_free((*node_first)->key); + // after first recursion, (*node_first)->prev->next == *node_first, + // so *node_first would be overwritten with (*node_first)->next + // => use a copy of *node_first (and later of (*node_first)->next) + ListNode *node = *node_first; + ListNode *node_next = node->next; + + checked_free(node->key); if (destructor_function) - destructor_function((*node_first)->content); + destructor_function(node->content); - if ((*node_first)->next) - (*node_first)->next->prev = (*node_first)->prev; + if (node->prev) + node->prev->next = node->next; - checked_free(*node_first); + if (node->next) + node->next->prev = node->prev; - *node_first = (*node_first)->next; + checked_free(node); + + // after removing node, set list pointer to next valid list node + // (this is important if the first node of the list was deleted) + *node_first = node_next; } else + { deleteNodeFromList(&(*node_first)->next, key, destructor_function); + } } ListNode *getNodeFromKey(ListNode *node_first, char *key) @@ -2148,6 +2135,8 @@ void dumpList(ListNode *node_first) /* functions for file handling */ /* ------------------------------------------------------------------------- */ +#define MAX_BUFFER_SIZE 4096 + File *openFile(char *filename, char *mode) { File *file = checked_calloc(sizeof(File)); @@ -2230,6 +2219,11 @@ size_t readFile(File *file, void *buffer, size_t item_size, size_t num_items) return fread(buffer, item_size, num_items, file->file); } +size_t writeFile(File *file, void *buffer, size_t item_size, size_t num_items) +{ + return fwrite(buffer, item_size, num_items, file->file); +} + int seekFile(File *file, long offset, int whence) { #if defined(PLATFORM_ANDROID) @@ -2299,6 +2293,36 @@ char *getStringFromFile(File *file, char *line, int size) return fgets(line, size, file->file); } +int copyFile(char *filename_from, char *filename_to) +{ + File *file_from, *file_to; + + if ((file_from = openFile(filename_from, MODE_READ)) == NULL) + { + return -1; + } + + if ((file_to = openFile(filename_to, MODE_WRITE)) == NULL) + { + closeFile(file_from); + + return -1; + } + + while (!checkEndOfFile(file_from)) + { + byte buffer[MAX_BUFFER_SIZE]; + size_t bytes_read = readFile(file_from, buffer, 1, MAX_BUFFER_SIZE); + + writeFile(file_to, buffer, 1, bytes_read); + } + + closeFile(file_from); + closeFile(file_to); + + return 0; +} + /* ------------------------------------------------------------------------- */ /* functions for directory handling */ @@ -2593,9 +2617,91 @@ char *get_mapped_token(char *token) return getStringCat2(map_token_prefix[i][1], &token[len_token_prefix]); } + // change tokens containing ".gfx" by moving the "gfx" part to the very left + char *gfx_substring = ".gfx"; + char *gfx_prefix = "gfx."; + if (strstr(token, gfx_substring) != NULL) + { + char *token_prefix = getStringCopy(token); + char *token_gfx_pos = strstr(token_prefix, gfx_substring); + char *token_suffix = &token_gfx_pos[strlen(gfx_substring)]; + char *mapped_token; + + // cut off token string at ".gfx" substring position + *token_gfx_pos = '\0'; + + // put together prefix "gfx." and token prefix and suffix without ".gfx" + mapped_token = getStringCat3(gfx_prefix, token_prefix, token_suffix); + + free(token_prefix); + + return mapped_token; + } + return NULL; } +char *get_special_base_token(struct ArtworkListInfo *artwork_info, char *token) +{ + /* !!! make this dynamically configurable (init.c:InitArtworkConfig) !!! */ + static struct ConfigTypeInfo prefix_list[] = + { + { "global.anim_1" }, + { "global.anim_2" }, + { "global.anim_3" }, + { "global.anim_4" }, + { "global.anim_5" }, + { "global.anim_6" }, + { "global.anim_7" }, + { "global.anim_8" }, + { "global.anim_9" }, + { "global.anim_10" }, + { "global.anim_11" }, + { "global.anim_12" }, + { "global.anim_13" }, + { "global.anim_14" }, + { "global.anim_15" }, + { "global.anim_16" }, + { "global.anim_17" }, + { "global.anim_18" }, + { "global.anim_19" }, + { "global.anim_20" }, + { "global.anim_21" }, + { "global.anim_22" }, + { "global.anim_23" }, + { "global.anim_24" }, + { "global.anim_25" }, + { "global.anim_26" }, + { "global.anim_27" }, + { "global.anim_28" }, + { "global.anim_29" }, + { "global.anim_30" }, + { "global.anim_31" }, + { "global.anim_32" }, + + { NULL } + }; + struct ConfigTypeInfo *suffix_list = artwork_info->suffix_list; + boolean prefix_found = FALSE; + int len_suffix = 0; + int i; + + /* search for prefix to check if base token has to be created */ + for (i = 0; prefix_list[i].token != NULL; i++) + if (strPrefix(token, prefix_list[i].token)) + prefix_found = TRUE; + + if (!prefix_found) + return NULL; + + /* search for suffix (parameter) to determine base token length */ + for (i = 0; suffix_list[i].token != NULL; i++) + if (strSuffix(token, suffix_list[i].token)) + len_suffix = strlen(suffix_list[i].token); + + return getStringCopyN(token, strlen(token) - len_suffix); +} + /* This function checks if a string of the format "string1, string2, ..." exactly contains a string . */ @@ -2633,6 +2739,100 @@ static boolean string_has_parameter(char *s, char *s_contained) return string_has_parameter(substring, s_contained); } +int get_anim_parameter_value(char *s) +{ + char *pattern_1 = "click:anim_"; + char *pattern_2 = ".part_"; + char *matching_char = NULL; + char *s_ptr = s; + int result = ANIM_EVENT_NONE; + + matching_char = strstr(s_ptr, pattern_1); + if (matching_char == NULL) + return ANIM_EVENT_NONE; + + s_ptr = matching_char + strlen(pattern_1); + + // check for main animation number ("anim_X" or "anim_XX") + if (*s_ptr >= '0' && *s_ptr <= '9') + { + int gic_anim_nr = (*s_ptr++ - '0'); + + if (*s_ptr >= '0' && *s_ptr <= '9') + gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0'); + + if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS) + return ANIM_EVENT_NONE; + + result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT; + } + else + { + // invalid main animation number specified + + return ANIM_EVENT_NONE; + } + + // check for animation part number ("part_X" or "part_XX") (optional) + if (strPrefix(s_ptr, pattern_2)) + { + s_ptr += strlen(pattern_2); + + if (*s_ptr >= '0' && *s_ptr <= '9') + { + int gic_part_nr = (*s_ptr++ - '0'); + + if (*s_ptr >= '0' && *s_ptr <= '9') + gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0'); + + if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS) + return ANIM_EVENT_NONE; + + result |= gic_part_nr << ANIM_EVENT_PART_BIT; + } + else + { + // invalid animation part number specified + + return ANIM_EVENT_NONE; + } + } + + /* discard result if next character is neither delimiter nor whitespace */ + if (!(*s_ptr == ',' || *s_ptr == '\0' || + *s_ptr == ' ' || *s_ptr == '\t')) + return ANIM_EVENT_NONE; + + return result; +} + +int get_anim_action_parameter_value(char *token) +{ + int result = getImageIDFromToken(token); + + if (result == -1) + { + char *gfx_token = getStringCat2("gfx.", token); + + result = getImageIDFromToken(gfx_token); + + checked_free(gfx_token); + } + + if (result == -1) + { + Key key = getKeyFromX11KeyName(token); + + if (key != KSYM_UNDEFINED) + result = -(int)key; + } + + if (result == -1) + result = ANIM_EVENT_ACTION_NONE; + + return result; +} + int get_parameter_value(char *value_raw, char *suffix, int type) { char *value = getStringToLower(value_raw); @@ -2654,7 +2854,8 @@ int get_parameter_value(char *value_raw, char *suffix, int type) strEqual(value, "middle") ? POS_MIDDLE : strEqual(value, "lower") ? POS_LOWER : strEqual(value, "bottom") ? POS_BOTTOM : - strEqual(value, "any") ? POS_ANY : POS_UNDEFINED); + strEqual(value, "any") ? POS_ANY : + strEqual(value, "last") ? POS_LAST : POS_UNDEFINED); } else if (strEqual(suffix, ".align")) { @@ -2687,6 +2888,9 @@ int get_parameter_value(char *value_raw, char *suffix, int type) string_has_parameter(value, "all") ? ANIM_ALL : ANIM_DEFAULT); + if (string_has_parameter(value, "once")) + result |= ANIM_ONCE; + if (string_has_parameter(value, "reverse")) result |= ANIM_REVERSE; @@ -2696,9 +2900,29 @@ int get_parameter_value(char *value_raw, char *suffix, int type) if (string_has_parameter(value, "static_panel")) result |= ANIM_STATIC_PANEL; } + else if (strEqual(suffix, ".init_event") || + strEqual(suffix, ".anim_event")) + { + result = ANIM_EVENT_DEFAULT; + + if (string_has_parameter(value, "any")) + result |= ANIM_EVENT_ANY; + + if (string_has_parameter(value, "click")) + result |= ANIM_EVENT_SELF; + + // add optional "click:anim_X" or "click:anim_X.part_X" parameter + result |= get_anim_parameter_value(value); + } + else if (strEqual(suffix, ".init_event_action") || + strEqual(suffix, ".anim_event_action")) + { + result = get_anim_action_parameter_value(value_raw); + } else if (strEqual(suffix, ".class")) { - result = get_hash_from_key(value); + result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE : + get_hash_from_key(value)); } else if (strEqual(suffix, ".style")) { @@ -2709,6 +2933,15 @@ int get_parameter_value(char *value_raw, char *suffix, int type) if (string_has_parameter(value, "inner_corners")) result |= STYLE_INNER_CORNERS; + + if (string_has_parameter(value, "reverse")) + result |= STYLE_REVERSE; + + if (string_has_parameter(value, "passthrough_clicks")) + result |= STYLE_PASSTHROUGH; + + if (string_has_parameter(value, "multiple_actions")) + result |= STYLE_MULTIPLE_ACTIONS; } else if (strEqual(suffix, ".fade_mode")) { @@ -2736,48 +2969,6 @@ int get_parameter_value(char *value_raw, char *suffix, int type) return result; } -struct ScreenModeInfo *get_screen_mode_from_string(char *screen_mode_string) -{ - static struct ScreenModeInfo screen_mode; - char *screen_mode_string_x = strchr(screen_mode_string, 'x'); - char *screen_mode_string_copy; - char *screen_mode_string_pos_w; - char *screen_mode_string_pos_h; - - if (screen_mode_string_x == NULL) /* invalid screen mode format */ - return NULL; - - screen_mode_string_copy = getStringCopy(screen_mode_string); - - screen_mode_string_pos_w = screen_mode_string_copy; - screen_mode_string_pos_h = strchr(screen_mode_string_copy, 'x'); - *screen_mode_string_pos_h++ = '\0'; - - screen_mode.width = atoi(screen_mode_string_pos_w); - screen_mode.height = atoi(screen_mode_string_pos_h); - - return &screen_mode; -} - -void get_aspect_ratio_from_screen_mode(struct ScreenModeInfo *screen_mode, - int *x, int *y) -{ - float aspect_ratio = (float)screen_mode->width / (float)screen_mode->height; - float aspect_ratio_new; - int i = 1; - - do - { - *x = i * aspect_ratio + 0.000001; - *y = i; - - aspect_ratio_new = (float)*x / (float)*y; - - i++; - } - while (aspect_ratio_new != aspect_ratio && *y < screen_mode->height); -} - static void FreeCustomArtworkList(struct ArtworkListInfo *, struct ListNodeInfo ***, int *); @@ -2786,12 +2977,18 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, char **ignore_tokens, int num_file_list_entries) { + SetupFileHash *ignore_tokens_hash; struct FileInfo *file_list; int num_file_list_entries_found = 0; int num_suffix_list_entries = 0; int list_pos; int i, j; + /* create hash from list of tokens to be ignored (for quick access) */ + ignore_tokens_hash = newSetupFileHash(); + for (i = 0; ignore_tokens[i] != NULL; i++) + setHashEntry(ignore_tokens_hash, ignore_tokens[i], ""); + file_list = checked_calloc(num_file_list_entries * sizeof(struct FileInfo)); for (i = 0; suffix_list[i].token != NULL; i++) @@ -2849,9 +3046,8 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, } /* the following tokens are no file definitions, but other config tokens */ - for (j = 0; ignore_tokens[j] != NULL; j++) - if (strEqual(config_list[i].token, ignore_tokens[j])) - is_file_entry = FALSE; + if (getHashEntry(ignore_tokens_hash, config_list[i].token) != NULL) + is_file_entry = FALSE; if (is_file_entry) { @@ -2881,6 +3077,8 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, Error(ERR_EXIT, "please fix"); } + freeSetupFileHash(ignore_tokens_hash); + return file_list; } @@ -3014,9 +3212,10 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, int num_ext2_suffixes = artwork_info->num_ext2_suffixes; int num_ext3_suffixes = artwork_info->num_ext3_suffixes; int num_ignore_tokens = artwork_info->num_ignore_tokens; - SetupFileHash *setup_file_hash, *valid_file_hash; + SetupFileHash *setup_file_hash, *valid_file_hash, *valid_file_hash_tmp; SetupFileHash *extra_file_hash, *empty_file_hash; char *known_token_value = KNOWN_TOKEN_VALUE; + char *base_token_value = UNDEFINED_FILENAME; int i, j, k, l; if (filename == NULL) @@ -3040,8 +3239,20 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, /* at this point, we do not need the setup file hash anymore -- free it */ freeSetupFileHash(setup_file_hash); - /* map deprecated to current tokens (using prefix match and replace) */ + /* prevent changing hash while iterating over it by using a temporary copy */ + valid_file_hash_tmp = newSetupFileHash(); BEGIN_HASH_ITERATION(valid_file_hash, itr) + { + setHashEntry(valid_file_hash_tmp, + HASH_ITERATION_TOKEN(itr), + HASH_ITERATION_VALUE(itr)); + } + END_HASH_ITERATION(valid_file_hash, itr) + + /* (iterate over same temporary hash, as modifications are independent) */ + + /* map deprecated to current tokens (using prefix match and replace) */ + BEGIN_HASH_ITERATION(valid_file_hash_tmp, itr) { char *token = HASH_ITERATION_TOKEN(itr); char *mapped_token = get_mapped_token(token); @@ -3059,7 +3270,27 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, free(mapped_token); } } - END_HASH_ITERATION(valid_file_hash, itr) + END_HASH_ITERATION(valid_file_hash_tmp, itr) + + /* add special base tokens (using prefix match and replace) */ + BEGIN_HASH_ITERATION(valid_file_hash_tmp, itr) + { + char *token = HASH_ITERATION_TOKEN(itr); + char *base_token = get_special_base_token(artwork_info, token); + + if (base_token != NULL) + { + /* add base token only if it does not already exist */ + if (getHashEntry(valid_file_hash, base_token) == NULL) + setHashEntry(valid_file_hash, base_token, base_token_value); + + free(base_token); + } + } + END_HASH_ITERATION(valid_file_hash_tmp, itr) + + /* free temporary hash used for iteration */ + freeSetupFileHash(valid_file_hash_tmp); /* read parameters for all known config file tokens */ for (i = 0; i < num_file_list_entries; i++)