rnd-20070302-1-src
[rocksndiamonds.git] / src / libgame / setup.c
index b116457e575d5df7addfa73e9571b0027c985395..922f8314aa1689dd47c52eea6af5dd96f2f84211 100644 (file)
@@ -88,6 +88,7 @@ static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
 
 
 static void setTreeInfoToDefaults(TreeInfo *, int);
+static TreeInfo *getTreeInfoCopy(TreeInfo *ti);
 static int compareTreeInfoEntries(const void *, const void *);
 
 static int token_value_position   = TOKEN_VALUE_POSITION_DEFAULT;
@@ -497,6 +498,23 @@ char *getLevelSetInfoFilename()
   return NULL;
 }
 
+char *getLevelSetTitleMessageFilename(int nr, boolean initial)
+{
+  static char *filename = NULL;
+  char basename[32];
+
+  sprintf(basename, "%s_%d.txt",
+         (initial ? "titlemessage_initial" : "titlemessage"), nr + 1);
+
+  checked_free(filename);
+  filename = getPath2(getCurrentLevelDir(), basename);
+
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;
+}
+
 static char *getCorrectedArtworkBasename(char *basename)
 {
   char *basename_corrected = basename;
@@ -980,9 +998,13 @@ TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
     return cloneTreeNode(node_top, node_parent, node->next,
                         skip_sets_without_levels);
 
+#if 1
+  node_new = getTreeInfoCopy(node);            /* copy complete node */
+#else
   node_new = newTreeInfo();
 
   *node_new = *node;                           /* copy complete node */
+#endif
 
   node_new->node_top = node_top;               /* correct top node link */
   node_new->node_parent = node_parent;         /* correct parent node link */
@@ -1585,25 +1607,42 @@ static void printSetupFileHash(SetupFileHash *hash)
 }
 #endif
 
-static void *loadSetupFileData(char *filename, boolean use_hash)
+#define ALLOW_TOKEN_VALUE_SEPARATOR_BEING_WHITESPACE           1
+#define CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING           0
+
+static void loadSetupFileData(void *setup_file_data, char *filename,
+                             boolean top_recursion_level, boolean is_hash)
 {
-  char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
+  static SetupFileHash *include_filename_hash = NULL;
+  char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
   char *token, *value, *line_ptr;
-  void *setup_file_data, *insert_ptr = NULL;
+  void *insert_ptr = NULL;
   boolean read_continued_line = FALSE;
+  boolean token_value_separator_found;
+#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING
+  boolean token_value_separator_warning = FALSE;
+#endif
   FILE *file;
+  int line_nr = 0;
+  int token_count = 0;
 
   if (!(file = fopen(filename, MODE_READ)))
   {
     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
 
-    return NULL;
+    return;
   }
 
-  if (use_hash)
-    setup_file_data = newSetupFileHash();
-  else
-    insert_ptr = setup_file_data = newSetupFileList("", "");
+  /* use "insert pointer" to store list end for constant insertion complexity */
+  if (!is_hash)
+    insert_ptr = setup_file_data;
+
+  /* on top invocation, create hash to mark included files (to prevent loops) */
+  if (top_recursion_level)
+    include_filename_hash = newSetupFileHash();
+
+  /* mark this file as already included (to prevent including it again) */
+  setHashEntry(include_filename_hash, getBaseNamePtr(filename), "true");
 
   while (!feof(file))
   {
@@ -1611,11 +1650,18 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
     if (!fgets(line, MAX_LINE_LEN, file))
       break;
 
-    /* cut trailing newline or carriage return */
+    /* check if line was completely read and is terminated by line break */
+    if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
+      line_nr++;
+
+    /* cut trailing line break (this can be newline and/or carriage return) */
     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
        *line_ptr = '\0';
 
+    /* copy raw input line for later use (mainly debugging output) */
+    strcpy(line_raw, line);
+
     if (read_continued_line)
     {
       /* cut leading whitespaces from input line */
@@ -1670,10 +1716,13 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
     /* start with empty value as reliable default */
     value = "";
 
+    token_value_separator_found = FALSE;
+
     /* find end of token to determine start of value */
     for (line_ptr = token; *line_ptr; line_ptr++)
     {
 #if 1
+      /* first look for an explicit token/value separator, like ':' or '=' */
       if (*line_ptr == ':' || *line_ptr == '=')
 #else
       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
@@ -1682,10 +1731,47 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
        *line_ptr = '\0';               /* terminate token string */
        value = line_ptr + 1;           /* set beginning of value */
 
+       token_value_separator_found = TRUE;
+
        break;
       }
     }
 
+#if ALLOW_TOKEN_VALUE_SEPARATOR_BEING_WHITESPACE
+    /* fallback: if no token/value separator found, also allow whitespaces */
+    if (!token_value_separator_found)
+    {
+      for (line_ptr = token; *line_ptr; line_ptr++)
+      {
+       if (*line_ptr == ' ' || *line_ptr == '\t')
+       {
+         *line_ptr = '\0';             /* terminate token string */
+         value = line_ptr + 1;         /* set beginning of value */
+
+         token_value_separator_found = TRUE;
+
+         break;
+       }
+      }
+
+#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING
+      if (token_value_separator_found)
+      {
+       if (!token_value_separator_warning)
+       {
+         Error(ERR_INFO_LINE, "-");
+         Error(ERR_WARN, "missing token/value separator(s) in config file:");
+         Error(ERR_INFO, "- config file: '%s'", filename);
+
+         token_value_separator_warning = TRUE;
+       }
+
+       Error(ERR_INFO, "- line %d: '%s'", line_nr, line_raw);
+      }
+#endif
+    }
+#endif
+
     /* cut trailing whitespaces from token */
     for (line_ptr = &token[strlen(token)]; line_ptr >= token; line_ptr--)
       if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
@@ -1703,35 +1789,53 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
 
     if (*token)
     {
-      if (use_hash)
-       setHashEntry((SetupFileHash *)setup_file_data, token, value);
+      if (strEqual(token, "include"))
+      {
+       if (getHashEntry(include_filename_hash, value) == NULL)
+       {
+         char *basepath = getBasePath(filename);
+         char *basename = getBaseName(value);
+         char *filename_include = getPath2(basepath, basename);
+
+#if 0
+         Error(ERR_INFO, "[including file '%s']", filename_include);
+#endif
+
+         loadSetupFileData(setup_file_data, filename_include, FALSE, is_hash);
+
+         free(basepath);
+         free(basename);
+         free(filename_include);
+       }
+       else
+       {
+         Error(ERR_WARN, "ignoring already processed file '%s'", value);
+       }
+      }
       else
-       insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
+      {
+       if (is_hash)
+         setHashEntry((SetupFileHash *)setup_file_data, token, value);
+       else
+         insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
+
+       token_count++;
+      }
     }
   }
 
   fclose(file);
 
-  if (use_hash)
-  {
-    if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
-      Error(ERR_WARN, "configuration file '%s' is empty", filename);
-  }
-  else
-  {
-    SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
-    SetupFileList *first_valid_list_entry = setup_file_list->next;
+#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING
+  if (token_value_separator_warning)
+    Error(ERR_INFO_LINE, "-");
+#endif
 
-    /* free empty list header */
-    setup_file_list->next = NULL;
-    freeSetupFileList(setup_file_list);
-    setup_file_data = first_valid_list_entry;
+  if (token_count == 0)
+    Error(ERR_WARN, "configuration file '%s' is empty", filename);
 
-    if (first_valid_list_entry == NULL)
-      Error(ERR_WARN, "configuration file '%s' is empty", filename);
-  }
-
-  return setup_file_data;
+  if (top_recursion_level)
+    freeSetupFileHash(include_filename_hash);
 }
 
 void saveSetupFileHash(SetupFileHash *hash, char *filename)
@@ -1757,12 +1861,27 @@ void saveSetupFileHash(SetupFileHash *hash, char *filename)
 
 SetupFileList *loadSetupFileList(char *filename)
 {
-  return (SetupFileList *)loadSetupFileData(filename, FALSE);
+  SetupFileList *setup_file_list = newSetupFileList("", "");
+  SetupFileList *first_valid_list_entry;
+
+  loadSetupFileData(setup_file_list, filename, TRUE, FALSE);
+
+  first_valid_list_entry = setup_file_list->next;
+
+  /* free empty list header */
+  setup_file_list->next = NULL;
+  freeSetupFileList(setup_file_list);
+
+  return first_valid_list_entry;
 }
 
 SetupFileHash *loadSetupFileHash(char *filename)
 {
-  return (SetupFileHash *)loadSetupFileData(filename, TRUE);
+  SetupFileHash *setup_file_hash = newSetupFileHash();
+
+  loadSetupFileData(setup_file_hash, filename, TRUE, TRUE);
+
+  return setup_file_hash;
 }
 
 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
@@ -1988,6 +2107,68 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent)
   }
 }
 
+static TreeInfo *getTreeInfoCopy(TreeInfo *ti)
+{
+  TreeInfo *ti_copy = newTreeInfo();
+
+  /* copy all values from the original structure */
+
+  ti_copy->type                        = ti->type;
+
+  ti_copy->node_top            = ti->node_top;
+  ti_copy->node_parent         = ti->node_parent;
+  ti_copy->node_group          = ti->node_group;
+  ti_copy->next                        = ti->next;
+
+  ti_copy->cl_first            = ti->cl_first;
+  ti_copy->cl_cursor           = ti->cl_cursor;
+
+  ti_copy->subdir              = getStringCopy(ti->subdir);
+  ti_copy->fullpath            = getStringCopy(ti->fullpath);
+  ti_copy->basepath            = getStringCopy(ti->basepath);
+  ti_copy->identifier          = getStringCopy(ti->identifier);
+  ti_copy->name                        = getStringCopy(ti->name);
+  ti_copy->name_sorting                = getStringCopy(ti->name_sorting);
+  ti_copy->author              = getStringCopy(ti->author);
+  ti_copy->imported_from       = getStringCopy(ti->imported_from);
+  ti_copy->imported_by         = getStringCopy(ti->imported_by);
+
+  ti_copy->graphics_set_ecs    = getStringCopy(ti->graphics_set_ecs);
+  ti_copy->graphics_set_aga    = getStringCopy(ti->graphics_set_aga);
+  ti_copy->graphics_set                = getStringCopy(ti->graphics_set);
+  ti_copy->sounds_set          = getStringCopy(ti->sounds_set);
+  ti_copy->music_set           = getStringCopy(ti->music_set);
+  ti_copy->graphics_path       = getStringCopy(ti->graphics_path);
+  ti_copy->sounds_path         = getStringCopy(ti->sounds_path);
+  ti_copy->music_path          = getStringCopy(ti->music_path);
+
+  ti_copy->level_filename      = getStringCopy(ti->level_filename);
+  ti_copy->level_filetype      = getStringCopy(ti->level_filetype);
+
+  ti_copy->levels              = ti->levels;
+  ti_copy->first_level         = ti->first_level;
+  ti_copy->last_level          = ti->last_level;
+  ti_copy->sort_priority       = ti->sort_priority;
+
+  ti_copy->latest_engine       = ti->latest_engine;
+
+  ti_copy->level_group         = ti->level_group;
+  ti_copy->parent_link         = ti->parent_link;
+  ti_copy->in_user_dir         = ti->in_user_dir;
+  ti_copy->user_defined                = ti->user_defined;
+  ti_copy->readonly            = ti->readonly;
+  ti_copy->handicap            = ti->handicap;
+  ti_copy->skip_levels         = ti->skip_levels;
+
+  ti_copy->color               = ti->color;
+  ti_copy->class_desc          = getStringCopy(ti->class_desc);
+  ti_copy->handicap_level      = ti->handicap_level;
+
+  ti_copy->infotext            = getStringCopy(ti->infotext);
+
+  return ti_copy;
+}
+
 static void freeTreeInfo(TreeInfo *ti)
 {
   if (ti == NULL)
@@ -2250,12 +2431,13 @@ static TreeInfo *getArtworkInfoCacheEntry(LevelDirTree *level_node, int type)
       if (value == NULL)
       {
 #if 1
-       printf("::: - WARNING: cache entry '%s' invalid\n", token);
+       Error(ERR_WARN, "cache entry '%s' invalid", token);
 #endif
 
        cached = FALSE;
       }
     }
+
     *artwork_info = ldi;
   }
 
@@ -2356,6 +2538,8 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
                                          char *level_directory,
                                          char *directory_name)
 {
+  static unsigned long progress_delay = 0;
+  unsigned long progress_delay_value = 100;    /* (in milliseconds) */
   char *directory_path = getPath2(level_directory, directory_name);
   char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
   SetupFileHash *setup_file_hash;
@@ -2455,8 +2639,13 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
     (leveldir_new->user_defined || !leveldir_new->handicap ?
      leveldir_new->last_level : leveldir_new->first_level);
 
-  if (leveldir_new->level_group)
+#if 1
+  if (leveldir_new->level_group ||
+      DelayReached(&progress_delay, progress_delay_value))
     DrawInitText(leveldir_new->name, 150, FC_YELLOW);
+#else
+  DrawInitText(leveldir_new->name, 150, FC_YELLOW);
+#endif
 
 #if 0
   /* !!! don't skip sets without levels (else artwork base sets are missing) */
@@ -2900,6 +3089,8 @@ void LoadArtworkInfo()
 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
                                  LevelDirTree *level_node)
 {
+  static unsigned long progress_delay = 0;
+  unsigned long progress_delay_value = 100;    /* (in milliseconds) */
   int type = (*artwork_node)->type;
 
   /* recursively check all level directories for artwork sub-directories */
@@ -2945,7 +3136,8 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
     }
 
 #if 1
-    if (level_node->level_group)
+    if (level_node->level_group ||
+       DelayReached(&progress_delay, progress_delay_value))
       DrawInitText(level_node->name, 150, FC_YELLOW);
 #endif