+ if (parameter_suffix_found)
+ continue;
+
+ /* ---------- step 0: search for matching base prefix ---------- */
+
+ start_pos = 0;
+ for (i = 0; i < num_base_prefixes && !base_prefix_found; i++)
+ {
+ char *base_prefix = base_prefixes[i];
+ int len_base_prefix = strlen(base_prefix);
+ boolean ext1_suffix_found = FALSE;
+ boolean ext2_suffix_found = FALSE;
+ boolean ext3_suffix_found = FALSE;
+ boolean exact_match = FALSE;
+ int base_index = -1;
+ int ext1_index = -1;
+ int ext2_index = -1;
+ int ext3_index = -1;
+
+ base_prefix_found = token_suffix_match(token, base_prefix, start_pos);
+
+ if (!base_prefix_found)
+ continue;
+
+ base_index = i;
+
+ if (start_pos + len_base_prefix == len_token) /* exact match */
+ {
+ exact_match = TRUE;
+
+ add_dynamic_file_list_entry(dynamic_file_list,
+ num_dynamic_file_list_entries,
+ extra_file_hash,
+ suffix_list,
+ num_suffix_list_entries,
+ token);
+ add_property_mapping(property_mapping,
+ num_property_mapping_entries,
+ base_index, -1, -1, -1,
+ current_summarized_file_list_entry);
+ continue;
+ }
+
+#if 0
+ if (IS_PARENT_PROCESS())
+ printf("---> examining token '%s': search 1st suffix ...\n", token);
+#endif
+
+ /* ---------- step 1: search for matching first suffix ---------- */
+
+ start_pos += len_base_prefix;
+ for (j = 0; j < num_ext1_suffixes && !ext1_suffix_found; j++)
+ {
+ char *ext1_suffix = ext1_suffixes[j];
+ int len_ext1_suffix = strlen(ext1_suffix);
+
+ ext1_suffix_found = token_suffix_match(token, ext1_suffix, start_pos);
+
+ if (!ext1_suffix_found)
+ continue;
+
+ ext1_index = j;
+
+ if (start_pos + len_ext1_suffix == len_token) /* exact match */
+ {
+ exact_match = TRUE;
+
+ add_dynamic_file_list_entry(dynamic_file_list,
+ num_dynamic_file_list_entries,
+ extra_file_hash,
+ suffix_list,
+ num_suffix_list_entries,
+ token);
+ add_property_mapping(property_mapping,
+ num_property_mapping_entries,
+ base_index, ext1_index, -1, -1,
+ current_summarized_file_list_entry);
+ continue;
+ }
+
+ start_pos += len_ext1_suffix;
+ }
+
+ if (exact_match)
+ break;
+
+#if 0
+ if (IS_PARENT_PROCESS())
+ printf("---> examining token '%s': search 2nd suffix ...\n", token);
+#endif
+
+ /* ---------- step 2: search for matching second suffix ---------- */
+
+ for (k = 0; k < num_ext2_suffixes && !ext2_suffix_found; k++)
+ {
+ char *ext2_suffix = ext2_suffixes[k];
+ int len_ext2_suffix = strlen(ext2_suffix);
+
+ ext2_suffix_found = token_suffix_match(token, ext2_suffix, start_pos);
+
+ if (!ext2_suffix_found)
+ continue;
+
+ ext2_index = k;
+
+ if (start_pos + len_ext2_suffix == len_token) /* exact match */
+ {
+ exact_match = TRUE;
+
+ add_dynamic_file_list_entry(dynamic_file_list,
+ num_dynamic_file_list_entries,
+ extra_file_hash,
+ suffix_list,
+ num_suffix_list_entries,
+ token);
+ add_property_mapping(property_mapping,
+ num_property_mapping_entries,
+ base_index, ext1_index, ext2_index, -1,
+ current_summarized_file_list_entry);
+ continue;
+ }
+
+ start_pos += len_ext2_suffix;
+ }
+
+ if (exact_match)
+ break;
+
+#if 0
+ if (IS_PARENT_PROCESS())
+ printf("---> examining token '%s': search 3rd suffix ...\n",token);
+#endif
+
+ /* ---------- step 3: search for matching third suffix ---------- */
+
+ for (l = 0; l < num_ext3_suffixes && !ext3_suffix_found; l++)
+ {
+ char *ext3_suffix = ext3_suffixes[l];
+ int len_ext3_suffix = strlen(ext3_suffix);
+
+ ext3_suffix_found = token_suffix_match(token, ext3_suffix, start_pos);
+
+ if (!ext3_suffix_found)
+ continue;
+
+ ext3_index = l;
+
+ if (start_pos + len_ext3_suffix == len_token) /* exact match */
+ {
+ exact_match = TRUE;
+
+ add_dynamic_file_list_entry(dynamic_file_list,
+ num_dynamic_file_list_entries,
+ extra_file_hash,
+ suffix_list,
+ num_suffix_list_entries,
+ token);
+ add_property_mapping(property_mapping,
+ num_property_mapping_entries,
+ base_index, ext1_index, ext2_index, ext3_index,
+ current_summarized_file_list_entry);
+ continue;
+ }
+ }
+ }
+ }
+ END_HASH_ITERATION(extra_file_hash, itr)
+
+ if (artwork_info->num_dynamic_file_list_entries > 0)
+ {
+ artwork_info->dynamic_artwork_list =
+ checked_calloc(artwork_info->num_dynamic_file_list_entries *
+ artwork_info->sizeof_artwork_list_entry);
+ }
+
+ if (options.verbose && IS_PARENT_PROCESS())
+ {
+ SetupFileList *setup_file_list, *list;
+ boolean dynamic_tokens_found = FALSE;
+ boolean unknown_tokens_found = FALSE;
+ boolean undefined_values_found = (hashtable_count(empty_file_hash) != 0);
+
+ if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+ Error(ERR_EXIT, "loadSetupFileHash works, but loadSetupFileList fails");
+
+ BEGIN_HASH_ITERATION(extra_file_hash, itr)
+ {
+ if (strcmp(HASH_ITERATION_VALUE(itr), known_token_value) == 0)
+ dynamic_tokens_found = TRUE;
+ else
+ unknown_tokens_found = TRUE;
+ }
+ END_HASH_ITERATION(extra_file_hash, itr)
+
+ if (options.debug && dynamic_tokens_found)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "dynamic token(s) found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'", filename);
+
+ for (list = setup_file_list; list != NULL; list = list->next)
+ {
+ char *value = getHashEntry(extra_file_hash, list->token);
+
+ if (value != NULL && strcmp(value, known_token_value) == 0)
+ Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
+ }
+
+ Error(ERR_RETURN_LINE, "-");
+ }
+
+ if (unknown_tokens_found)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'", filename);
+
+ for (list = setup_file_list; list != NULL; list = list->next)
+ {
+ char *value = getHashEntry(extra_file_hash, list->token);
+
+ if (value != NULL && strcmp(value, known_token_value) != 0)
+ Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
+ }
+
+ Error(ERR_RETURN_LINE, "-");
+ }
+
+ if (undefined_values_found)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: undefined values found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'", filename);
+
+ for (list = setup_file_list; list != NULL; list = list->next)
+ {
+ char *value = getHashEntry(empty_file_hash, list->token);
+
+ if (value != NULL)
+ Error(ERR_RETURN, "- undefined value for token: '%s'", list->token);
+ }
+
+ Error(ERR_RETURN_LINE, "-");
+ }
+
+ freeSetupFileList(setup_file_list);
+ }
+
+ freeSetupFileHash(extra_file_hash);
+ freeSetupFileHash(empty_file_hash);
+
+#if 0
+ for (i = 0; i < num_file_list_entries; i++)
+ {
+ printf("'%s' ", file_list[i].token);
+ if (file_list[i].filename)
+ printf("-> '%s'\n", file_list[i].filename);
+ else
+ printf("-> UNDEFINED [-> '%s']\n", file_list[i].default_filename);
+ }
+#endif
+}
+
+void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
+{
+ struct FileInfo *file_list = artwork_info->file_list;
+ int num_file_list_entries = artwork_info->num_file_list_entries;
+ int num_suffix_list_entries = artwork_info->num_suffix_list_entries;
+ char *filename_base = UNDEFINED_FILENAME, *filename_local;
+ int i, j;
+
+#if 0
+ printf("GOT CUSTOM ARTWORK CONFIG FILE '%s'\n", filename);
+#endif
+
+ DrawInitText("Loading artwork config:", 120, FC_GREEN);
+ DrawInitText(ARTWORKINFO_FILENAME(artwork_info->type), 150, FC_YELLOW);
+
+ /* always start with reliable default values */
+ for (i = 0; i < num_file_list_entries; i++)
+ {
+ setString(&file_list[i].filename, file_list[i].default_filename);
+
+ for (j = 0; j < num_suffix_list_entries; j++)
+ setString(&file_list[i].parameter[j], file_list[i].default_parameter[j]);
+
+ file_list[i].redefined = FALSE;
+ file_list[i].fallback_to_default = FALSE;
+ }
+
+ /* free previous dynamic artwork file array */
+ if (artwork_info->dynamic_file_list != NULL)
+ {
+ for (i = 0; i < artwork_info->num_dynamic_file_list_entries; i++)
+ {
+ free(artwork_info->dynamic_file_list[i].token);
+ free(artwork_info->dynamic_file_list[i].filename);
+ free(artwork_info->dynamic_file_list[i].parameter);
+ }
+
+ free(artwork_info->dynamic_file_list);
+ artwork_info->dynamic_file_list = NULL;
+
+ FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
+ &artwork_info->num_dynamic_file_list_entries);
+ }
+
+ /* free previous property mapping */
+ if (artwork_info->property_mapping != NULL)
+ {
+ free(artwork_info->property_mapping);
+
+ artwork_info->property_mapping = NULL;
+ artwork_info->num_property_mapping_entries = 0;
+ }
+
+ if (!SETUP_OVERRIDE_ARTWORK(setup, artwork_info->type))
+ {
+ /* first look for special artwork configured in level series config */
+ filename_base = getCustomArtworkLevelConfigFilename(artwork_info->type);
+
+ if (fileExists(filename_base))
+ LoadArtworkConfigFromFilename(artwork_info, filename_base);
+ }
+
+ filename_local = getCustomArtworkConfigFilename(artwork_info->type);
+
+ if (filename_local != NULL && strcmp(filename_base, filename_local) != 0)
+ LoadArtworkConfigFromFilename(artwork_info, filename_local);
+}
+
+static void deleteArtworkListEntry(struct ArtworkListInfo *artwork_info,
+ struct ListNodeInfo **listnode)
+{
+ if (*listnode)
+ {
+ char *filename = (*listnode)->source_filename;
+
+ if (--(*listnode)->num_references <= 0)
+ deleteNodeFromList(&artwork_info->content_list, filename,
+ artwork_info->free_artwork);
+
+ *listnode = NULL;
+ }
+}
+
+static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
+ struct ListNodeInfo **listnode,
+ struct FileInfo *file_list_entry)
+{
+ char *init_text[] =
+ {
+ "Loading graphics:",
+ "Loading sounds:",
+ "Loading music:"
+ };
+
+ ListNode *node;
+ char *basename = file_list_entry->filename;
+ char *filename = getCustomArtworkFilename(basename, artwork_info->type);
+
+ if (filename == NULL)
+ {
+ Error(ERR_WARN, "cannot find artwork file '%s'", basename);
+
+ basename = file_list_entry->default_filename;
+
+ /* dynamic artwork has no default filename / skip empty default artwork */
+ if (basename == NULL || strcmp(basename, UNDEFINED_FILENAME) == 0)
+ return;
+
+ file_list_entry->fallback_to_default = TRUE;
+
+ Error(ERR_WARN, "trying default artwork file '%s'", basename);
+
+ filename = getCustomArtworkFilename(basename, artwork_info->type);
+
+ if (filename == NULL)
+ {
+ int error_mode = ERR_WARN;
+
+ /* we can get away without sounds and music, but not without graphics */
+ if (*listnode == NULL && artwork_info->type == ARTWORK_TYPE_GRAPHICS)
+ error_mode = ERR_EXIT;
+
+ Error(error_mode, "cannot find default artwork file '%s'", basename);
+
+ return;
+ }
+ }
+
+ /* check if the old and the new artwork file are the same */
+ if (*listnode && strcmp((*listnode)->source_filename, filename) == 0)
+ {
+ /* The old and new artwork are the same (have the same filename and path).
+ This usually means that this artwork does not exist in this artwork set
+ and a fallback to the existing artwork is done. */
+
+#if 0
+ printf("[artwork '%s' already exists (same list entry)]\n", filename);
+#endif
+
+ return;
+ }
+
+ /* delete existing artwork file entry */
+ deleteArtworkListEntry(artwork_info, listnode);
+
+ /* check if the new artwork file already exists in the list of artworks */
+ if ((node = getNodeFromKey(artwork_info->content_list, filename)) != NULL)
+ {
+#if 0
+ printf("[artwork '%s' already exists (other list entry)]\n", filename);
+#endif
+
+ *listnode = (struct ListNodeInfo *)node->content;
+ (*listnode)->num_references++;
+
+ return;
+ }
+
+ DrawInitText(init_text[artwork_info->type], 120, FC_GREEN);
+ DrawInitText(basename, 150, FC_YELLOW);
+
+ if ((*listnode = artwork_info->load_artwork(filename)) != NULL)
+ {
+#if 0
+ printf("[adding new artwork '%s']\n", filename);
+#endif
+
+ (*listnode)->num_references = 1;
+ addNodeToList(&artwork_info->content_list, (*listnode)->source_filename,
+ *listnode);
+ }
+ else
+ {
+ int error_mode = ERR_WARN;
+
+ /* we can get away without sounds and music, but not without graphics */
+ if (artwork_info->type == ARTWORK_TYPE_GRAPHICS)
+ error_mode = ERR_EXIT;
+
+ Error(error_mode, "cannot load artwork file '%s'", basename);
+ return;
+ }
+}
+
+static void LoadCustomArtwork(struct ArtworkListInfo *artwork_info,
+ struct ListNodeInfo **listnode,
+ struct FileInfo *file_list_entry)
+{
+#if 0
+ printf("GOT CUSTOM ARTWORK FILE '%s'\n", filename);
+#endif
+
+ if (strcmp(file_list_entry->filename, UNDEFINED_FILENAME) == 0)
+ {
+ deleteArtworkListEntry(artwork_info, listnode);
+ return;
+ }
+
+ replaceArtworkListEntry(artwork_info, listnode, file_list_entry);
+}
+
+void ReloadCustomArtworkList(struct ArtworkListInfo *artwork_info)
+{
+ struct FileInfo *file_list = artwork_info->file_list;
+ struct FileInfo *dynamic_file_list = artwork_info->dynamic_file_list;
+ int num_file_list_entries = artwork_info->num_file_list_entries;
+ int num_dynamic_file_list_entries =
+ artwork_info->num_dynamic_file_list_entries;
+ int i;
+
+ for (i = 0; i < num_file_list_entries; i++)
+ LoadCustomArtwork(artwork_info, &artwork_info->artwork_list[i],
+ &file_list[i]);
+
+ for (i = 0; i < num_dynamic_file_list_entries; i++)
+ LoadCustomArtwork(artwork_info, &artwork_info->dynamic_artwork_list[i],
+ &dynamic_file_list[i]);
+
+#if 0
+ dumpList(artwork_info->content_list);
+#endif
+}
+
+static void FreeCustomArtworkList(struct ArtworkListInfo *artwork_info,
+ struct ListNodeInfo ***list,
+ int *num_list_entries)
+{
+ int i;
+
+ if (*list == NULL)
+ return;
+
+ for (i = 0; i < *num_list_entries; i++)
+ deleteArtworkListEntry(artwork_info, &(*list)[i]);
+ free(*list);
+
+ *list = NULL;
+ *num_list_entries = 0;
+}
+
+void FreeCustomArtworkLists(struct ArtworkListInfo *artwork_info)
+{
+ if (artwork_info == NULL)
+ return;
+
+ FreeCustomArtworkList(artwork_info, &artwork_info->artwork_list,
+ &artwork_info->num_file_list_entries);
+
+ FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
+ &artwork_info->num_dynamic_file_list_entries);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions only needed for non-Unix (non-command-line) systems */
+/* (MS-DOS only; SDL/Windows creates files "stdout.txt" and "stderr.txt") */
+/* ------------------------------------------------------------------------- */
+
+#if defined(PLATFORM_MSDOS)
+
+#define ERROR_FILENAME "stderr.txt"
+
+void initErrorFile()
+{
+ unlink(ERROR_FILENAME);
+}
+
+FILE *openErrorFile()
+{
+ return fopen(ERROR_FILENAME, MODE_APPEND);
+}
+
+void dumpErrorFile()
+{
+ FILE *error_file = fopen(ERROR_FILENAME, MODE_READ);