rocksndiamonds-3.1.0
[rocksndiamonds.git] / src / libgame / setup.c
index 8ba46e0be7e7a4772f5f90f78ab6cd424d2757d8..dc01ad4c9ecdbdcbffec3c9cf9a7117909d9877b 100644 (file)
@@ -97,8 +97,7 @@ static char *getUserLevelDir(char *level_subdir)
   char *data_dir = getUserDataDir();
   char *userlevel_subdir = LEVELS_DIRECTORY;
 
-  if (userlevel_dir)
-    free(userlevel_dir);
+  checked_free(userlevel_dir);
 
   if (level_subdir != NULL)
     userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
@@ -108,31 +107,13 @@ static char *getUserLevelDir(char *level_subdir)
   return userlevel_dir;
 }
 
-static char *getTapeDir(char *level_subdir)
-{
-  static char *tape_dir = NULL;
-  char *data_dir = getUserDataDir();
-  char *tape_subdir = TAPES_DIRECTORY;
-
-  if (tape_dir)
-    free(tape_dir);
-
-  if (level_subdir != NULL)
-    tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
-  else
-    tape_dir = getPath2(data_dir, tape_subdir);
-
-  return tape_dir;
-}
-
 static char *getScoreDir(char *level_subdir)
 {
   static char *score_dir = NULL;
   char *data_dir = getCommonDataDir();
   char *score_subdir = SCORES_DIRECTORY;
 
-  if (score_dir)
-    free(score_dir);
+  checked_free(score_dir);
 
   if (level_subdir != NULL)
     score_dir = getPath3(data_dir, score_subdir, level_subdir);
@@ -148,8 +129,7 @@ static char *getLevelSetupDir(char *level_subdir)
   char *data_dir = getUserDataDir();
   char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
 
-  if (levelsetup_dir)
-    free(levelsetup_dir);
+  checked_free(levelsetup_dir);
 
   if (level_subdir != NULL)
     levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
@@ -166,8 +146,7 @@ static char *getLevelDirFromTreeInfo(TreeInfo *node)
   if (node == NULL)
     return options.level_directory;
 
-  if (level_dir)
-    free(level_dir);
+  checked_free(level_dir);
 
   level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
                        options.level_directory), node->fullpath);
@@ -175,11 +154,40 @@ static char *getLevelDirFromTreeInfo(TreeInfo *node)
   return level_dir;
 }
 
-static char *getCurrentLevelDir()
+char *getCurrentLevelDir()
 {
   return getLevelDirFromTreeInfo(leveldir_current);
 }
 
+static char *getTapeDir(char *level_subdir)
+{
+  static char *tape_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *tape_subdir = TAPES_DIRECTORY;
+
+  checked_free(tape_dir);
+
+  if (level_subdir != NULL)
+    tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
+  else
+    tape_dir = getPath2(data_dir, tape_subdir);
+
+  return tape_dir;
+}
+
+static char *getSolutionTapeDir()
+{
+  static char *tape_dir = NULL;
+  char *data_dir = getCurrentLevelDir();
+  char *tape_subdir = TAPES_DIRECTORY;
+
+  checked_free(tape_dir);
+
+  tape_dir = getPath2(data_dir, tape_subdir);
+
+  return tape_dir;
+}
+
 static char *getDefaultGraphicsDir(char *graphics_subdir)
 {
   static char *graphics_dir = NULL;
@@ -187,8 +195,7 @@ static char *getDefaultGraphicsDir(char *graphics_subdir)
   if (graphics_subdir == NULL)
     return options.graphics_directory;
 
-  if (graphics_dir)
-    free(graphics_dir);
+  checked_free(graphics_dir);
 
   graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
 
@@ -202,8 +209,7 @@ static char *getDefaultSoundsDir(char *sounds_subdir)
   if (sounds_subdir == NULL)
     return options.sounds_directory;
 
-  if (sounds_dir)
-    free(sounds_dir);
+  checked_free(sounds_dir);
 
   sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
 
@@ -217,8 +223,7 @@ static char *getDefaultMusicDir(char *music_subdir)
   if (music_subdir == NULL)
     return options.music_directory;
 
-  if (music_dir)
-    free(music_dir);
+  checked_free(music_dir);
 
   music_dir = getPath2(options.music_directory, music_subdir);
 
@@ -276,8 +281,7 @@ static char *getSetupArtworkDir(TreeInfo *ti)
 {
   static char *artwork_dir = NULL;
 
-  if (artwork_dir != NULL)
-    free(artwork_dir);
+  checked_free(artwork_dir);
 
   artwork_dir = getPath2(ti->basepath, ti->fullpath);
 
@@ -295,8 +299,7 @@ char *setLevelArtworkDir(TreeInfo *ti)
   artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
   artwork_set_ptr  = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
 
-  if (*artwork_path_ptr != NULL)
-    free(*artwork_path_ptr);
+  checked_free(*artwork_path_ptr);
 
   if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
     *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
@@ -311,8 +314,7 @@ char *setLevelArtworkDir(TreeInfo *ti)
 
     char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
 
-    if (*artwork_set_ptr != NULL)
-      free(*artwork_set_ptr);
+    checked_free(*artwork_set_ptr);
 
     if (fileExists(dir))
     {
@@ -347,34 +349,28 @@ inline static char *getLevelArtworkDir(int type)
   return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
 }
 
-char *getLevelFilename(int nr)
+char *getTapeFilename(int nr)
 {
   static char *filename = NULL;
   char basename[MAX_FILENAME_LEN];
 
-  if (filename != NULL)
-    free(filename);
-
-  if (nr < 0)
-    sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
-  else
-    sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
+  checked_free(filename);
 
-  filename = getPath2(getCurrentLevelDir(), basename);
+  sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
+  filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
 
   return filename;
 }
 
-char *getTapeFilename(int nr)
+char *getSolutionTapeFilename(int nr)
 {
   static char *filename = NULL;
   char basename[MAX_FILENAME_LEN];
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
-  filename = getPath2(getTapeDir(leveldir_current->filename), basename);
+  filename = getPath2(getSolutionTapeDir(), basename);
 
   return filename;
 }
@@ -384,11 +380,10 @@ char *getScoreFilename(int nr)
   static char *filename = NULL;
   char basename[MAX_FILENAME_LEN];
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
-  filename = getPath2(getScoreDir(leveldir_current->filename), basename);
+  filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
 
   return filename;
 }
@@ -397,14 +392,75 @@ char *getSetupFilename()
 {
   static char *filename = NULL;
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   filename = getPath2(getSetupDir(), SETUP_FILENAME);
 
   return filename;
 }
 
+char *getEditorSetupFilename()
+{
+  static char *filename = NULL;
+
+  checked_free(filename);
+
+  filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
+
+  return filename;
+}
+
+char *getHelpAnimFilename()
+{
+  static char *filename = NULL;
+
+  checked_free(filename);
+
+  filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
+
+  return filename;
+}
+
+char *getHelpTextFilename()
+{
+  static char *filename = NULL;
+
+  checked_free(filename);
+
+  filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
+
+  return filename;
+}
+
+char *getLevelSetInfoFilename()
+{
+  static char *filename = NULL;
+  char *basenames[] =
+  {
+    "README",
+    "README.txt",
+    "README.TXT",
+    "Readme",
+    "Readme.txt",
+    "readme",
+    "readme.txt",
+
+    NULL
+  };
+  int i;
+
+  for (i = 0; basenames[i] != NULL; i++)
+  {
+    checked_free(filename);
+
+    filename = getPath2(getCurrentLevelDir(), basenames[i]);
+    if (fileExists(filename))
+      return filename;
+  }
+
+  return NULL;
+}
+
 static char *getCorrectedArtworkBasename(char *basename)
 {
   char *basename_corrected = basename;
@@ -424,8 +480,7 @@ static char *getCorrectedArtworkBasename(char *basename)
     {
       static char *msdos_filename = NULL;
 
-      if (msdos_filename != NULL)
-       free(msdos_filename);
+      checked_free(msdos_filename);
 
       msdos_filename = getStringCopy(basename_corrected);
       strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
@@ -443,8 +498,7 @@ char *getCustomImageFilename(char *basename)
   static char *filename = NULL;
   boolean skip_setup_artwork = FALSE;
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   basename = getCorrectedArtworkBasename(basename);
 
@@ -502,8 +556,7 @@ char *getCustomSoundFilename(char *basename)
   static char *filename = NULL;
   boolean skip_setup_artwork = FALSE;
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   basename = getCorrectedArtworkBasename(basename);
 
@@ -561,8 +614,7 @@ char *getCustomMusicFilename(char *basename)
   static char *filename = NULL;
   boolean skip_setup_artwork = FALSE;
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   basename = getCorrectedArtworkBasename(basename);
 
@@ -636,8 +688,7 @@ char *getCustomArtworkLevelConfigFilename(int type)
 {
   static char *filename = NULL;
 
-  if (filename != NULL)
-    free(filename);
+  checked_free(filename);
 
   filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
 
@@ -649,8 +700,7 @@ char *getCustomMusicDirectory(void)
   static char *directory = NULL;
   boolean skip_setup_artwork = FALSE;
 
-  if (directory != NULL)
-    free(directory);
+  checked_free(directory);
 
   if (!setup.override_level_music)
   {
@@ -874,15 +924,15 @@ void dumpTreeInfo(TreeInfo *node, int depth)
 
   while (node)
   {
-    for (i=0; i<(depth + 1) * 3; i++)
+    for (i = 0; i < (depth + 1) * 3; i++)
       printf(" ");
 
 #if 1
-    printf("filename == '%s' ['%s', '%s'] [%d])\n",
-          node->filename, node->fullpath, node->basepath, node->user_defined);
+    printf("subdir == '%s' ['%s', '%s'] [%d])\n",
+          node->subdir, node->fullpath, node->basepath, node->user_defined);
 #else
-    printf("filename == '%s' (%s) [%s] (%d)\n",
-          node->filename, node->name, node->identifier, node->sort_priority);
+    printf("subdir == '%s' (%s) [%s] (%d)\n",
+          node->subdir, node->name, node->identifier, node->sort_priority);
 #endif
 
     if (node->node_group != NULL)
@@ -920,7 +970,7 @@ void sortTreeInfo(TreeInfo **node_first,
        compare_function);
 
   /* update the linkage of list elements with the sorted node array */
-  for (i=0; i<num_nodes - 1; i++)
+  for (i = 0; i < num_nodes - 1; i++)
     sort_array[i]->next = sort_array[i + 1];
   sort_array[num_nodes - 1]->next = NULL;
 
@@ -1142,9 +1192,13 @@ char *getFormattedSetupEntry(char *token, char *value)
   int i;
   static char entry[MAX_LINE_LEN];
 
+  /* if value is an empty string, just return token without value */
+  if (*value == '\0')
+    return token;
+
   /* start with the token and some spaces to format output line */
   sprintf(entry, "%s:", token);
-  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
+  for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
     strcat(entry, " ");
 
   /* continue with the token's value */
@@ -1170,12 +1224,12 @@ void freeSetupFileList(SetupFileList *list)
   if (list == NULL)
     return;
 
-  if (list->token)
-    free(list->token);
-  if (list->value)
-    free(list->value);
+  checked_free(list->token);
+  checked_free(list->value);
+
   if (list->next)
     freeSetupFileList(list->next);
+
   free(list);
 }
 
@@ -1197,8 +1251,7 @@ SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
 
   if (strcmp(list->token, token) == 0)
   {
-    if (list->value)
-      free(list->value);
+    checked_free(list->value);
 
     list->value = getStringCopy(value);
 
@@ -1210,6 +1263,17 @@ SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
     return setListEntry(list->next, token, value);
 }
 
+SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
+{
+  if (list == NULL)
+    return NULL;
+
+  if (list->next == NULL)
+    return (list->next = newSetupFileList(token, value));
+  else
+    return addListEntry(list->next, token, value);
+}
+
 #ifdef DEBUG
 static void printSetupFileList(SetupFileList *list)
 {
@@ -1275,6 +1339,9 @@ SetupFileHash *newSetupFileHash()
   SetupFileHash *new_hash =
     create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
 
+  if (new_hash == NULL)
+    Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
+
   return new_hash;
 }
 
@@ -1309,6 +1376,14 @@ void setHashEntry(SetupFileHash *hash, char *token, char *value)
       Error(ERR_EXIT, "cannot insert into hash -- aborting");
 }
 
+char *removeHashEntry(SetupFileHash *hash, char *token)
+{
+  if (hash == NULL)
+    return NULL;
+
+  return remove_hash_entry(hash, token);
+}
+
 #if 0
 #ifdef DEBUG
 static void printSetupFileHash(SetupFileHash *hash)
@@ -1325,10 +1400,10 @@ static void printSetupFileHash(SetupFileHash *hash)
 
 static void *loadSetupFileData(char *filename, boolean use_hash)
 {
-  int line_len;
-  char line[MAX_LINE_LEN];
+  char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
   char *token, *value, *line_ptr;
   void *setup_file_data, *insert_ptr = NULL;
+  boolean read_continued_line = FALSE;
   FILE *file;
 
   if (use_hash)
@@ -1342,16 +1417,48 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
     return NULL;
   }
 
-  while(!feof(file))
+  while (!feof(file))
   {
     /* read next line of input file */
     if (!fgets(line, MAX_LINE_LEN, file))
       break;
 
-    /* cut trailing comment or whitespace from input line */
+    /* cut trailing newline 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';
+
+    if (read_continued_line)
+    {
+      /* cut leading whitespaces from input line */
+      for (line_ptr = line; *line_ptr; line_ptr++)
+       if (*line_ptr != ' ' && *line_ptr != '\t')
+         break;
+
+      /* append new line to existing line, if there is enough space */
+      if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
+       strcat(previous_line, line_ptr);
+
+      strcpy(line, previous_line);     /* copy storage buffer to line */
+
+      read_continued_line = FALSE;
+    }
+
+    /* if the last character is '\', continue at next line */
+    if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
+    {
+      line[strlen(line) - 1] = '\0';   /* cut off trailing backslash */
+      strcpy(previous_line, line);     /* copy line to storage buffer */
+
+      read_continued_line = TRUE;
+
+      continue;
+    }
+
+    /* cut trailing comment from input line */
     for (line_ptr = line; *line_ptr; line_ptr++)
     {
-      if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
+      if (*line_ptr == '#')
       {
        *line_ptr = '\0';
        break;
@@ -1359,47 +1466,50 @@ static void *loadSetupFileData(char *filename, boolean use_hash)
     }
 
     /* cut trailing whitespaces from input line */
-    for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
-      if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
+    for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
+      if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
        *line_ptr = '\0';
 
     /* ignore empty lines */
     if (*line == '\0')
       continue;
 
-    line_len = strlen(line);
-
     /* cut leading whitespaces from token */
     for (token = line; *token; token++)
       if (*token != ' ' && *token != '\t')
        break;
 
-    /* find end of token */
+    /* start with empty value as reliable default */
+    value = "";
+
+    /* find end of token to determine start of value */
     for (line_ptr = token; *line_ptr; line_ptr++)
     {
       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
       {
-       *line_ptr = '\0';
+       *line_ptr = '\0';               /* terminate token string */
+       value = line_ptr + 1;           /* set beginning of value */
+
        break;
       }
     }
 
-    if (line_ptr < line + line_len)
-      value = line_ptr + 1;
-    else
-      value = "\0";
-
     /* cut leading whitespaces from value */
     for (; *value; value++)
       if (*value != ' ' && *value != '\t')
        break;
 
-    if (*token && *value)
+#if 0
+    if (*value == '\0')
+      value = "true";  /* treat tokens without value as "true" */
+#endif
+
+    if (*token)
     {
       if (use_hash)
        setHashEntry((SetupFileHash *)setup_file_data, token, value);
       else
-       insert_ptr = setListEntry((SetupFileList *)insert_ptr, token, value);
+       insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
     }
   }
 
@@ -1472,28 +1582,34 @@ void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
 #define LEVELINFO_TOKEN_GRAPHICS_SET   11
 #define LEVELINFO_TOKEN_SOUNDS_SET     12
 #define LEVELINFO_TOKEN_MUSIC_SET      13
+#define LEVELINFO_TOKEN_FILENAME       14
+#define LEVELINFO_TOKEN_FILETYPE       15
+#define LEVELINFO_TOKEN_HANDICAP       16
 
-#define NUM_LEVELINFO_TOKENS           14
+#define NUM_LEVELINFO_TOKENS           17
 
 static LevelDirTree ldi;
 
 static struct TokenInfo levelinfo_tokens[] =
 {
   /* level directory info */
-  { TYPE_STRING,  &ldi.identifier,     "identifier"    },
-  { TYPE_STRING,  &ldi.name,           "name"          },
-  { TYPE_STRING,  &ldi.name_sorting,   "name_sorting"  },
-  { TYPE_STRING,  &ldi.author,         "author"        },
-  { TYPE_STRING,  &ldi.imported_from,  "imported_from" },
-  { TYPE_INTEGER, &ldi.levels,         "levels"        },
-  { TYPE_INTEGER, &ldi.first_level,    "first_level"   },
-  { TYPE_INTEGER, &ldi.sort_priority,  "sort_priority" },
-  { TYPE_BOOLEAN, &ldi.latest_engine,  "latest_engine" },
-  { TYPE_BOOLEAN, &ldi.level_group,    "level_group"   },
-  { TYPE_BOOLEAN, &ldi.readonly,       "readonly"      },
-  { TYPE_STRING,  &ldi.graphics_set,   "graphics_set"  },
-  { TYPE_STRING,  &ldi.sounds_set,     "sounds_set"    },
-  { TYPE_STRING,  &ldi.music_set,      "music_set"     }
+  { TYPE_STRING,       &ldi.identifier,        "identifier"    },
+  { TYPE_STRING,       &ldi.name,              "name"          },
+  { TYPE_STRING,       &ldi.name_sorting,      "name_sorting"  },
+  { TYPE_STRING,       &ldi.author,            "author"        },
+  { TYPE_STRING,       &ldi.imported_from,     "imported_from" },
+  { TYPE_INTEGER,      &ldi.levels,            "levels"        },
+  { TYPE_INTEGER,      &ldi.first_level,       "first_level"   },
+  { TYPE_INTEGER,      &ldi.sort_priority,     "sort_priority" },
+  { TYPE_BOOLEAN,      &ldi.latest_engine,     "latest_engine" },
+  { TYPE_BOOLEAN,      &ldi.level_group,       "level_group"   },
+  { TYPE_BOOLEAN,      &ldi.readonly,          "readonly"      },
+  { TYPE_STRING,       &ldi.graphics_set,      "graphics_set"  },
+  { TYPE_STRING,       &ldi.sounds_set,        "sounds_set"    },
+  { TYPE_STRING,       &ldi.music_set,         "music_set"     },
+  { TYPE_STRING,       &ldi.level_filename,    "filename"      },
+  { TYPE_STRING,       &ldi.level_filetype,    "filetype"      },
+  { TYPE_BOOLEAN,      &ldi.handicap,          "handicap"      }
 };
 
 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
@@ -1513,7 +1629,7 @@ static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
   ldi->cl_first = -1;
   ldi->cl_cursor = -1;
 
-  ldi->filename = NULL;
+  ldi->subdir = NULL;
   ldi->fullpath = NULL;
   ldi->basepath = NULL;
   ldi->identifier = NULL;
@@ -1539,12 +1655,16 @@ static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
     ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
     ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
 
+    ldi->level_filename = NULL;
+    ldi->level_filetype = NULL;
+
     ldi->levels = 0;
     ldi->first_level = 0;
     ldi->last_level = 0;
     ldi->level_group = FALSE;
     ldi->handicap_level = 0;
     ldi->readonly = TRUE;
+    ldi->handicap = TRUE;
   }
 }
 
@@ -1572,7 +1692,7 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
   ldi->cl_first = -1;
   ldi->cl_cursor = -1;
 
-  ldi->filename = NULL;
+  ldi->subdir = NULL;
   ldi->fullpath = NULL;
   ldi->basepath = NULL;
   ldi->identifier = NULL;
@@ -1598,15 +1718,18 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
     ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
     ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
 
+    ldi->level_filename = NULL;
+    ldi->level_filetype = NULL;
+
     ldi->levels = 0;
     ldi->first_level = 0;
     ldi->last_level = 0;
     ldi->level_group = FALSE;
     ldi->handicap_level = 0;
     ldi->readonly = TRUE;
+    ldi->handicap = TRUE;
   }
 
-
 #else
 
   /* first copy all values from the parent structure ... */
@@ -1619,7 +1742,7 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
      other structure's pointer may point to them!
   */
 
-  ldi->filename = NULL;
+  ldi->subdir = NULL;
   ldi->fullpath = NULL;
   ldi->basepath = NULL;
   ldi->identifier = NULL;
@@ -1650,40 +1773,31 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
 
 static void freeTreeInfo(TreeInfo *ldi)
 {
-  if (ldi->filename)
-    free(ldi->filename);
-  if (ldi->fullpath)
-    free(ldi->fullpath);
-  if (ldi->basepath)
-    free(ldi->basepath);
-  if (ldi->identifier)
-    free(ldi->identifier);
-
-  if (ldi->name)
-    free(ldi->name);
-  if (ldi->name_sorting)
-    free(ldi->name_sorting);
-  if (ldi->author)
-    free(ldi->author);
-
-  if (ldi->class_desc)
-    free(ldi->class_desc);
+  checked_free(ldi->subdir);
+  checked_free(ldi->fullpath);
+  checked_free(ldi->basepath);
+  checked_free(ldi->identifier);
+
+  checked_free(ldi->name);
+  checked_free(ldi->name_sorting);
+  checked_free(ldi->author);
+
+  checked_free(ldi->class_desc);
 
   if (ldi->type == TREE_TYPE_LEVEL_DIR)
   {
-    if (ldi->graphics_set)
-      free(ldi->graphics_set);
-    if (ldi->sounds_set)
-      free(ldi->sounds_set);
-    if (ldi->music_set)
-      free(ldi->music_set);
-
-    if (ldi->graphics_path)
-      free(ldi->graphics_path);
-    if (ldi->sounds_path)
-      free(ldi->sounds_path);
-    if (ldi->music_path)
-      free(ldi->music_path);
+    checked_free(ldi->imported_from);
+
+    checked_free(ldi->graphics_set);
+    checked_free(ldi->sounds_set);
+    checked_free(ldi->music_set);
+
+    checked_free(ldi->graphics_path);
+    checked_free(ldi->sounds_path);
+    checked_free(ldi->music_path);
+
+    checked_free(ldi->level_filename);
+    checked_free(ldi->level_filetype);
   }
 }
 
@@ -1717,8 +1831,7 @@ void setSetupInfo(struct TokenInfo *token_info,
       break;
 
     case TYPE_STRING:
-      if (*(char **)setup_value != NULL)
-       free(*(char **)setup_value);
+      checked_free(*(char **)setup_value);
       *(char **)setup_value = getStringCopy(token_value);
       break;
 
@@ -1783,7 +1896,7 @@ static void createParentTreeInfoNode(TreeInfo *node_parent)
   setString(&ti_new->name, ".. (parent directory)");
   setString(&ti_new->name_sorting, ti_new->name);
 
-  setString(&ti_new->filename, "..");
+  setString(&ti_new->subdir, "..");
   setString(&ti_new->fullpath, node_parent->fullpath);
 
   ti_new->sort_priority = node_parent->sort_priority;
@@ -1795,7 +1908,7 @@ static void createParentTreeInfoNode(TreeInfo *node_parent)
   ti_new->name = ".. (parent directory)";
   ti_new->name_sorting = getStringCopy(ti_new->name);
 
-  ti_new->filename = "..";
+  ti_new->subdir = "..";
   ti_new->fullpath = getStringCopy(node_parent->fullpath);
 
   ti_new->sort_priority = node_parent->sort_priority;
@@ -1838,32 +1951,32 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
   else
     setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
 
-  leveldir_new->filename = getStringCopy(directory_name);
+  leveldir_new->subdir = getStringCopy(directory_name);
 
   checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
 
   /* set all structure fields according to the token/value pairs */
   ldi = *leveldir_new;
-  for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+  for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
     setSetupInfo(levelinfo_tokens, i,
                 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
   *leveldir_new = ldi;
 
 #if 1
   if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
-    setString(&leveldir_new->name, leveldir_new->filename);
+    setString(&leveldir_new->name, leveldir_new->subdir);
 #else
   if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
   {
     free(leveldir_new->name);
-    leveldir_new->name = getStringCopy(leveldir_new->filename);
+    leveldir_new->name = getStringCopy(leveldir_new->subdir);
   }
 #endif
 
   DrawInitText(leveldir_new->name, 150, FC_YELLOW);
 
   if (leveldir_new->identifier == NULL)
-    leveldir_new->identifier = getStringCopy(leveldir_new->filename);
+    leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
 
   if (leveldir_new->name_sorting == NULL)
     leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
@@ -1871,7 +1984,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
   if (node_parent == NULL)             /* top level group */
   {
     leveldir_new->basepath = getStringCopy(level_directory);
-    leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
+    leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
   }
   else                                 /* sub level group */
   {
@@ -1901,9 +2014,8 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
 #endif
 
   leveldir_new->handicap_level =       /* set handicap to default value */
-    (leveldir_new->user_defined ?
-     leveldir_new->last_level :
-     leveldir_new->first_level);
+    (leveldir_new->user_defined || !leveldir_new->handicap ?
+     leveldir_new->last_level : leveldir_new->first_level);
 
   pushTreeInfo(node_first, leveldir_new);
 
@@ -2064,7 +2176,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
   else
     setTreeInfoToDefaults(artwork_new, type);
 
-  artwork_new->filename = getStringCopy(directory_name);
+  artwork_new->subdir = getStringCopy(directory_name);
 
   if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
   {
@@ -2074,19 +2186,19 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
 
     /* set all structure fields according to the token/value pairs */
     ldi = *artwork_new;
-    for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+    for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
       setSetupInfo(levelinfo_tokens, i,
                   getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
     *artwork_new = ldi;
 
 #if 1
     if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
-      setString(&artwork_new->name, artwork_new->filename);
+      setString(&artwork_new->name, artwork_new->subdir);
 #else
     if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
     {
       free(artwork_new->name);
-      artwork_new->name = getStringCopy(artwork_new->filename);
+      artwork_new->name = getStringCopy(artwork_new->subdir);
     }
 #endif
 
@@ -2095,7 +2207,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
 #endif
 
     if (artwork_new->identifier == NULL)
-      artwork_new->identifier = getStringCopy(artwork_new->filename);
+      artwork_new->identifier = getStringCopy(artwork_new->subdir);
 
     if (artwork_new->name_sorting == NULL)
       artwork_new->name_sorting = getStringCopy(artwork_new->name);
@@ -2104,7 +2216,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
   if (node_parent == NULL)             /* top level group */
   {
     artwork_new->basepath = getStringCopy(base_directory);
-    artwork_new->fullpath = getStringCopy(artwork_new->filename);
+    artwork_new->fullpath = getStringCopy(artwork_new->subdir);
   }
   else                                 /* sub level group */
   {
@@ -2146,7 +2258,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
     }
 #endif
 
-    if (strcmp(artwork_new->filename, ".") == 0)
+    if (strcmp(artwork_new->subdir, ".") == 0)
     {
       if (artwork_new->user_defined)
       {
@@ -2179,9 +2291,9 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
     else
     {
 #if 1
-      setString(&artwork_new->identifier, artwork_new->filename);
+      setString(&artwork_new->identifier, artwork_new->subdir);
 #else
-      artwork_new->identifier = getStringCopy(artwork_new->filename);
+      artwork_new->identifier = getStringCopy(artwork_new->subdir);
 #endif
     }
 
@@ -2270,7 +2382,7 @@ static TreeInfo *getDummyArtworkInfo(int type)
   setTreeInfoToDefaults(artwork_new, type);
 
 #if 1
-  setString(&artwork_new->filename, UNDEFINED_FILENAME);
+  setString(&artwork_new->subdir,   UNDEFINED_FILENAME);
   setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
   setString(&artwork_new->basepath, UNDEFINED_FILENAME);
 
@@ -2278,12 +2390,11 @@ static TreeInfo *getDummyArtworkInfo(int type)
   setString(&artwork_new->name,         UNDEFINED_FILENAME);
   setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
 #else
-  artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
+  artwork_new->subdir   = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
 
-  if (artwork_new->name != NULL)
-    free(artwork_new->name);
+  checked_free(artwork_new->name);
 
   artwork_new->identifier   = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->name         = getStringCopy(UNDEFINED_FILENAME);
@@ -2384,7 +2495,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
 #if 0
     if (!level_node->parent_link)
       printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
-            level_node->filename, level_node->name);
+            level_node->subdir, level_node->name);
 #endif
 
     if (!level_node->parent_link)
@@ -2400,7 +2511,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
        free((*artwork_node)->name);
        free((*artwork_node)->name_sorting);
 
-       (*artwork_node)->identifier   = getStringCopy(level_node->filename);
+       (*artwork_node)->identifier   = getStringCopy(level_node->subdir);
        (*artwork_node)->name         = getStringCopy(level_node->name);
        (*artwork_node)->name_sorting = getStringCopy(level_node->name);
 
@@ -2519,10 +2630,12 @@ static void SaveUserLevelInfo()
                                                 getCookie("LEVELINFO")));
 
   ldi = *level_info;
-  for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+  for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
     if (i != LEVELINFO_TOKEN_IDENTIFIER &&
        i != LEVELINFO_TOKEN_NAME_SORTING &&
-       i != LEVELINFO_TOKEN_IMPORTED_FROM)
+       i != LEVELINFO_TOKEN_IMPORTED_FROM &&
+       i != LEVELINFO_TOKEN_FILENAME &&
+       i != LEVELINFO_TOKEN_FILETYPE)
       fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
 
   fclose(file);
@@ -2605,7 +2718,7 @@ char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
     {
       /* add at least one whitespace */
       strcat(line, " ");
-      for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
+      for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
        strcat(line, " ");
 
       strcat(line, "# ");
@@ -2618,17 +2731,15 @@ char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
 
 void LoadLevelSetup_LastSeries()
 {
-  char *filename;
-  SetupFileHash *level_setup_hash = NULL;
-
-  /* always start with reliable default values */
-  leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
-
   /* ----------------------------------------------------------------------- */
   /* ~/.<program>/levelsetup.conf                                            */
   /* ----------------------------------------------------------------------- */
 
-  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+  char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+  SetupFileHash *level_setup_hash = NULL;
+
+  /* always start with reliable default values */
+  leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
 
   if ((level_setup_hash = loadSetupFileHash(filename)))
   {
@@ -2652,17 +2763,15 @@ void LoadLevelSetup_LastSeries()
 
 void SaveLevelSetup_LastSeries()
 {
-  char *filename;
-  char *level_subdir = leveldir_current->filename;
-  FILE *file;
-
   /* ----------------------------------------------------------------------- */
   /* ~/.<program>/levelsetup.conf                                            */
   /* ----------------------------------------------------------------------- */
 
-  InitUserDataDirectory();
+  char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+  char *level_subdir = leveldir_current->subdir;
+  FILE *file;
 
-  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+  InitUserDataDirectory();
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
@@ -2738,7 +2847,7 @@ void LoadLevelSetup_SeriesInfo()
 {
   char *filename;
   SetupFileHash *level_setup_hash = NULL;
-  char *level_subdir = leveldir_current->filename;
+  char *level_subdir = leveldir_current->subdir;
 
   /* always start with reliable default values */
   level_nr = leveldir_current->first_level;
@@ -2749,7 +2858,7 @@ void LoadLevelSetup_SeriesInfo()
   /* ~/.<program>/levelsetup/<level series>/levelsetup.conf                  */
   /* ----------------------------------------------------------------------- */
 
-  level_subdir = leveldir_current->filename;
+  level_subdir = leveldir_current->subdir;
 
   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
 
@@ -2780,7 +2889,7 @@ void LoadLevelSetup_SeriesInfo()
       if (level_nr > leveldir_current->last_level + 1)
        level_nr = leveldir_current->last_level;
 
-      if (leveldir_current->user_defined)
+      if (leveldir_current->user_defined || !leveldir_current->handicap)
        level_nr = leveldir_current->last_level;
 
       leveldir_current->handicap_level = level_nr;
@@ -2799,7 +2908,7 @@ void LoadLevelSetup_SeriesInfo()
 void SaveLevelSetup_SeriesInfo()
 {
   char *filename;
-  char *level_subdir = leveldir_current->filename;
+  char *level_subdir = leveldir_current->subdir;
   char *level_nr_str = int2str(level_nr, 0);
   char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
   FILE *file;