rnd-20031129-1-src
[rocksndiamonds.git] / src / files.c
index dd68c91411386c94261c6076f98995f1ba2d9b73..cb4c6a605a173139931c8203c7f39353b77275a6 100644 (file)
@@ -13,6 +13,8 @@
 
 #include <ctype.h>
 #include <sys/stat.h>
+#include <dirent.h>
+#include <math.h>
 
 #include "libgame/libgame.h"
 
@@ -612,7 +614,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
     {
       Error(ERR_WARN, "invalid custom element number %d", element);
 
-      element = EL_DEFAULT;    /* dummy element used for artwork config */
+      element = EL_DUMMY;
     }
 
     for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
@@ -695,7 +697,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   {
     Error(ERR_WARN, "invalid custom element number %d", element);
 
-    element = EL_DEFAULT;      /* dummy element used for artwork config */
+    element = EL_DUMMY;
   }
 
   ei = &element_info[element];
@@ -1997,8 +1999,8 @@ void LoadTapeFromFilename(char *filename)
   tape.length_seconds = GetTapeLength();
 
 #if 0
-  printf("tape game version: %d\n", tape.game_version);
-  printf("tape engine version: %d\n", tape.engine_version);
+  printf("::: tape game version: %d\n", tape.game_version);
+  printf("::: tape engine version: %d\n", tape.engine_version);
 #endif
 }
 
@@ -2299,8 +2301,9 @@ void SaveScore(int level_nr)
 #define SETUP_TOKEN_EDITOR_EL_CUSTOM           8
 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE      9
 #define SETUP_TOKEN_EDITOR_EL_HEADLINES                10
+#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     11
 
-#define NUM_EDITOR_SETUP_TOKENS                        11
+#define NUM_EDITOR_SETUP_TOKENS                        12
 
 /* shortcut setup */
 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
@@ -2387,6 +2390,7 @@ static struct TokenInfo editor_setup_tokens[] =
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
   { TYPE_SWITCH, &sei.el_custom_more,  "editor.el_custom_more"         },
   { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
+  { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined"        },
 };
 
 static struct TokenInfo shortcut_setup_tokens[] =
@@ -2485,6 +2489,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_custom_more = FALSE;
 
   si->editor.el_headlines = TRUE;
+  si->editor.el_user_defined = FALSE;
 
   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
@@ -2756,3 +2761,500 @@ void LoadSpecialMenuDesignSettings()
 
   freeSetupFileHash(setup_file_hash);
 }
+
+static char *itoa(unsigned int i)
+{
+  static char *a = NULL;
+
+  if (a != NULL)
+    free(a);
+
+  if (i > 2147483647)  /* yes, this is a kludge */
+    i = 2147483647;
+
+  a = checked_malloc(10 + 1);
+
+  sprintf(a, "%d", i);
+
+  return a;
+}
+
+void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
+{
+  char *filename = getEditorSetupFilename();
+  SetupFileList *setup_file_list, *list;
+  SetupFileHash *element_hash;
+  int num_unknown_tokens = 0;
+  int i;
+
+  if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+    return;
+
+  element_hash = newSetupFileHash();
+
+  for (i=0; i < NUM_FILE_ELEMENTS; i++)
+    setHashEntry(element_hash, element_info[i].token_name, itoa(i));
+
+  /* determined size may be larger than needed (due to unknown elements) */
+  *num_elements = 0;
+  for (list = setup_file_list; list != NULL; list = list->next)
+    (*num_elements)++;
+
+  /* add space for up to 3 more elements for padding that may be needed */
+  *num_elements += 3;
+
+  *elements = checked_malloc(*num_elements * sizeof(int));
+
+  *num_elements = 0;
+  for (list = setup_file_list; list != NULL; list = list->next)
+  {
+    char *value = getHashEntry(element_hash, list->token);
+
+    if (value)
+    {
+      (*elements)[(*num_elements)++] = atoi(value);
+    }
+    else
+    {
+      if (num_unknown_tokens == 0)
+      {
+       Error(ERR_RETURN_LINE, "-");
+       Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
+       Error(ERR_RETURN, "- config file: '%s'", filename);
+
+       num_unknown_tokens++;
+      }
+
+      Error(ERR_RETURN, "- token: '%s'", list->token);
+    }
+  }
+
+  if (num_unknown_tokens > 0)
+    Error(ERR_RETURN_LINE, "-");
+
+  while (*num_elements % 4)    /* pad with empty elements, if needed */
+    (*elements)[(*num_elements)++] = EL_EMPTY;
+
+  freeSetupFileList(setup_file_list);
+  freeSetupFileHash(element_hash);
+
+#if 0
+  /* TEST-ONLY */
+  for (i=0; i < *num_elements; i++)
+    printf("editor: element '%s' [%d]\n",
+          element_info[(*elements)[i]].token_name, (*elements)[i]);
+#endif
+}
+
+static struct MusicFileInfo *get_music_file_info(char *basename)
+{
+  SetupFileHash *setup_file_hash = NULL;
+  struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
+  char *filename_music = getCustomMusicFilename(basename);
+  char *filename_prefix, *filename_info;
+  struct
+  {
+    char *token;
+    char **value_ptr;
+  }
+  token_to_value_ptr[] =
+  {
+    { "context",       &tmp_music_file_info.context    },
+    { "title",         &tmp_music_file_info.title      },
+    { "artist",                &tmp_music_file_info.artist     },
+    { "album",         &tmp_music_file_info.album      },
+    { "year",          &tmp_music_file_info.year       },
+    { NULL,            NULL                            },
+  };
+  int i;
+
+  if (filename_music == NULL)
+    return NULL;
+
+  /* ---------- try to replace file extension ---------- */
+
+  filename_prefix = getStringCopy(filename_music);
+  if (strrchr(filename_prefix, '.') != NULL)
+    *strrchr(filename_prefix, '.') = '\0';
+  filename_info = getStringCat2(filename_prefix, ".txt");
+
+#if 0
+  printf("trying to load file '%s'...\n", filename_info);
+#endif
+
+  if (fileExists(filename_info))
+    setup_file_hash = loadSetupFileHash(filename_info);
+
+  free(filename_prefix);
+  free(filename_info);
+
+  if (setup_file_hash == NULL)
+  {
+    /* ---------- try to add file extension ---------- */
+
+    filename_prefix = getStringCopy(filename_music);
+    filename_info = getStringCat2(filename_prefix, ".txt");
+
+#if 0
+    printf("trying to load file '%s'...\n", filename_info);
+#endif
+
+    if (fileExists(filename_info))
+      setup_file_hash = loadSetupFileHash(filename_info);
+
+    free(filename_prefix);
+    free(filename_info);
+  }
+
+  if (setup_file_hash == NULL)
+    return NULL;
+
+  /* ---------- music file info found ---------- */
+
+  for (i = 0; token_to_value_ptr[i].token != NULL; i++)
+  {
+    char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
+
+    *token_to_value_ptr[i].value_ptr = getStringCopy(value);  /* may be NULL */
+  }
+
+  new_music_file_info = checked_calloc(sizeof(struct MusicFileInfo));
+  *new_music_file_info = tmp_music_file_info;
+
+  return new_music_file_info;
+}
+
+void LoadMusicInfo()
+{
+  char *music_directory = getCustomMusicDirectory();
+  int num_music = getMusicListSize();
+  DIR *dir;
+  struct dirent *dir_entry;
+  struct FileInfo *music;
+  struct MusicFileInfo *next, **new;
+  int i;
+
+  while (music_file_info != NULL)
+  {
+    next = music_file_info->next;
+
+    if (music_file_info->context)
+      free(music_file_info->context);
+    if (music_file_info->title)
+      free(music_file_info->title);
+    if (music_file_info->artist)
+      free(music_file_info->artist);
+    if (music_file_info->album)
+      free(music_file_info->album);
+    if (music_file_info->year)
+      free(music_file_info->year);
+
+    free(music_file_info);
+
+    music_file_info = next;
+  }
+
+  new = &music_file_info;
+
+  for (i=0; i < num_music; i++)
+  {
+    music = getMusicListEntry(i);
+
+    if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
+      continue;
+
+#if 0
+    printf("::: -> '%s'\n", music->filename);
+#endif
+
+    *new = get_music_file_info(music->filename);
+    if (*new != NULL)
+      new = &(*new)->next;
+  }
+
+  if ((dir = opendir(music_directory)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+    return;
+  }
+
+  while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
+  {
+    char *basename = dir_entry->d_name;
+    boolean music_already_used = FALSE;
+    int i;
+
+    for (i=0; i < num_music; i++)
+    {
+      music = getMusicListEntry(i);
+
+      if (strcmp(basename, music->filename) == 0)
+      {
+       music_already_used = TRUE;
+       break;
+      }
+    }
+
+    if (music_already_used)
+      continue;
+
+    if (!FileIsSound(basename) && !FileIsMusic(basename))
+      continue;
+
+#if 0
+    printf("::: -> '%s'\n", basename);
+#endif
+
+    *new = get_music_file_info(basename);
+    if (*new != NULL)
+      new = &(*new)->next;
+  }
+
+  closedir(dir);
+
+#if 0
+  /* TEST-ONLY */
+  for (next = music_file_info; next != NULL; next = next->next)
+    printf("::: title == '%s'\n", next->title);
+#endif
+}
+
+void add_demo_anim(int element, int action, int direction, int delay,
+                  int *num_list_entries)
+{
+  struct DemoAnimInfo *new_list_entry;
+  (*num_list_entries)++;
+
+  demo_anim_info =
+    checked_realloc(demo_anim_info,
+                   *num_list_entries * sizeof(struct DemoAnimInfo));
+  new_list_entry = &demo_anim_info[*num_list_entries - 1];
+
+  new_list_entry->element = element;
+  new_list_entry->action = action;
+  new_list_entry->direction = direction;
+  new_list_entry->delay = delay;
+}
+
+void print_unknown_token(char *filename, char *token, int token_nr)
+{
+  if (token_nr == 0)
+  {
+    Error(ERR_RETURN_LINE, "-");
+    Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
+    Error(ERR_RETURN, "- config file: '%s'", filename);
+  }
+
+  Error(ERR_RETURN, "- token: '%s'", token);
+}
+
+void print_unknown_token_end(int token_nr)
+{
+  if (token_nr > 0)
+    Error(ERR_RETURN_LINE, "-");
+}
+
+void LoadDemoAnimInfo()
+{
+  char *filename = getDemoAnimInfoFilename();
+  SetupFileList *setup_file_list, *list;
+  SetupFileHash *element_hash, *action_hash, *direction_hash;
+  int num_list_entries = 0;
+  int num_unknown_tokens = 0;
+  int i;
+
+  if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+  {
+    /* use reliable default values from static configuration */
+    SetupFileList *insert_ptr;
+
+    insert_ptr = setup_file_list =
+      newSetupFileList(demo_anim_info_config[0].token,
+                      demo_anim_info_config[0].value);
+
+    for (i=1; demo_anim_info_config[i].token; i++)
+      insert_ptr = addListEntry(insert_ptr,
+                               demo_anim_info_config[i].token,
+                               demo_anim_info_config[i].value);
+  }
+
+  element_hash   = newSetupFileHash();
+  action_hash    = newSetupFileHash();
+  direction_hash = newSetupFileHash();
+
+  for (i=0; i < MAX_NUM_ELEMENTS; i++)
+    setHashEntry(element_hash, element_info[i].token_name, itoa(i));
+
+  for (i=0; i < NUM_ACTIONS; i++)
+    setHashEntry(action_hash, element_action_info[i].suffix,
+                itoa(element_action_info[i].value));
+
+  /* do not store direction index (bit) here, but direction value! */
+  for (i=0; i < NUM_DIRECTIONS; i++)
+    setHashEntry(direction_hash, element_direction_info[i].suffix,
+                itoa(1 << element_direction_info[i].value));
+
+  for (list = setup_file_list; list != NULL; list = list->next)
+  {
+    char *element_token, *action_token, *direction_token;
+    char *element_value, *action_value, *direction_value;
+    int delay = atoi(list->value);
+
+    if (strcmp(list->token, "end") == 0)
+    {
+      add_demo_anim(-1, -1, -1, -1, &num_list_entries);
+
+      continue;
+    }
+
+    element_token = list->token;
+    element_value = getHashEntry(element_hash, element_token);
+
+    if (element_value != NULL)
+    {
+      /* element found */
+      add_demo_anim(atoi(element_value), -1, -1, delay, &num_list_entries);
+
+      continue;
+    }
+
+    if (strchr(element_token, '.') == NULL)
+    {
+      /* no further suffixes found -- this is not an element */
+      print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+      continue;
+    }
+
+    action_token = strchr(element_token, '.');
+    element_token = getStringCopy(element_token);
+    *strchr(element_token, '.') = '\0';
+
+    element_value = getHashEntry(element_hash, element_token);
+
+    if (element_value == NULL)
+    {
+      /* this is not an element */
+      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      free(element_token);
+
+      continue;
+    }
+
+    action_value = getHashEntry(action_hash, action_token);
+
+    if (action_value != NULL)
+    {
+      /* action found */
+      add_demo_anim(atoi(element_value), atoi(action_value), -1, delay,
+                   &num_list_entries);
+      free(element_token);
+
+      continue;
+    }
+
+    direction_token = action_token;
+    direction_value = getHashEntry(direction_hash, direction_token);
+
+    if (direction_value != NULL)
+    {
+      /* direction found */
+      add_demo_anim(atoi(element_value), -1, atoi(direction_value), delay,
+                   &num_list_entries);
+      free(element_token);
+
+      continue;
+    }
+
+    if (strchr(action_token + 1, '.') == NULL)
+    {
+      /* no further suffixes found -- this is not an action or direction */
+      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      free(element_token);
+
+      continue;
+    }
+
+    direction_token = strchr(action_token + 1, '.');
+    action_token = getStringCopy(action_token);
+    *strchr(action_token + 1, '.') = '\0';
+
+    action_value = getHashEntry(action_hash, action_token);
+
+    if (action_value == NULL)
+    {
+      /* this is not an action */
+      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      free(element_token);
+      free(action_token);
+
+      continue;
+    }
+
+    direction_value = getHashEntry(direction_hash, direction_token);
+
+    if (direction_value != NULL)
+    {
+      /* direction found */
+      add_demo_anim(atoi(element_value), atoi(action_value),
+                   atoi(direction_value), delay, &num_list_entries);
+      free(element_token);
+      free(action_token);
+
+      continue;
+    }
+
+    print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+    free(element_token);
+    free(action_token);
+  }
+
+  print_unknown_token_end(num_unknown_tokens);
+
+  add_demo_anim(-999, -999, -999, -999, &num_list_entries);
+
+  freeSetupFileList(setup_file_list);
+  freeSetupFileHash(element_hash);
+  freeSetupFileHash(action_hash);
+  freeSetupFileHash(direction_hash);
+
+#if 0
+  /* TEST ONLY */
+  for (i=0; i < num_list_entries; i++)
+    printf("::: %d, %d, %d => %d\n",
+          demo_anim_info[i].element,
+          demo_anim_info[i].action,
+          demo_anim_info[i].direction,
+          demo_anim_info[i].delay);
+#endif
+}
+
+void LoadDemoAnimText()
+{
+  char *filename = getDemoAnimTextFilename();
+  int i;
+
+  if (demo_anim_text != NULL)
+    freeSetupFileHash(demo_anim_text);
+
+  if ((demo_anim_text = loadSetupFileHash(filename)) == NULL)
+  {
+    /* use reliable default values from static configuration */
+    demo_anim_text = newSetupFileHash();
+
+    for (i=0; demo_anim_text_config[i].token; i++)
+      setHashEntry(demo_anim_text, demo_anim_text_config[i].token,
+                  demo_anim_text_config[i].value);
+  }
+
+#if 0
+  /* TEST ONLY */
+  BEGIN_HASH_ITERATION(demo_anim_text, itr)
+  {
+    printf("::: '%s' => '%s'\n",
+          HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+  }
+  END_HASH_ITERATION(hash, itr)
+#endif
+}