+ return dir->dir_entry;
+ }
+#endif
+
+ struct dirent *dir_entry = readdir(dir->dir);
+
+ if (dir_entry == NULL)
+ return NULL;
+
+ dir->dir_entry = checked_calloc(sizeof(DirectoryEntry));
+
+ dir->dir_entry->basename = getStringCopy(dir_entry->d_name);
+ dir->dir_entry->filename = getPath2(dir->filename, dir_entry->d_name);
+
+ struct stat file_status;
+
+ dir->dir_entry->is_directory =
+ (stat(dir->dir_entry->filename, &file_status) == 0 &&
+ S_ISDIR(file_status.st_mode));
+
+ return dir->dir_entry;
+}
+
+void freeDirectoryEntry(DirectoryEntry *dir_entry)
+{
+ if (dir_entry == NULL)
+ return;
+
+ checked_free(dir_entry->basename);
+ checked_free(dir_entry->filename);
+ checked_free(dir_entry);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for checking files and filenames */
+/* ------------------------------------------------------------------------- */
+
+boolean directoryExists(char *dir_name)
+{
+ if (dir_name == NULL)
+ return FALSE;
+
+ struct stat file_status;
+ boolean success = (stat(dir_name, &file_status) == 0 &&
+ S_ISDIR(file_status.st_mode));
+
+#if defined(PLATFORM_ANDROID)
+ if (!success)
+ {
+ // this might be an asset directory; check by trying to open toc file
+ char *asset_toc_filename = getPath2(dir_name, ASSET_TOC_BASENAME);
+ SDL_RWops *file = SDL_RWFromFile(asset_toc_filename, MODE_READ);
+
+ checked_free(asset_toc_filename);
+
+ success = (file != NULL);
+
+ if (success)
+ SDL_RWclose(file);
+ }
+#endif
+
+ return success;
+}
+
+boolean fileExists(char *filename)
+{
+ if (filename == NULL)
+ return FALSE;
+
+ boolean success = (access(filename, F_OK) == 0);
+
+#if defined(PLATFORM_ANDROID)
+ if (!success)
+ {
+ // this might be an asset file; check by trying to open it
+ SDL_RWops *file = SDL_RWFromFile(filename, MODE_READ);
+
+ success = (file != NULL);
+
+ if (success)
+ SDL_RWclose(file);
+ }
+#endif
+
+ return success;
+}
+
+boolean fileHasPrefix(char *basename, char *prefix)
+{
+ static char *basename_lower = NULL;
+ int basename_length, prefix_length;
+
+ checked_free(basename_lower);
+
+ if (basename == NULL || prefix == NULL)
+ return FALSE;
+
+ basename_lower = getStringToLower(basename);
+ basename_length = strlen(basename_lower);
+ prefix_length = strlen(prefix);
+
+ if (basename_length > prefix_length + 1 &&
+ basename_lower[prefix_length] == '.' &&
+ strncmp(basename_lower, prefix, prefix_length) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+boolean fileHasSuffix(char *basename, char *suffix)
+{
+ static char *basename_lower = NULL;
+ int basename_length, suffix_length;
+
+ checked_free(basename_lower);
+
+ if (basename == NULL || suffix == NULL)
+ return FALSE;
+
+ basename_lower = getStringToLower(basename);
+ basename_length = strlen(basename_lower);
+ suffix_length = strlen(suffix);
+
+ if (basename_length > suffix_length + 1 &&
+ basename_lower[basename_length - suffix_length - 1] == '.' &&
+ strEqual(&basename_lower[basename_length - suffix_length], suffix))
+ return TRUE;
+
+ return FALSE;
+}
+
+static boolean FileCouldBeArtwork(char *filename)
+{
+ char *basename = getBaseNamePtr(filename);
+
+ return (!strEqual(basename, ".") &&
+ !strEqual(basename, "..") &&
+ !fileHasSuffix(basename, "txt") &&
+ !fileHasSuffix(basename, "conf") &&
+ !directoryExists(filename));
+}
+
+boolean FileIsGraphic(char *filename)
+{
+ return FileCouldBeArtwork(filename);
+}
+
+boolean FileIsSound(char *filename)
+{
+ return FileCouldBeArtwork(filename);
+}
+
+boolean FileIsMusic(char *filename)
+{
+ return FileCouldBeArtwork(filename);
+}
+
+boolean FileIsArtworkType(char *filename, int type)
+{
+ if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(filename)) ||
+ (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(filename)) ||
+ (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(filename)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading artwork configuration information */
+/* ------------------------------------------------------------------------- */
+
+char *get_mapped_token(char *token)
+{
+ /* !!! make this dynamically configurable (init.c:InitArtworkConfig) !!! */
+ static char *map_token_prefix[][2] =
+ {
+ { "char_procent", "char_percent" },
+ { NULL, }
+ };
+ int i;
+
+ for (i = 0; map_token_prefix[i][0] != NULL; i++)
+ {
+ int len_token_prefix = strlen(map_token_prefix[i][0]);
+
+ if (strncmp(token, map_token_prefix[i][0], len_token_prefix) == 0)
+ 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" },
+
+ { 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 <s> of the format "string1, string2, ..."
+ exactly contains a string <s_contained>. */
+
+static boolean string_has_parameter(char *s, char *s_contained)
+{
+ char *substring;
+
+ if (s == NULL || s_contained == NULL)
+ return FALSE;
+
+ if (strlen(s_contained) > strlen(s))
+ return FALSE;
+
+ if (strncmp(s, s_contained, strlen(s_contained)) == 0)
+ {
+ char next_char = s[strlen(s_contained)];
+
+ /* check if next character is delimiter or whitespace */
+ return (next_char == ',' || next_char == '\0' ||
+ next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
+ }
+
+ /* check if string contains another parameter string after a comma */
+ substring = strchr(s, ',');
+ if (substring == NULL) /* string does not contain a comma */
+ return FALSE;
+
+ /* advance string pointer to next character after the comma */
+ substring++;
+
+ /* skip potential whitespaces after the comma */
+ while (*substring == ' ' || *substring == '\t')
+ substring++;
+
+ return string_has_parameter(substring, s_contained);
+}
+
+int get_parameter_value(char *value_raw, char *suffix, int type)
+{
+ char *value = getStringToLower(value_raw);
+ int result = 0; /* probably a save default value */
+
+ if (strEqual(suffix, ".direction"))
+ {
+ result = (strEqual(value, "left") ? MV_LEFT :
+ strEqual(value, "right") ? MV_RIGHT :
+ strEqual(value, "up") ? MV_UP :
+ strEqual(value, "down") ? MV_DOWN : MV_NONE);
+ }
+ else if (strEqual(suffix, ".position"))
+ {
+ result = (strEqual(value, "left") ? POS_LEFT :
+ strEqual(value, "right") ? POS_RIGHT :
+ strEqual(value, "top") ? POS_TOP :
+ strEqual(value, "upper") ? POS_UPPER :
+ strEqual(value, "middle") ? POS_MIDDLE :
+ strEqual(value, "lower") ? POS_LOWER :
+ strEqual(value, "bottom") ? POS_BOTTOM :
+ strEqual(value, "any") ? POS_ANY : POS_UNDEFINED);
+ }
+ else if (strEqual(suffix, ".align"))
+ {
+ result = (strEqual(value, "left") ? ALIGN_LEFT :
+ strEqual(value, "right") ? ALIGN_RIGHT :
+ strEqual(value, "center") ? ALIGN_CENTER :
+ strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".valign"))
+ {
+ result = (strEqual(value, "top") ? VALIGN_TOP :
+ strEqual(value, "bottom") ? VALIGN_BOTTOM :
+ strEqual(value, "middle") ? VALIGN_MIDDLE :
+ strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".anim_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? ANIM_NONE :
+ string_has_parameter(value, "loop") ? ANIM_LOOP :
+ string_has_parameter(value, "linear") ? ANIM_LINEAR :
+ string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
+ string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
+ string_has_parameter(value, "random") ? ANIM_RANDOM :
+ string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
+ string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
+ string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
+ string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
+ string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
+ string_has_parameter(value, "centered") ? ANIM_CENTERED :
+ 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;
+
+ if (string_has_parameter(value, "opaque_player"))
+ result |= ANIM_OPAQUE_PLAYER;
+
+ if (string_has_parameter(value, "static_panel"))
+ result |= ANIM_STATIC_PANEL;
+ }
+ else if (strEqual(suffix, ".class"))
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ get_hash_from_key(value));
+ }
+ else if (strEqual(suffix, ".style"))
+ {
+ result = STYLE_DEFAULT;
+
+ if (string_has_parameter(value, "accurate_borders"))
+ result |= STYLE_ACCURATE_BORDERS;
+
+ if (string_has_parameter(value, "inner_corners"))
+ result |= STYLE_INNER_CORNERS;
+ }
+ else if (strEqual(suffix, ".fade_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
+ string_has_parameter(value, "fade") ? FADE_MODE_FADE :
+ string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
+ string_has_parameter(value, "melt") ? FADE_MODE_MELT :
+ string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
+ FADE_MODE_DEFAULT);
+ }
+ else if (strPrefix(suffix, ".font")) /* (may also be ".font_xyz") */
+ {
+ result = gfx.get_font_from_token_function(value);
+ }
+ else /* generic parameter of type integer or boolean */
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ type == TYPE_INTEGER ? get_integer_from_string(value) :
+ type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
+ ARG_UNDEFINED_VALUE);
+ }
+
+ free(value);
+
+ return result;
+}
+
+static void FreeCustomArtworkList(struct ArtworkListInfo *,
+ struct ListNodeInfo ***, int *);
+
+struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list,
+ struct ConfigTypeInfo *suffix_list,
+ char **ignore_tokens,
+ int num_file_list_entries)
+{
+ struct FileInfo *file_list;
+ int num_file_list_entries_found = 0;
+ int num_suffix_list_entries = 0;
+ int list_pos;
+ int i, j;
+
+ file_list = checked_calloc(num_file_list_entries * sizeof(struct FileInfo));
+
+ for (i = 0; suffix_list[i].token != NULL; i++)
+ num_suffix_list_entries++;
+
+ /* always start with reliable default values */
+ for (i = 0; i < num_file_list_entries; i++)
+ {
+ file_list[i].token = NULL;
+
+ file_list[i].default_filename = NULL;
+ file_list[i].filename = NULL;
+
+ if (num_suffix_list_entries > 0)
+ {
+ int parameter_array_size = num_suffix_list_entries * sizeof(char *);
+
+ file_list[i].default_parameter = checked_calloc(parameter_array_size);
+ file_list[i].parameter = checked_calloc(parameter_array_size);
+
+ for (j = 0; j < num_suffix_list_entries; j++)
+ {
+ setString(&file_list[i].default_parameter[j], suffix_list[j].value);
+ setString(&file_list[i].parameter[j], suffix_list[j].value);
+ }
+
+ file_list[i].redefined = FALSE;
+ file_list[i].fallback_to_default = FALSE;
+ file_list[i].default_is_cloned = FALSE;
+ }
+ }
+
+ list_pos = 0;
+
+ for (i = 0; config_list[i].token != NULL; i++)
+ {
+ int len_config_token = strlen(config_list[i].token);
+ boolean is_file_entry = TRUE;
+
+ for (j = 0; suffix_list[j].token != NULL; j++)
+ {
+ int len_suffix = strlen(suffix_list[j].token);
+
+ if (len_suffix < len_config_token &&
+ strEqual(&config_list[i].token[len_config_token - len_suffix],
+ suffix_list[j].token))
+ {
+ setString(&file_list[list_pos].default_parameter[j],
+ config_list[i].value);
+
+ is_file_entry = FALSE;
+
+ break;
+ }
+ }
+
+ /* 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 (is_file_entry)