set default position for snapshot buttons (for old level set compatibility)
[rocksndiamonds.git] / src / files.c
index 2e58ad30240e469571637a99b075a5267d87dd4d..9704cdfd5b294f1a7296522fac3ca6e9f8b6f999 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* files.c                                                  *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// files.c
+// ============================================================================
 
 #include <ctype.h>
 #include <sys/stat.h>
@@ -23,6 +21,9 @@
 #include "tools.h"
 #include "tape.h"
 
+#define ENABLE_UNUSED_CODE     0       /* currently unused functions */
+#define ENABLE_HISTORIC_CHUNKS 0       /* only for historic reference */
+#define ENABLE_RESERVED_CODE   0       /* reserved for later use */
 
 #define CHUNK_ID_LEN           4       /* IFF style chunk id length  */
 #define CHUNK_SIZE_UNDEFINED   0       /* undefined chunk size == 0  */
@@ -869,8 +870,8 @@ static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
     &yy_ei.properties[EP_BITFIELD_BASE_NR]
   },
-#if 0
-  /* (reserved) */
+#if ENABLE_RESERVED_CODE
+  /* (reserved for later use) */
   {
     -1,                                        -1,
     TYPE_BITFIELD,                     CONF_VALUE_32_BIT(2),
@@ -1322,13 +1323,6 @@ filetype_id_list[] =
 
 static boolean check_special_flags(char *flag)
 {
-#if 0
-  printf("::: '%s', '%s', '%s'\n",
-        flag,
-        options.special_flags,
-        leveldir_current->special_flags);
-#endif
-
   if (strEqual(options.special_flags, flag) ||
       strEqual(leveldir_current->special_flags, flag))
     return TRUE;
@@ -1577,11 +1571,6 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 {
   xx_change = *change;         /* copy change data into temporary buffer */
 
-#if 0
-  /* (not needed; set by setConfigToDefaultsFromConfigList()) */
-  xx_num_contents = 1;
-#endif
-
   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
 
   *change = xx_change;
@@ -1596,18 +1585,12 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->post_change_function = NULL;
 }
 
-static void setLevelInfoToDefaults(struct LevelInfo *level)
+static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
 {
-  static boolean clipboard_elements_initialized = FALSE;
   int i, x, y;
 
-  InitElementPropertiesStatic();
-
   li = *level;         /* copy level data into temporary buffer */
-
   setConfigToDefaultsFromConfigList(chunk_config_INFO);
-  setConfigToDefaultsFromConfigList(chunk_config_ELEM);
-
   *level = li;         /* copy temporary buffer back to level data */
 
   setLevelInfoToDefaults_EM();
@@ -1625,21 +1608,74 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   level->encoding_16bit_yamyam = TRUE;
   level->encoding_16bit_amoeba = TRUE;
 
-  for (x = 0; x < MAX_LEV_FIELDX; x++)
-    for (y = 0; y < MAX_LEV_FIELDY; y++)
-      level->field[x][y] = EL_SAND;
-
+  /* clear level name and level author string buffers */
   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
     level->name[i] = '\0';
   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
     level->author[i] = '\0';
 
+  /* set level name and level author to default values */
   strcpy(level->name, NAMELESS_LEVEL_NAME);
   strcpy(level->author, ANONYMOUS_NAME);
 
+  /* set level playfield to playable default level with player and exit */
+  for (x = 0; x < MAX_LEV_FIELDX; x++)
+    for (y = 0; y < MAX_LEV_FIELDY; y++)
+      level->field[x][y] = EL_SAND;
+
   level->field[0][0] = EL_PLAYER_1;
   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
 
+  BorderElement = EL_STEELWALL;
+
+  /* set all bug compatibility flags to "false" => do not emulate this bug */
+  level->use_action_after_change_bug = FALSE;
+
+  if (leveldir_current)
+  {
+    /* try to determine better author name than 'anonymous' */
+    if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
+    {
+      strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
+      level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+    }
+    else
+    {
+      switch (LEVELCLASS(leveldir_current))
+      {
+       case LEVELCLASS_TUTORIAL:
+         strcpy(level->author, PROGRAM_AUTHOR_STRING);
+         break;
+
+        case LEVELCLASS_CONTRIB:
+         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
+         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+         break;
+
+        case LEVELCLASS_PRIVATE:
+         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
+         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+         break;
+
+        default:
+         /* keep default value */
+         break;
+      }
+    }
+  }
+}
+
+static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
+{
+  static boolean clipboard_elements_initialized = FALSE;
+  int i;
+
+  InitElementPropertiesStatic();
+
+  li = *level;         /* copy level data into temporary buffer */
+  setConfigToDefaultsFromConfigList(chunk_config_ELEM);
+  *level = li;         /* copy temporary buffer back to level data */
+
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     int element = i;
@@ -1721,48 +1757,19 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   }
 
   clipboard_elements_initialized = TRUE;
+}
 
-  BorderElement = EL_STEELWALL;
+static void setLevelInfoToDefaults(struct LevelInfo *level,
+                                  boolean level_info_only)
+{
+  setLevelInfoToDefaults_Level(level);
+
+  if (!level_info_only)
+    setLevelInfoToDefaults_Elements(level);
 
   level->no_valid_file = FALSE;
 
   level->changed = FALSE;
-
-  /* set all bug compatibility flags to "false" => do not emulate this bug */
-  level->use_action_after_change_bug = FALSE;
-
-  if (leveldir_current == NULL)                /* only when dumping level */
-    return;
-
-  /* try to determine better author name than 'anonymous' */
-  if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
-  {
-    strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
-    level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
-  }
-  else
-  {
-    switch (LEVELCLASS(leveldir_current))
-    {
-      case LEVELCLASS_TUTORIAL:
-       strcpy(level->author, PROGRAM_AUTHOR_STRING);
-       break;
-
-      case LEVELCLASS_CONTRIB:
-       strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
-       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
-       break;
-
-      case LEVELCLASS_PRIVATE:
-       strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
-       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
-       break;
-
-      default:
-       /* keep default value */
-       break;
-    }
-  }
 }
 
 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
@@ -1837,14 +1844,8 @@ static int getFileTypeFromBasename(char *basename)
   /* ---------- try to determine file type from filename ---------- */
 
   /* check for typical filename of a Supaplex level package file */
-#if 1
   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
     return LEVEL_FILE_TYPE_SP;
-#else
-  if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
-                                strncmp(basename, "LEVELS.D", 8) == 0))
-    return LEVEL_FILE_TYPE_SP;
-#endif
 
   /* check for typical filename of a Diamond Caves II level package file */
   if (strSuffixLower(basename, ".dc") ||
@@ -1900,21 +1901,21 @@ static char *getPackedLevelBasename(int type)
 {
   static char basename[MAX_FILENAME_LEN];
   char *directory = getCurrentLevelDir();
-  DIR *dir;
-  struct dirent *dir_entry;
+  Directory *dir;
+  DirectoryEntry *dir_entry;
 
   strcpy(basename, UNDEFINED_FILENAME);                /* default: undefined file */
 
-  if ((dir = opendir(directory)) == NULL)
+  if ((dir = openDirectory(directory)) == NULL)
   {
     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
 
     return basename;
   }
 
-  while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
+  while ((dir_entry = readDirectory(dir)) != NULL)     /* loop all entries */
   {
-    char *entry_basename = dir_entry->d_name;
+    char *entry_basename = dir_entry->basename;
     int entry_type = getFileTypeFromBasename(entry_basename);
 
     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
@@ -1929,7 +1930,7 @@ static char *getPackedLevelBasename(int type)
     }
   }
 
-  closedir(dir);
+  closeDirectory(dir);
 
   return basename;
 }
@@ -1939,7 +1940,7 @@ static char *getSingleLevelFilename(int nr)
   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
 }
 
-#if 0
+#if ENABLE_UNUSED_CODE
 static char *getPackedLevelFilename(int type)
 {
   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
@@ -1951,7 +1952,7 @@ char *getDefaultLevelFilename(int nr)
   return getSingleLevelFilename(nr);
 }
 
-#if 0
+#if ENABLE_UNUSED_CODE
 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
                                                 int type)
 {
@@ -2023,7 +2024,6 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
   /* special case: level number is negative => check for level template file */
   if (nr < 0)
   {
-#if 1
     /* global variable "leveldir_current" must be modified in the loop below */
     LevelDirTree *leveldir_current_last = leveldir_current;
 
@@ -2043,13 +2043,6 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
     /* restore global variable "leveldir_current" modified in above loop */
     leveldir_current = leveldir_current_last;
 
-#else
-
-    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
-                                        "template.%s", LEVELFILE_EXTENSION);
-
-#endif
-
     /* no fallback if template file not existing */
     return;
   }
@@ -2213,7 +2206,7 @@ int getMappedElementByVersion(int element, int game_version)
   return element;
 }
 
-static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
 {
   level->file_version = getFileVersion(file);
   level->game_version = getFileVersion(file);
@@ -2221,7 +2214,7 @@ static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
 {
   level->creation_date.year  = getFile16BitBE(file);
   level->creation_date.month = getFile8Bit(file);
@@ -2232,7 +2225,7 @@ static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
 {
   int initial_player_stepsize;
   int initial_player_gravity;
@@ -2297,7 +2290,7 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
 {
   int i;
 
@@ -2308,7 +2301,7 @@ static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
 {
   int i;
 
@@ -2319,7 +2312,7 @@ static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
 {
   int x, y;
   int chunk_size_expected = level->fieldx * level->fieldy;
@@ -2346,7 +2339,7 @@ static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
 {
   int i, x, y;
   int header_size = 4;
@@ -2386,17 +2379,18 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
 {
   int i, x, y;
   int element;
-  int num_contents, content_xsize, content_ysize;
+  int num_contents;
   int content_array[MAX_ELEMENT_CONTENTS][3][3];
 
   element = getMappedElement(getFile16BitBE(file));
   num_contents = getFile8Bit(file);
-  content_xsize = getFile8Bit(file);
-  content_ysize = getFile8Bit(file);
+
+  getFile8Bit(file);   /* content x size (unused) */
+  getFile8Bit(file);   /* content y size (unused) */
 
   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
 
@@ -2430,7 +2424,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
 {
   int i;
   int element;
@@ -2464,7 +2458,7 @@ static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
 {
   int num_changed_custom_elements = getFile16BitBE(file);
   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
@@ -2495,7 +2489,7 @@ static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
 {
   int num_changed_custom_elements = getFile16BitBE(file);
   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
@@ -2521,7 +2515,7 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
 {
   int num_changed_custom_elements = getFile16BitBE(file);
   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
@@ -2612,7 +2606,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
 {
   struct ElementInfo *ei;
   int chunk_size_expected;
@@ -2760,7 +2754,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
 {
   struct ElementInfo *ei;
   struct ElementGroupInfo *group;
@@ -2804,7 +2798,7 @@ static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
+static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
                                int element, int real_element)
 {
   int micro_chunk_size = 0;
@@ -2944,13 +2938,13 @@ static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
   return micro_chunk_size;
 }
 
-static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
 {
   int real_chunk_size = 0;
 
   li = *level;         /* copy level data into temporary buffer */
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
 
@@ -2963,13 +2957,13 @@ static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
   return real_chunk_size;
 }
 
-static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
 {
   int real_chunk_size = 0;
 
   li = *level;         /* copy level data into temporary buffer */
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     int element = getMappedElement(getFile16BitBE(file));
 
@@ -2985,13 +2979,13 @@ static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
   return real_chunk_size;
 }
 
-static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
 {
   int real_chunk_size = 0;
 
   li = *level;         /* copy level data into temporary buffer */
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     int element = getMappedElement(getFile16BitBE(file));
 
@@ -3007,13 +3001,13 @@ static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
   return real_chunk_size;
 }
 
-static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
 {
   int element = getMappedElement(getFile16BitBE(file));
   int envelope_nr = element - EL_ENVELOPE_1;
   int real_chunk_size = 2;
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
                                            -1, element);
@@ -3027,7 +3021,7 @@ static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
   return real_chunk_size;
 }
 
-static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
 {
   int element = getMappedElement(getFile16BitBE(file));
   int real_chunk_size = 2;
@@ -3038,7 +3032,7 @@ static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
 
   xx_ei.num_change_pages = -1;
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
                                            -1, element);
@@ -3072,7 +3066,7 @@ static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
   /* start with reading properties for the first change page */
   xx_current_change_page = 0;
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
 
@@ -3094,7 +3088,7 @@ static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
   return real_chunk_size;
 }
 
-static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
+static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
 {
   int element = getMappedElement(getFile16BitBE(file));
   int real_chunk_size = 2;
@@ -3104,7 +3098,7 @@ static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
   xx_ei = *ei;         /* copy element data into temporary buffer */
   xx_group = *group;   /* copy group data into temporary buffer */
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
                                            -1, element);
@@ -3120,24 +3114,21 @@ static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
 }
 
 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
-                                     struct LevelFileInfo *level_file_info)
+                                     struct LevelFileInfo *level_file_info,
+                                     boolean level_info_only)
 {
   char *filename = level_file_info->filename;
   char cookie[MAX_LINE_LEN];
   char chunk_name[CHUNK_ID_LEN + 1];
   int chunk_size;
-  FILE *file;
+  File *file;
 
-  if (!(file = fopen(filename, MODE_READ)))
+  if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
 
-#if 1
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
-#else
-    if (level != &level_template)
+    if (!level_info_only)
       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
-#endif
 
     return;
   }
@@ -3153,14 +3144,17 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
       level->no_valid_file = TRUE;
 
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
   }
   else /* check for pre-2.0 file format with cookie string */
   {
     strcpy(cookie, chunk_name);
-    fgets(&cookie[4], MAX_LINE_LEN - 4, file);
+    if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
+      cookie[4] = '\0';
     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
       cookie[strlen(cookie) - 1] = '\0';
 
@@ -3169,7 +3163,9 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
       level->no_valid_file = TRUE;
 
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
 
@@ -3178,7 +3174,9 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
       level->no_valid_file = TRUE;
 
       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
 
@@ -3198,7 +3196,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       char *name;
       int size;
-      int (*loader)(FILE *, int, struct LevelInfo *);
+      int (*loader)(File *, int, struct LevelInfo *);
     }
     chunk_info[] =
     {
@@ -3265,435 +3263,14 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     }
   }
 
-  fclose(file);
+  closeFile(file);
 }
 
+
 /* ------------------------------------------------------------------------- */
 /* functions for loading EM level                                            */
 /* ------------------------------------------------------------------------- */
 
-#if 0
-
-static int map_em_element_yam(int element)
-{
-  switch (element)
-  {
-    case 0x00: return EL_EMPTY;
-    case 0x01: return EL_EMERALD;
-    case 0x02: return EL_DIAMOND;
-    case 0x03: return EL_ROCK;
-    case 0x04: return EL_ROBOT;
-    case 0x05: return EL_SPACESHIP_UP;
-    case 0x06: return EL_BOMB;
-    case 0x07: return EL_BUG_UP;
-    case 0x08: return EL_AMOEBA_DROP;
-    case 0x09: return EL_NUT;
-    case 0x0a: return EL_YAMYAM;
-    case 0x0b: return EL_QUICKSAND_FULL;
-    case 0x0c: return EL_SAND;
-    case 0x0d: return EL_WALL_SLIPPERY;
-    case 0x0e: return EL_STEELWALL;
-    case 0x0f: return EL_WALL;
-    case 0x10: return EL_EM_KEY_1;
-    case 0x11: return EL_EM_KEY_2;
-    case 0x12: return EL_EM_KEY_4;
-    case 0x13: return EL_EM_KEY_3;
-    case 0x14: return EL_MAGIC_WALL;
-    case 0x15: return EL_ROBOT_WHEEL;
-    case 0x16: return EL_DYNAMITE;
-
-    case 0x17: return EL_EM_KEY_1;                     /* EMC */
-    case 0x18: return EL_BUG_UP;                       /* EMC */
-    case 0x1a: return EL_DIAMOND;                      /* EMC */
-    case 0x1b: return EL_EMERALD;                      /* EMC */
-    case 0x25: return EL_NUT;                          /* EMC */
-    case 0x80: return EL_EMPTY;                        /* EMC */
-    case 0x85: return EL_EM_KEY_1;                     /* EMC */
-    case 0x86: return EL_EM_KEY_2;                     /* EMC */
-    case 0x87: return EL_EM_KEY_4;                     /* EMC */
-    case 0x88: return EL_EM_KEY_3;                     /* EMC */
-    case 0x94: return EL_QUICKSAND_EMPTY;              /* EMC */
-    case 0x9a: return EL_AMOEBA_WET;                   /* EMC */
-    case 0xaf: return EL_DYNAMITE;                     /* EMC */
-    case 0xbd: return EL_SAND;                         /* EMC */
-
-    default:
-      Error(ERR_WARN, "invalid level element %d", element);
-      return EL_UNKNOWN;
-  }
-}
-
-static int map_em_element_field(int element)
-{
-  if (element >= 0xc8 && element <= 0xe1)
-    return EL_CHAR_A + (element - 0xc8);
-  else if (element >= 0xe2 && element <= 0xeb)
-    return EL_CHAR_0 + (element - 0xe2);
-
-  switch (element)
-  {
-    case 0x00: return EL_ROCK;
-    case 0x01: return EL_ROCK;                         /* EMC */
-    case 0x02: return EL_DIAMOND;
-    case 0x03: return EL_DIAMOND;
-    case 0x04: return EL_ROBOT;
-    case 0x05: return EL_ROBOT;                        /* EMC */
-    case 0x06: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x07: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x08: return EL_SPACESHIP_UP;
-    case 0x09: return EL_SPACESHIP_RIGHT;
-    case 0x0a: return EL_SPACESHIP_DOWN;
-    case 0x0b: return EL_SPACESHIP_LEFT;
-    case 0x0c: return EL_SPACESHIP_UP;
-    case 0x0d: return EL_SPACESHIP_RIGHT;
-    case 0x0e: return EL_SPACESHIP_DOWN;
-    case 0x0f: return EL_SPACESHIP_LEFT;
-
-    case 0x10: return EL_BOMB;
-    case 0x11: return EL_BOMB;                         /* EMC */
-    case 0x12: return EL_EMERALD;
-    case 0x13: return EL_EMERALD;
-    case 0x14: return EL_BUG_UP;
-    case 0x15: return EL_BUG_RIGHT;
-    case 0x16: return EL_BUG_DOWN;
-    case 0x17: return EL_BUG_LEFT;
-    case 0x18: return EL_BUG_UP;
-    case 0x19: return EL_BUG_RIGHT;
-    case 0x1a: return EL_BUG_DOWN;
-    case 0x1b: return EL_BUG_LEFT;
-    case 0x1c: return EL_AMOEBA_DROP;
-    case 0x1d: return EL_AMOEBA_DROP;                  /* EMC */
-    case 0x1e: return EL_AMOEBA_DROP;                  /* EMC */
-    case 0x1f: return EL_AMOEBA_DROP;                  /* EMC */
-
-    case 0x20: return EL_ROCK;
-    case 0x21: return EL_BOMB;                         /* EMC */
-    case 0x22: return EL_DIAMOND;                      /* EMC */
-    case 0x23: return EL_EMERALD;                      /* EMC */
-    case 0x24: return EL_MAGIC_WALL;
-    case 0x25: return EL_NUT;
-    case 0x26: return EL_NUT;                          /* EMC */
-    case 0x27: return EL_NUT;                          /* EMC */
-
-      /* looks like magic wheel, but is _always_ activated */
-    case 0x28: return EL_ROBOT_WHEEL;                  /* EMC */
-
-    case 0x29: return EL_YAMYAM;       /* up */
-    case 0x2a: return EL_YAMYAM;       /* down */
-    case 0x2b: return EL_YAMYAM;       /* left */      /* EMC */
-    case 0x2c: return EL_YAMYAM;       /* right */     /* EMC */
-    case 0x2d: return EL_QUICKSAND_FULL;
-    case 0x2e: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x2f: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0x30: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x31: return EL_SAND;                         /* EMC */
-    case 0x32: return EL_SAND;                         /* EMC */
-    case 0x33: return EL_SAND;                         /* EMC */
-    case 0x34: return EL_QUICKSAND_FULL;               /* EMC */
-    case 0x35: return EL_QUICKSAND_FULL;               /* EMC */
-    case 0x36: return EL_QUICKSAND_FULL;               /* EMC */
-    case 0x37: return EL_SAND;                         /* EMC */
-    case 0x38: return EL_ROCK;                         /* EMC */
-    case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
-    case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
-    case 0x3b: return EL_DYNAMITE_ACTIVE;      /* 1 */
-    case 0x3c: return EL_DYNAMITE_ACTIVE;      /* 2 */
-    case 0x3d: return EL_DYNAMITE_ACTIVE;      /* 3 */
-    case 0x3e: return EL_DYNAMITE_ACTIVE;      /* 4 */
-    case 0x3f: return EL_ACID_POOL_BOTTOM;
-
-    case 0x40: return EL_EXIT_OPEN;    /* 1 */
-    case 0x41: return EL_EXIT_OPEN;    /* 2 */
-    case 0x42: return EL_EXIT_OPEN;    /* 3 */
-    case 0x43: return EL_BALLOON;                      /* EMC */
-    case 0x44: return EL_UNKNOWN;                      /* EMC ("plant") */
-    case 0x45: return EL_SPRING;                       /* EMC */
-    case 0x46: return EL_SPRING;       /* falling */   /* EMC */
-    case 0x47: return EL_SPRING;       /* left */      /* EMC */
-    case 0x48: return EL_SPRING;       /* right */     /* EMC */
-    case 0x49: return EL_UNKNOWN;                      /* EMC ("ball 1") */
-    case 0x4a: return EL_UNKNOWN;                      /* EMC ("ball 2") */
-    case 0x4b: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x4c: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x4d: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x4e: return EL_INVISIBLE_WALL;               /* EMC (? "android") */
-    case 0x4f: return EL_UNKNOWN;                      /* EMC ("android") */
-
-    case 0x50: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x51: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x52: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x53: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x54: return EL_UNKNOWN;                      /* EMC ("android") */
-    case 0x55: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x56: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x57: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x58: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x59: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5a: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5b: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5c: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5d: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5e: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x5f: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0x60: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x61: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x62: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x63: return EL_SPRING;       /* left */      /* EMC */
-    case 0x64: return EL_SPRING;       /* right */     /* EMC */
-    case 0x65: return EL_ACID;         /* 1 */         /* EMC */
-    case 0x66: return EL_ACID;         /* 2 */         /* EMC */
-    case 0x67: return EL_ACID;         /* 3 */         /* EMC */
-    case 0x68: return EL_ACID;         /* 4 */         /* EMC */
-    case 0x69: return EL_ACID;         /* 5 */         /* EMC */
-    case 0x6a: return EL_ACID;         /* 6 */         /* EMC */
-    case 0x6b: return EL_ACID;         /* 7 */         /* EMC */
-    case 0x6c: return EL_ACID;         /* 8 */         /* EMC */
-    case 0x6d: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x6e: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x6f: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0x70: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x71: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x72: return EL_NUT;          /* left */      /* EMC */
-    case 0x73: return EL_SAND;                         /* EMC (? "nut") */
-    case 0x74: return EL_STEELWALL;
-    case 0x75: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x76: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x77: return EL_BOMB;         /* left */      /* EMC */
-    case 0x78: return EL_BOMB;         /* right */     /* EMC */
-    case 0x79: return EL_ROCK;         /* left */      /* EMC */
-    case 0x7a: return EL_ROCK;         /* right */     /* EMC */
-    case 0x7b: return EL_ACID;                         /* (? EMC "blank") */
-    case 0x7c: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x7d: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x7e: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0x7f: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0x80: return EL_EMPTY;
-    case 0x81: return EL_WALL_SLIPPERY;
-    case 0x82: return EL_SAND;
-    case 0x83: return EL_STEELWALL;
-    case 0x84: return EL_WALL;
-    case 0x85: return EL_EM_KEY_1;
-    case 0x86: return EL_EM_KEY_2;
-    case 0x87: return EL_EM_KEY_4;
-    case 0x88: return EL_EM_KEY_3;
-    case 0x89: return EL_EM_GATE_1;
-    case 0x8a: return EL_EM_GATE_2;
-    case 0x8b: return EL_EM_GATE_4;
-    case 0x8c: return EL_EM_GATE_3;
-    case 0x8d: return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
-    case 0x8e: return EL_EM_GATE_1_GRAY;
-    case 0x8f: return EL_EM_GATE_2_GRAY;
-
-    case 0x90: return EL_EM_GATE_4_GRAY;
-    case 0x91: return EL_EM_GATE_3_GRAY;
-    case 0x92: return EL_MAGIC_WALL;
-    case 0x93: return EL_ROBOT_WHEEL;
-    case 0x94: return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
-    case 0x95: return EL_ACID_POOL_TOPLEFT;
-    case 0x96: return EL_ACID_POOL_TOPRIGHT;
-    case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
-    case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
-    case 0x99: return EL_ACID;                 /* (? EMC "fake blank") */
-    case 0x9a: return EL_AMOEBA_DEAD;          /* 1 */
-    case 0x9b: return EL_AMOEBA_DEAD;          /* 2 */
-    case 0x9c: return EL_AMOEBA_DEAD;          /* 3 */
-    case 0x9d: return EL_AMOEBA_DEAD;          /* 4 */
-    case 0x9e: return EL_EXIT_CLOSED;
-    case 0x9f: return EL_CHAR_LESS;            /* arrow left */
-
-      /* looks like normal sand, but behaves like wall */
-    case 0xa0: return EL_UNKNOWN;              /* EMC ("fake grass") */
-    case 0xa1: return EL_UNKNOWN;              /* EMC ("lenses") */
-    case 0xa2: return EL_UNKNOWN;              /* EMC ("magnify") */
-    case 0xa3: return EL_UNKNOWN;              /* EMC ("fake blank") */
-    case 0xa4: return EL_UNKNOWN;              /* EMC ("fake grass") */
-    case 0xa5: return EL_UNKNOWN;              /* EMC ("switch") */
-    case 0xa6: return EL_UNKNOWN;              /* EMC ("switch") */
-    case 0xa7: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xa8: return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
-    case 0xa9: return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
-    case 0xaa: return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
-    case 0xab: return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
-    case 0xac: return EL_CHAR_COMMA;                   /* EMC */
-    case 0xad: return EL_CHAR_QUOTEDBL;                /* EMC */
-    case 0xae: return EL_CHAR_MINUS;                   /* EMC */
-    case 0xaf: return EL_DYNAMITE;
-
-    case 0xb0: return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
-    case 0xb1: return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
-    case 0xb2: return EL_UNKNOWN;                      /* EMC ("decor 7") */
-    case 0xb3: return EL_STEELWALL;            /* 2 */ /* EMC */
-    case 0xb4: return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
-    case 0xb5: return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
-    case 0xb6: return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
-    case 0xb7: return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
-    case 0xb8: return EL_BALLOON_SWITCH_ANY;           /* EMC */
-    case 0xb9: return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
-    case 0xba: return EL_BALLOON_SWITCH_DOWN;          /* EMC */
-    case 0xbb: return EL_BALLOON_SWITCH_LEFT;          /* EMC */
-    case 0xbc: return EL_BALLOON_SWITCH_UP;            /* EMC */
-    case 0xbd: return EL_SAND;                         /* EMC ("dirt") */
-    case 0xbe: return EL_UNKNOWN;                      /* EMC ("plant") */
-    case 0xbf: return EL_UNKNOWN;                      /* EMC ("key 5") */
-
-    case 0xc0: return EL_UNKNOWN;                      /* EMC ("key 6") */
-    case 0xc1: return EL_UNKNOWN;                      /* EMC ("key 7") */
-    case 0xc2: return EL_UNKNOWN;                      /* EMC ("key 8") */
-    case 0xc3: return EL_UNKNOWN;                      /* EMC ("door 5") */
-    case 0xc4: return EL_UNKNOWN;                      /* EMC ("door 6") */
-    case 0xc5: return EL_UNKNOWN;                      /* EMC ("door 7") */
-    case 0xc6: return EL_UNKNOWN;                      /* EMC ("door 8") */
-    case 0xc7: return EL_UNKNOWN;                      /* EMC ("bumper") */
-
-      /* characters: see above */
-
-    case 0xec: return EL_CHAR_PERIOD;
-    case 0xed: return EL_CHAR_EXCLAM;
-    case 0xee: return EL_CHAR_COLON;
-    case 0xef: return EL_CHAR_QUESTION;
-
-    case 0xf0: return EL_CHAR_GREATER;                 /* arrow right */
-    case 0xf1: return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
-    case 0xf2: return EL_UNKNOWN;              /* EMC ("fake door 5") */
-    case 0xf3: return EL_UNKNOWN;              /* EMC ("fake door 6") */
-    case 0xf4: return EL_UNKNOWN;              /* EMC ("fake door 7") */
-    case 0xf5: return EL_UNKNOWN;              /* EMC ("fake door 8") */
-    case 0xf6: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xf7: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0xf8: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xf9: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xfa: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xfb: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xfc: return EL_EMPTY_SPACE;                  /* EMC */
-    case 0xfd: return EL_EMPTY_SPACE;                  /* EMC */
-
-    case 0xfe: return EL_PLAYER_1;                     /* EMC: "blank" */
-    case 0xff: return EL_PLAYER_2;                     /* EMC: "blank" */
-
-    default:
-      /* should never happen (all 8-bit value cases should be handled) */
-      Error(ERR_WARN, "invalid level element %d", element);
-      return EL_UNKNOWN;
-  }
-}
-
-#define EM_LEVEL_SIZE                  2106
-#define EM_LEVEL_XSIZE                 64
-#define EM_LEVEL_YSIZE                 32
-
-static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
-                                        struct LevelFileInfo *level_file_info)
-{
-  char *filename = level_file_info->filename;
-  FILE *file;
-  unsigned char leveldata[EM_LEVEL_SIZE];
-  unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
-  int nr = level_file_info->nr;
-  int i, x, y;
-
-  if (!(file = fopen(filename, MODE_READ)))
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
-
-    return;
-  }
-
-  for (i = 0; i < EM_LEVEL_SIZE; i++)
-    leveldata[i] = fgetc(file);
-
-  fclose(file);
-
-  /* check if level data is crypted by testing against known starting bytes
-     of the few existing crypted level files (from Emerald Mine 1 + 2) */
-
-  if ((leveldata[0] == 0xf1 ||
-       leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
-  {
-    unsigned char code0 = 0x65;
-    unsigned char code1 = 0x11;
-
-    if (leveldata[0] == 0xf5)  /* error in crypted Emerald Mine 2 levels */
-      leveldata[0] = 0xf1;
-
-    /* decode crypted level data */
-
-    for (i = 0; i < EM_LEVEL_SIZE; i++)
-    {
-      leveldata[i] ^= code0;
-      leveldata[i] -= code1;
-
-      code0 = (code0 + 7) & 0xff;
-    }
-  }
-
-  level->fieldx        = EM_LEVEL_XSIZE;
-  level->fieldy        = EM_LEVEL_YSIZE;
-
-  level->time          = header[46] * 10;
-  level->gems_needed   = header[47];
-
-  /* The original Emerald Mine levels have their level number stored
-     at the second byte of the level file...
-     Do not trust this information at other level files, e.g. EMC,
-     but correct it anyway (normally the first row is completely
-     steel wall, so the correction does not hurt anyway). */
-
-  if (leveldata[1] == nr)
-    leveldata[1] = leveldata[2];       /* correct level number field */
-
-  sprintf(level->name, "Level %d", nr);                /* set level name */
-
-  level->score[SC_EMERALD]     = header[36];
-  level->score[SC_DIAMOND]     = header[37];
-  level->score[SC_ROBOT]       = header[38];
-  level->score[SC_SPACESHIP]   = header[39];
-  level->score[SC_BUG]         = header[40];
-  level->score[SC_YAMYAM]      = header[41];
-  level->score[SC_NUT]         = header[42];
-  level->score[SC_DYNAMITE]    = header[43];
-  level->score[SC_TIME_BONUS]  = header[44];
-
-  level->num_yamyam_contents = 4;
-
-  for (i = 0; i < level->num_yamyam_contents; i++)
-    for (y = 0; y < 3; y++)
-      for (x = 0; x < 3; x++)
-       level->yamyam_content[i].e[x][y] =
-         map_em_element_yam(header[i * 9 + y * 3 + x]);
-
-  level->amoeba_speed          = (header[52] * 256 + header[53]) % 256;
-  level->time_magic_wall       = (header[54] * 256 + header[55]) * 16 / 100;
-  level->time_wheel            = (header[56] * 256 + header[57]) * 16 / 100;
-  level->amoeba_content                = EL_DIAMOND;
-
-  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
-  {
-    int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
-
-    if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
-      new_element = EL_AMOEBA_WET;
-
-    level->field[x][y] = new_element;
-  }
-
-  x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
-  y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
-  level->field[x][y] = EL_PLAYER_1;
-
-  x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
-  y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
-  level->field[x][y] = EL_PLAYER_2;
-}
-
-#endif
-
 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
 {
   static int ball_xy[8][2] =
@@ -3923,407 +3500,58 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
 /* functions for loading SP level                                            */
 /* ------------------------------------------------------------------------- */
 
-#if 0
-
-#define NUM_SUPAPLEX_LEVELS_PER_PACKAGE        111
-#define SP_LEVEL_SIZE                  1536
-#define SP_LEVEL_XSIZE                 60
-#define SP_LEVEL_YSIZE                 24
-#define SP_LEVEL_NAME_LEN              23
-
-static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
-                                      int nr)
+void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
 {
-  int initial_player_gravity;
-  int num_special_ports;
+  struct LevelInfo_SP *level_sp = level->native_sp_level;
+  LevelInfoType *header = &level_sp->header;
   int i, x, y;
 
-  /* for details of the Supaplex level format, see Herman Perk's Supaplex
-     documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
-
-  /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
-  for (y = 0; y < SP_LEVEL_YSIZE; y++)
-  {
-    for (x = 0; x < SP_LEVEL_XSIZE; x++)
-    {
-      int element_old = fgetc(file);
-      int element_new;
-
-      if (element_old <= 0x27)
-       element_new = getMappedElement(EL_SP_START + element_old);
-      else if (element_old == 0x28)
-       element_new = EL_INVISIBLE_WALL;
-      else
-      {
-       Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
-       Error(ERR_WARN, "invalid level element %d", element_old);
-
-       element_new = EL_UNKNOWN;
-      }
-
-      level->field[x][y] = element_new;
-    }
-  }
-
-  ReadUnusedBytesFromFile(file, 4);    /* (not used by Supaplex engine) */
-
-  /* initial gravity: 1 == "on", anything else (0) == "off" */
-  initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
+  level_sp->width  = level->fieldx;
+  level_sp->height = level->fieldy;
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    level->initial_player_gravity[i] = initial_player_gravity;
+  for (x = 0; x < level->fieldx; x++)
+    for (y = 0; y < level->fieldy; y++)
+      level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
 
-  ReadUnusedBytesFromFile(file, 1);    /* (not used by Supaplex engine) */
+  header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
 
-  /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
-    level->name[i] = fgetc(file);
-  level->name[SP_LEVEL_NAME_LEN] = '\0';
-
-  /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
-  ReadUnusedBytesFromFile(file, 1);    /* (not used by R'n'D engine) */
+    header->LevelTitle[i] = level->name[i];
+  /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
 
-  /* number of infotrons needed; 0 means that Supaplex will count the total
-     amount of infotrons in the level and use the low byte of that number
-     (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
-  level->gems_needed = fgetc(file);
+  header->InfotronsNeeded = level->gems_needed;
 
-  /* number of special ("gravity") port entries below (maximum 10 allowed) */
-  num_special_ports = fgetc(file);
+  header->SpecialPortCount = 0;
 
-  /* database of properties of up to 10 special ports (6 bytes per port) */
-  for (i = 0; i < 10; i++)
+  for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
   {
-    int port_location, port_x, port_y, port_element;
-    int gravity;
-
-    /* high and low byte of the location of a special port; if (x, y) are the
-       coordinates of a port in the field and (0, 0) is the top-left corner,
-       the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
-       of what may be expected: Supaplex works with a game field in memory
-       which is 2 bytes per tile) */
-    port_location = getFile16BitBE(file);
-
-    /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
-    gravity = fgetc(file);
-
-    /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
-    ReadUnusedBytesFromFile(file, 1);  /* (not used by R'n'D engine) */
-
-    /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
-    ReadUnusedBytesFromFile(file, 1);  /* (not used by R'n'D engine) */
-
-    ReadUnusedBytesFromFile(file, 1);  /* (not used by Supaplex engine) */
-
-    if (i >= num_special_ports)
-      continue;
-
-    port_x = (port_location / 2) % SP_LEVEL_XSIZE;
-    port_y = (port_location / 2) / SP_LEVEL_XSIZE;
+    boolean gravity_port_found = FALSE;
+    boolean gravity_port_valid = FALSE;
+    int gravity_port_flag;
+    int gravity_port_base_element;
+    int element = level->field[x][y];
 
-    if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
-       port_y < 0 || port_y >= SP_LEVEL_YSIZE)
+    if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
+       element <= EL_SP_GRAVITY_ON_PORT_UP)
     {
-      Error(ERR_WARN, "special port position (%d, %d) out of bounds",
-           port_x, port_y);
-
-      continue;
+      gravity_port_found = TRUE;
+      gravity_port_valid = TRUE;
+      gravity_port_flag = 1;
+      gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
     }
-
-    port_element = level->field[port_x][port_y];
-
-    if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
-       port_element > EL_SP_GRAVITY_PORT_UP)
+    else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
+            element <= EL_SP_GRAVITY_OFF_PORT_UP)
     {
-      Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
-
-      continue;
+      gravity_port_found = TRUE;
+      gravity_port_valid = TRUE;
+      gravity_port_flag = 0;
+      gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
     }
-
-    /* change previous (wrong) gravity inverting special port to either
-       gravity enabling special port or gravity disabling special port */
-    level->field[port_x][port_y] +=
-      (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
-       EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
-  }
-
-  ReadUnusedBytesFromFile(file, 4);    /* (not used by Supaplex engine) */
-
-  /* change special gravity ports without database entries to normal ports */
-  for (y = 0; y < SP_LEVEL_YSIZE; y++)
-    for (x = 0; x < SP_LEVEL_XSIZE; x++)
-      if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
-         level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
-       level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
-
-  /* auto-determine number of infotrons if it was stored as "0" -- see above */
-  if (level->gems_needed == 0)
-  {
-    for (y = 0; y < SP_LEVEL_YSIZE; y++)
-      for (x = 0; x < SP_LEVEL_XSIZE; x++)
-       if (level->field[x][y] == EL_SP_INFOTRON)
-         level->gems_needed++;
-
-    level->gems_needed &= 0xff;                /* only use low byte -- see above */
-  }
-
-  level->fieldx = SP_LEVEL_XSIZE;
-  level->fieldy = SP_LEVEL_YSIZE;
-
-  level->time = 0;                     /* no time limit */
-  level->amoeba_speed = 0;
-  level->time_magic_wall = 0;
-  level->time_wheel = 0;
-  level->amoeba_content = EL_EMPTY;
-
-#if 1
-  /* original Supaplex does not use score values -- use default values */
-#else
-  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
-    level->score[i] = 0;
-#endif
-
-  /* there are no yamyams in supaplex levels */
-  for (i = 0; i < level->num_yamyam_contents; i++)
-    for (y = 0; y < 3; y++)
-      for (x = 0; x < 3; x++)
-       level->yamyam_content[i].e[x][y] = EL_EMPTY;
-}
-
-static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
-{
-  char *filename = level_file_info->filename;
-  FILE *file;
-  int nr = level_file_info->nr - leveldir_current->first_level;
-  int i, l, x, y;
-  char name_first, name_last;
-  struct LevelInfo multipart_level;
-  int multipart_xpos, multipart_ypos;
-  boolean is_multipart_level;
-  boolean is_first_part;
-  boolean reading_multipart_level = FALSE;
-  boolean use_empty_level = FALSE;
-
-  if (!(file = fopen(filename, MODE_READ)))
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
-
-    return;
-  }
-
-  /* position file stream to the requested level inside the level package */
-  if (level_file_info->packed &&
-      fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
-
-    return;
-  }
-
-  /* there exist Supaplex level package files with multi-part levels which
-     can be detected as follows: instead of leading and trailing dashes ('-')
-     to pad the level name, they have leading and trailing numbers which are
-     the x and y coordinations of the current part of the multi-part level;
-     if there are '?' characters instead of numbers on the left or right side
-     of the level name, the multi-part level consists of only horizontal or
-     vertical parts */
-
-  for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
-  {
-    LoadLevelFromFileStream_SP(file, level, l);
-
-    /* check if this level is a part of a bigger multi-part level */
-
-    name_first = level->name[0];
-    name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
-
-    is_multipart_level =
-      ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
-       (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
-
-    is_first_part =
-      ((name_first == '?' || name_first == '1') &&
-       (name_last  == '?' || name_last  == '1'));
-
-    /* correct leading multipart level meta information in level name */
-    for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
-      level->name[i] = '-';
-
-    /* correct trailing multipart level meta information in level name */
-    for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
-      level->name[i] = '-';
-
-    /* ---------- check for normal single level ---------- */
-
-    if (!reading_multipart_level && !is_multipart_level)
-    {
-      /* the current level is simply a normal single-part level, and we are
-        not reading a multi-part level yet, so return the level as it is */
-
-      break;
-    }
-
-    /* ---------- check for empty level (unused multi-part) ---------- */
-
-    if (!reading_multipart_level && is_multipart_level && !is_first_part)
-    {
-      /* this is a part of a multi-part level, but not the first part
-        (and we are not already reading parts of a multi-part level);
-        in this case, use an empty level instead of the single part */
-
-      use_empty_level = TRUE;
-
-      break;
-    }
-
-    /* ---------- check for finished multi-part level ---------- */
-
-    if (reading_multipart_level &&
-       (!is_multipart_level ||
-        !strEqual(level->name, multipart_level.name)))
-    {
-      /* we are already reading parts of a multi-part level, but this level is
-        either not a multi-part level, or a part of a different multi-part
-        level; in both cases, the multi-part level seems to be complete */
-
-      break;
-    }
-
-    /* ---------- here we have one part of a multi-part level ---------- */
-
-    reading_multipart_level = TRUE;
-
-    if (is_first_part) /* start with first part of new multi-part level */
-    {
-      /* copy level info structure from first part */
-      multipart_level = *level;
-
-      /* clear playfield of new multi-part level */
-      for (y = 0; y < MAX_LEV_FIELDY; y++)
-       for (x = 0; x < MAX_LEV_FIELDX; x++)
-         multipart_level.field[x][y] = EL_EMPTY;
-    }
-
-    if (name_first == '?')
-      name_first = '1';
-    if (name_last == '?')
-      name_last = '1';
-
-    multipart_xpos = (int)(name_first - '0');
-    multipart_ypos = (int)(name_last  - '0');
-
-#if 0
-    printf("----------> part (%d/%d) of multi-part level '%s'\n",
-          multipart_xpos, multipart_ypos, multipart_level.name);
-#endif
-
-    if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
-       multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
-    {
-      Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
-
-      break;
-    }
-
-    multipart_level.fieldx = MAX(multipart_level.fieldx,
-                                multipart_xpos * SP_LEVEL_XSIZE);
-    multipart_level.fieldy = MAX(multipart_level.fieldy,
-                                multipart_ypos * SP_LEVEL_YSIZE);
-
-    /* copy level part at the right position of multi-part level */
-    for (y = 0; y < SP_LEVEL_YSIZE; y++)
-    {
-      for (x = 0; x < SP_LEVEL_XSIZE; x++)
-      {
-       int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
-       int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
-
-       multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
-      }
-    }
-  }
-
-  fclose(file);
-
-  if (use_empty_level)
-  {
-    setLevelInfoToDefaults(level);
-
-    level->fieldx = SP_LEVEL_XSIZE;
-    level->fieldy = SP_LEVEL_YSIZE;
-
-    for (y = 0; y < SP_LEVEL_YSIZE; y++)
-      for (x = 0; x < SP_LEVEL_XSIZE; x++)
-       level->field[x][y] = EL_EMPTY;
-
-    strcpy(level->name, "-------- EMPTY --------");
-
-    Error(ERR_WARN, "single part of multi-part level -- using empty level");
-  }
-
-  if (reading_multipart_level)
-    *level = multipart_level;
-}
-
-#endif
-
-void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
-{
-  struct LevelInfo_SP *level_sp = level->native_sp_level;
-  LevelInfoType *header = &level_sp->header;
-  int i, x, y;
-
-  level_sp->width  = level->fieldx;
-  level_sp->height = level->fieldy;
-
-  for (x = 0; x < level->fieldx; x++)
-    for (y = 0; y < level->fieldy; y++)
-      level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
-
-  header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
-
-  for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
-    header->LevelTitle[i] = level->name[i];
-  /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
-
-  header->InfotronsNeeded = level->gems_needed;
-
-  header->SpecialPortCount = 0;
-
-  for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
-  {
-    boolean gravity_port_found = FALSE;
-    boolean gravity_port_valid = FALSE;
-    int gravity_port_flag;
-    int gravity_port_base_element;
-    int element = level->field[x][y];
-
-    if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
-       element <= EL_SP_GRAVITY_ON_PORT_UP)
-    {
-      gravity_port_found = TRUE;
-      gravity_port_valid = TRUE;
-      gravity_port_flag = 1;
-      gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
-    }
-    else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
-            element <= EL_SP_GRAVITY_OFF_PORT_UP)
-    {
-      gravity_port_found = TRUE;
-      gravity_port_valid = TRUE;
-      gravity_port_flag = 0;
-      gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
-    }
-    else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
-            element <= EL_SP_GRAVITY_PORT_UP)
-    {
-      /* change R'n'D style gravity inverting special port to normal port
-        (there are no gravity inverting ports in native Supaplex engine) */
+    else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
+            element <= EL_SP_GRAVITY_PORT_UP)
+    {
+      /* change R'n'D style gravity inverting special port to normal port
+        (there are no gravity inverting ports in native Supaplex engine) */
 
       gravity_port_found = TRUE;
       gravity_port_valid = FALSE;
@@ -4518,7 +3746,8 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     tape.pos[i].delay = demo_repeat + 1;
   }
 
-  tape.length_seconds = GetTapeLength();
+  tape.length_frames  = GetTapeLengthFrames();
+  tape.length_seconds = GetTapeLengthSeconds();
 }
 
 
@@ -5995,9 +5224,7 @@ int getMappedElement_DC(int element)
   return getMappedElement(element);
 }
 
-#if 1
-
-static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
+static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
                                       int nr)
 {
   byte header[DC_LEVEL_HEADER_SIZE];
@@ -6095,11 +5322,7 @@ static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
     {
       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-#if 1
       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
-#else
-      int element_dc = word;
-#endif
 
       if (i < MAX_ELEMENT_CONTENTS)
        level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
@@ -6114,11 +5337,7 @@ static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
   {
     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-#if 1
     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
-#else
-    int element_dc = word;
-#endif
 
     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
       level->field[x][y] = getMappedElement_DC(element_dc);
@@ -6160,45 +5379,24 @@ static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
      can slip down from flat walls, like normal walls and steel walls */
   level->em_slippery_gems = TRUE;
-
-#if 0
-  /* Diamond Caves II levels are always surrounded by indestructible wall, but
-     not necessarily in a rectangular way -- fill with invisible steel wall */
-
-  /* !!! not always true !!! keep level and set BorderElement instead !!! */
-
-  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
-  {
-#if 1
-    if ((x == 0 || x == level->fieldx - 1 ||
-        y == 0 || y == level->fieldy - 1) &&
-       level->field[x][y] == EL_EMPTY)
-      level->field[x][y] = EL_INVISIBLE_STEELWALL;
-#else
-    if ((x == 0 || x == level->fieldx - 1 ||
-        y == 0 || y == level->fieldy - 1) &&
-       level->field[x][y] == EL_EMPTY)
-      FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
-                    level->field, level->fieldx, level->fieldy);
-#endif
-  }
-#endif
 }
 
 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+                                    struct LevelFileInfo *level_file_info,
+                                    boolean level_info_only)
 {
   char *filename = level_file_info->filename;
-  FILE *file;
+  File *file;
   int num_magic_bytes = 8;
   char magic_bytes[num_magic_bytes + 1];
   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
 
-  if (!(file = fopen(filename, MODE_READ)))
+  if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (!level_info_only)
+      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -6208,7 +5406,8 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
   if (level_file_info->packed)
   {
     /* read "magic bytes" from start of file */
-    fgets(magic_bytes, num_magic_bytes + 1, file);
+    if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
+      magic_bytes[0] = '\0';
 
     /* check "magic bytes" for correct file format */
     if (!strPrefix(magic_bytes, "DC2"))
@@ -6238,7 +5437,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
       while (num_levels_to_skip >= 0)
       {
        /* advance file stream to next level inside the level package */
-       if (fseek(file, skip_bytes, SEEK_CUR) != 0)
+       if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
        {
          level->no_valid_file = TRUE;
 
@@ -6270,230 +5469,9 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
 
   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
 
-  fclose(file);
-}
-
-#else
-
-static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
-{
-  char *filename = level_file_info->filename;
-  FILE *file;
-#if 0
-  int nr = level_file_info->nr - leveldir_current->first_level;
-#endif
-  byte header[DC_LEVEL_HEADER_SIZE];
-  int envelope_size;
-  int envelope_header_pos = 62;
-  int envelope_content_pos = 94;
-  int level_name_pos = 251;
-  int level_author_pos = 292;
-  int envelope_header_len;
-  int envelope_content_len;
-  int level_name_len;
-  int level_author_len;
-  int fieldx, fieldy;
-  int num_yamyam_contents;
-  int i, x, y;
-
-  if (!(file = fopen(filename, MODE_READ)))
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
-
-    return;
-  }
-
-#if 0
-  /* position file stream to the requested level inside the level package */
-  if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
-
-    return;
-  }
-#endif
-
-  getDecodedWord_DC(0, TRUE);          /* initialize DC2 decoding engine */
-
-  for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
-  {
-    unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-
-    header[i * 2 + 0] = header_word >> 8;
-    header[i * 2 + 1] = header_word & 0xff;
-  }
-
-  /* read some values from level header to check level decoding integrity */
-  fieldx = header[6] | (header[7] << 8);
-  fieldy = header[8] | (header[9] << 8);
-  num_yamyam_contents = header[60] | (header[61] << 8);
-
-  /* do some simple sanity checks to ensure that level was correctly decoded */
-  if (fieldx < 1 || fieldx > 256 ||
-      fieldy < 1 || fieldy > 256 ||
-      num_yamyam_contents < 1 || num_yamyam_contents > 8)
-  {
-    level->no_valid_file = TRUE;
-
-    Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
-         filename);
-
-    return;
-  }
-
-  /* maximum envelope header size is 31 bytes */
-  envelope_header_len  = header[envelope_header_pos];
-  /* maximum envelope content size is 110 (156?) bytes */
-  envelope_content_len = header[envelope_content_pos];
-
-  /* maximum level title size is 40 bytes */
-  level_name_len       = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
-  /* maximum level author size is 30 (51?) bytes */
-  level_author_len     = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
-
-  envelope_size = 0;
-
-  for (i = 0; i < envelope_header_len; i++)
-    if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
-      level->envelope[0].text[envelope_size++] =
-       header[envelope_header_pos + 1 + i];
-
-  if (envelope_header_len > 0 && envelope_content_len > 0)
-  {
-    if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
-      level->envelope[0].text[envelope_size++] = '\n';
-    if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
-      level->envelope[0].text[envelope_size++] = '\n';
-  }
-
-  for (i = 0; i < envelope_content_len; i++)
-    if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
-      level->envelope[0].text[envelope_size++] =
-       header[envelope_content_pos + 1 + i];
-
-  level->envelope[0].text[envelope_size] = '\0';
-
-  level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
-  level->envelope[0].ysize = 10;
-  level->envelope[0].autowrap = TRUE;
-  level->envelope[0].centered = TRUE;
-
-  for (i = 0; i < level_name_len; i++)
-    level->name[i] = header[level_name_pos + 1 + i];
-  level->name[level_name_len] = '\0';
-
-  for (i = 0; i < level_author_len; i++)
-    level->author[i] = header[level_author_pos + 1 + i];
-  level->author[level_author_len] = '\0';
-
-  num_yamyam_contents = header[60] | (header[61] << 8);
-  level->num_yamyam_contents =
-    MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
-
-  for (i = 0; i < num_yamyam_contents; i++)
-  {
-    for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
-    {
-      unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-#if 1
-      int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
-#else
-      int element_dc = word;
-#endif
-
-      if (i < MAX_ELEMENT_CONTENTS)
-       level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
-    }
-  }
-
-  fieldx = header[6] | (header[7] << 8);
-  fieldy = header[8] | (header[9] << 8);
-  level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
-  level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
-
-  for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
-  {
-    unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-#if 1
-    int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
-#else
-    int element_dc = word;
-#endif
-
-    if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
-      level->field[x][y] = getMappedElement_DC(element_dc);
-  }
-
-  x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
-  y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
-  level->field[x][y] = EL_PLAYER_1;
-
-  x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
-  y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
-  level->field[x][y] = EL_PLAYER_2;
-
-  level->gems_needed           = header[18] | (header[19] << 8);
-
-  level->score[SC_EMERALD]     = header[20] | (header[21] << 8);
-  level->score[SC_DIAMOND]     = header[22] | (header[23] << 8);
-  level->score[SC_PEARL]       = header[24] | (header[25] << 8);
-  level->score[SC_CRYSTAL]     = header[26] | (header[27] << 8);
-  level->score[SC_NUT]         = header[28] | (header[29] << 8);
-  level->score[SC_ROBOT]       = header[30] | (header[31] << 8);
-  level->score[SC_SPACESHIP]   = header[32] | (header[33] << 8);
-  level->score[SC_BUG]         = header[34] | (header[35] << 8);
-  level->score[SC_YAMYAM]      = header[36] | (header[37] << 8);
-  level->score[SC_DYNAMITE]    = header[38] | (header[39] << 8);
-  level->score[SC_KEY]         = header[40] | (header[41] << 8);
-  level->score[SC_TIME_BONUS]  = header[42] | (header[43] << 8);
-
-  level->time                  = header[44] | (header[45] << 8);
-
-  level->amoeba_speed          = header[46] | (header[47] << 8);
-  level->time_light            = header[48] | (header[49] << 8);
-  level->time_timegate         = header[50] | (header[51] << 8);
-  level->time_wheel            = header[52] | (header[53] << 8);
-  level->time_magic_wall       = header[54] | (header[55] << 8);
-  level->extra_time            = header[56] | (header[57] << 8);
-  level->shield_normal_time    = header[58] | (header[59] << 8);
-
-  fclose(file);
-
-  /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
-     can slip down from flat walls, like normal walls and steel walls */
-  level->em_slippery_gems = TRUE;
-
-#if 0
-  /* Diamond Caves II levels are always surrounded by indestructible wall, but
-     not necessarily in a rectangular way -- fill with invisible steel wall */
-
-  /* !!! not always true !!! keep level and set BorderElement instead !!! */
-
-  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
-  {
-#if 1
-    if ((x == 0 || x == level->fieldx - 1 ||
-        y == 0 || y == level->fieldy - 1) &&
-       level->field[x][y] == EL_EMPTY)
-      level->field[x][y] = EL_INVISIBLE_STEELWALL;
-#else
-    if ((x == 0 || x == level->fieldx - 1 ||
-        y == 0 || y == level->fieldy - 1) &&
-       level->field[x][y] == EL_EMPTY)
-      FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
-                    level->field, level->fieldx, level->fieldy);
-#endif
-  }
-#endif
+  closeFile(file);
 }
 
-#endif
-
 
 /* ------------------------------------------------------------------------- */
 /* functions for loading SB level                                            */
@@ -6516,11 +5494,7 @@ int getMappedElement_SB(int element_ascii, boolean use_ces)
     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
-#if 0
-    { '_', EL_INVISIBLE_STEELWALL,  EL_CUSTOM_8 },  /* floor beyond border */
-#else
     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
-#endif
 
     { 0,   -1,                      -1          },
   };
@@ -6535,14 +5509,15 @@ int getMappedElement_SB(int element_ascii, boolean use_ces)
 }
 
 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+                                    struct LevelFileInfo *level_file_info,
+                                    boolean level_info_only)
 {
   char *filename = level_file_info->filename;
   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
   char last_comment[MAX_LINE_LEN];
   char level_name[MAX_LINE_LEN];
   char *line_ptr;
-  FILE *file;
+  File *file;
   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
   boolean read_continued_line = FALSE;
   boolean reading_playfield = FALSE;
@@ -6551,34 +5526,26 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
   int file_level_nr = 0;
   int line_nr = 0;
-  int x, y;
-
-#if 0
-  printf("::: looking for level number %d [%d]\n",
-        level_file_info->nr, num_levels_to_skip);
-#endif
+  int x = 0, y = 0;            /* initialized to make compilers happy */
 
   last_comment[0] = '\0';
   level_name[0] = '\0';
 
-  if (!(file = fopen(filename, MODE_READ)))
+  if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (!level_info_only)
+      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
     return;
   }
 
-  while (!feof(file))
+  while (!checkEndOfFile(file))
   {
     /* level successfully read, but next level may follow here */
     if (!got_valid_playfield_line && reading_playfield)
     {
-#if 0
-      printf("::: read complete playfield\n");
-#endif
-
       /* read playfield from single level file -- skip remaining file */
       if (!level_file_info->packed)
        break;
@@ -6597,7 +5564,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
     got_valid_playfield_line = FALSE;
 
     /* read next line of input file */
-    if (!fgets(line, MAX_LINE_LEN, file))
+    if (!getStringFromFile(file, line, MAX_LINE_LEN))
       break;
 
     /* check if line was completely read and is terminated by line break */
@@ -6647,10 +5614,6 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
 
       strcpy(last_comment, line_ptr);
 
-#if 0
-      printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
-#endif
-
       continue;
     }
 
@@ -6662,10 +5625,6 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
        level_name[strlen(level_name) - 1] = '\0';
 
-#if 0
-      printf("::: found level name '%s' in line %d\n", level_name, line_nr);
-#endif
-
       continue;
     }
 
@@ -6678,10 +5637,6 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
 
     /* at this point, we have found a line containing part of a playfield */
 
-#if 0
-    printf("::: found playfield row in line %d\n", line_nr);
-#endif
-
     got_valid_playfield_line = TRUE;
 
     if (!reading_playfield)
@@ -6744,7 +5699,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
     y++;
   }
 
-  fclose(file);
+  closeFile(file);
 
   level->fieldy = y;
 
@@ -6764,19 +5719,11 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
   {
     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
     level->name[MAX_LEVEL_NAME_LEN] = '\0';
-
-#if 0
-    printf(":1: level name: '%s'\n", level->name);
-#endif
   }
   else if (*last_comment != '\0')
   {
     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
     level->name[MAX_LEVEL_NAME_LEN] = '\0';
-
-#if 0
-    printf(":2: level name: '%s'\n", level->name);
-#endif
   }
   else
   {
@@ -6800,11 +5747,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
 
   if (load_xsb_to_ces)
   {
-#if 1
-    /* !!! special global settings can now be set in level template !!! */
-#else
-    level->initial_player_stepsize[0] = STEPSIZE_SLOW;
-#endif
+    /* special global settings can now be set in level template */
 
     /* fill smaller playfields with padding "beyond border wall" elements */
     if (level->fieldx < SCR_FIELDX ||
@@ -6842,14 +5785,16 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
 /* ------------------------------------------------------------------------- */
 
 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+                                    struct LevelFileInfo *level_file_info,
+                                    boolean level_info_only)
 {
-  if (!LoadNativeLevel_EM(level_file_info->filename))
+  if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
     level->no_valid_file = TRUE;
 }
 
 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+                                    struct LevelFileInfo *level_file_info,
+                                    boolean level_info_only)
 {
   int pos = 0;
 
@@ -6857,7 +5802,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
   if (level_file_info->packed)
     pos = level_file_info->nr - leveldir_current->first_level;
 
-  if (!LoadNativeLevel_SP(level_file_info->filename, pos))
+  if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
     level->no_valid_file = TRUE;
 }
 
@@ -6896,44 +5841,49 @@ void SaveNativeLevel(struct LevelInfo *level)
 /* functions for loading generic level                                       */
 /* ------------------------------------------------------------------------- */
 
-void LoadLevelFromFileInfo(struct LevelInfo *level,
-                          struct LevelFileInfo *level_file_info)
+static void LoadLevelFromFileInfo(struct LevelInfo *level,
+                                 struct LevelFileInfo *level_file_info,
+                                 boolean level_info_only)
 {
   /* always start with reliable default values */
-  setLevelInfoToDefaults(level);
+  setLevelInfoToDefaults(level, level_info_only);
 
   switch (level_file_info->type)
   {
     case LEVEL_FILE_TYPE_RND:
-      LoadLevelFromFileInfo_RND(level, level_file_info);
+      LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
       break;
 
     case LEVEL_FILE_TYPE_EM:
-      LoadLevelFromFileInfo_EM(level, level_file_info);
+      LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
       level->game_engine_type = GAME_ENGINE_TYPE_EM;
       break;
 
     case LEVEL_FILE_TYPE_SP:
-      LoadLevelFromFileInfo_SP(level, level_file_info);
+      LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
       level->game_engine_type = GAME_ENGINE_TYPE_SP;
       break;
 
     case LEVEL_FILE_TYPE_DC:
-      LoadLevelFromFileInfo_DC(level, level_file_info);
+      LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
       break;
 
     case LEVEL_FILE_TYPE_SB:
-      LoadLevelFromFileInfo_SB(level, level_file_info);
+      LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
       break;
 
     default:
-      LoadLevelFromFileInfo_RND(level, level_file_info);
+      LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
       break;
   }
 
   /* if level file is invalid, restore level structure to default values */
   if (level->no_valid_file)
-    setLevelInfoToDefaults(level);
+  {
+    setLevelInfoToDefaults(level, level_info_only);
+
+    level->no_valid_file = TRUE;       /* but keep "no valid file" flag */
+  }
 
   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
     level->game_engine_type = GAME_ENGINE_TYPE_RND;
@@ -6953,7 +5903,7 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
   level_file_info.type = LEVEL_FILE_TYPE_RND;  /* no others supported yet */
   level_file_info.filename = filename;
 
-  LoadLevelFromFileInfo(level, &level_file_info);
+  LoadLevelFromFileInfo(level, &level_file_info, FALSE);
 }
 
 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
@@ -6970,10 +5920,6 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
     level->score[SC_TIME_BONUS] /= 10;
   }
 
-#if 0
-  leveldir_current->latest_engine = TRUE;      /* !!! TEST ONLY !!! */
-#endif
-
   if (leveldir_current->latest_engine)
   {
     /* ---------- use latest game engine ----------------------------------- */
@@ -7036,11 +5982,6 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
     /* extra time score was same value as time left score before 3.2.0-5 */
     level->extra_time_score = level->score[SC_TIME_BONUS];
-
-#if 0
-    /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
-    level->score[SC_TIME_BONUS] /= 10;
-#endif
   }
 
   if (level->game_version < VERSION_IDENT(3,2,0,7))
@@ -7105,7 +6046,6 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       change->target_element = EL_PLAYER_1;
   }
 
-#if 1
   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
   if (level->game_version < VERSION_IDENT(3,2,5,0))
   {
@@ -7124,40 +6064,11 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
        actions will get executed for this CE, too (which is wrong), while
        further changes are ignored (which is correct). As this bugfix breaks
        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
-       few other levels like Alan Bond's "FMV"), allow the previous, incorrect
-       behaviour for existing levels and tapes that make use of this bug */
-
-    level->use_action_after_change_bug = TRUE;
-  }
-#else
-  /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
-  /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
-  {
-    int element = EL_CUSTOM_16;
-    struct ElementInfo *ei = &element_info[element];
-
-    /* This is needed to fix a problem that was caused by a bugfix in function
-       game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
-       corrects the behaviour when a custom element changes to another custom
-       element with a higher element number that has change actions defined.
-       Normally, only one change per frame is allowed for custom elements.
-       Therefore, it is checked if a custom element already changed in the
-       current frame; if it did, subsequent changes are suppressed.
-       Unfortunately, this is only checked for element changes, but not for
-       change actions, which are still executed. As the function above loops
-       through all custom elements from lower to higher, an element change
-       resulting in a lower CE number won't be checked again, while a target
-       element with a higher number will also be checked, and potential change
-       actions will get executed for this CE, too (which is wrong), while
-       further changes are ignored (which is correct). As this bugfix breaks
-       Zelda II (but no other levels), allow the previous, incorrect behaviour
-       for this outstanding level set to not break the game or existing tapes */
+       few other levels like Alan Bond's "FMV"), allow the previous, incorrect
+       behaviour for existing levels and tapes that make use of this bug */
 
-    if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
-       strncmp(ei->description, "scanline - row 1", 16) == 0)
-      level->use_action_after_change_bug = TRUE;
+    level->use_action_after_change_bug = TRUE;
   }
-#endif
 
   /* not centering level after relocating player was default only in 3.2.3 */
   if (level->game_version == VERSION_IDENT(3,2,3,0))   /* (no pre-releases) */
@@ -7310,6 +6221,12 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
                                                     level->game_version);
 
+  /* clear unused playfield data (nicer if level gets resized in editor) */
+  for (x = 0; x < MAX_LEV_FIELDX; x++)
+    for (y = 0; y < MAX_LEV_FIELDY; y++)
+      if (x >= level->fieldx || y >= level->fieldy)
+       level->field[x][y] = EL_EMPTY;
+
   /* copy elements to runtime playfield array */
   for (x = 0; x < MAX_LEV_FIELDX; x++)
     for (y = 0; y < MAX_LEV_FIELDY; y++)
@@ -7341,7 +6258,7 @@ void LoadLevelTemplate(int nr)
   setLevelFileInfo(&level_template.file_info, nr);
   filename = level_template.file_info.filename;
 
-  LoadLevelFromFileInfo(&level_template, &level_template.file_info);
+  LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
 
   LoadLevel_InitVersion(&level_template, filename);
   LoadLevel_InitElements(&level_template, filename);
@@ -7356,7 +6273,7 @@ void LoadLevel(int nr)
   setLevelFileInfo(&level.file_info, nr);
   filename = level.file_info.filename;
 
-  LoadLevelFromFileInfo(&level, &level.file_info);
+  LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
 
   if (level.use_custom_template)
     LoadLevelTemplate(-1);
@@ -7368,6 +6285,13 @@ void LoadLevel(int nr)
   LoadLevel_InitNativeEngines(&level, filename);
 }
 
+void LoadLevelInfoOnly(int nr)
+{
+  setLevelFileInfo(&level.file_info, nr);
+
+  LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
+}
+
 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
 {
   int chunk_size = 0;
@@ -7389,7 +6313,7 @@ static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
   return chunk_size;
 }
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
 {
   int i, x, y;
@@ -7463,7 +6387,7 @@ static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
   return chunk_size;
 }
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
 {
   int chunk_size = 0;
@@ -7492,7 +6416,7 @@ static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   return chunk_size;
 }
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
 {
   int i, x, y;
@@ -7512,7 +6436,7 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
 {
   int i, x, y;
@@ -7565,7 +6489,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
 {
   int envelope_nr = element - EL_ENVELOPE_1;
@@ -7588,7 +6512,7 @@ static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -7619,7 +6543,7 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -7648,7 +6572,7 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -7731,7 +6655,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 {
   struct ElementInfo *ei = &element_info[element];
@@ -7851,7 +6775,7 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 }
 #endif
 
-#if 0
+#if ENABLE_HISTORIC_CHUNKS
 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
 {
   struct ElementInfo *ei = &element_info[element];
@@ -8053,12 +6977,6 @@ static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
   /* set default description string for this specific element */
   strcpy(xx_default_description, getDefaultElementDescription(ei));
 
-#if 0
-  /* set (fixed) number of content areas (may be wrong by broken level file) */
-  /* (this is now directly corrected for broken level files after loading) */
-  xx_num_contents = 1;
-#endif
-
   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
 
@@ -8218,12 +7136,12 @@ boolean SaveLevelChecked(int nr)
   boolean new_level = !fileExists(filename);
   boolean level_saved = FALSE;
 
-  if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
+  if (new_level || Request("Save this level and kill the old?", REQ_ASK))
   {
     SaveLevel(nr);
 
     if (new_level)
-      Request("Level saved !", REQ_CONFIRM);
+      Request("Level saved!", REQ_CONFIRM);
 
     level_saved = TRUE;
   }
@@ -8240,34 +7158,34 @@ void DumpLevel(struct LevelInfo *level)
     return;
   }
 
-  printf_line("-", 79);
-  printf("Level xxx (file version %08d, game version %08d)\n",
-        level->file_version, level->game_version);
-  printf_line("-", 79);
+  PrintLine("-", 79);
+  Print("Level xxx (file version %08d, game version %08d)\n",
+       level->file_version, level->game_version);
+  PrintLine("-", 79);
 
-  printf("Level author: '%s'\n", level->author);
-  printf("Level title:  '%s'\n", level->name);
-  printf("\n");
-  printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
-  printf("\n");
-  printf("Level time:  %d seconds\n", level->time);
-  printf("Gems needed: %d\n", level->gems_needed);
-  printf("\n");
-  printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
-  printf("Time for wheel:      %d seconds\n", level->time_wheel);
-  printf("Time for light:      %d seconds\n", level->time_light);
-  printf("Time for timegate:   %d seconds\n", level->time_timegate);
-  printf("\n");
-  printf("Amoeba speed: %d\n", level->amoeba_speed);
-  printf("\n");
+  Print("Level author: '%s'\n", level->author);
+  Print("Level title:  '%s'\n", level->name);
+  Print("\n");
+  Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
+  Print("\n");
+  Print("Level time:  %d seconds\n", level->time);
+  Print("Gems needed: %d\n", level->gems_needed);
+  Print("\n");
+  Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
+  Print("Time for wheel:      %d seconds\n", level->time_wheel);
+  Print("Time for light:      %d seconds\n", level->time_light);
+  Print("Time for timegate:   %d seconds\n", level->time_timegate);
+  Print("\n");
+  Print("Amoeba speed: %d\n", level->amoeba_speed);
+  Print("\n");
 
-  printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
-  printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
-  printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
-  printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
-  printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
+  Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
+  Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
+  Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
+  Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
+  Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
 
-  printf_line("-", 79);
+  PrintLine("-", 79);
 }
 
 
@@ -8301,7 +7219,7 @@ static void setTapeInfoToDefaults()
   tape.no_valid_file = FALSE;
 }
 
-static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
+static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
 {
   tape->file_version = getFileVersion(file);
   tape->game_version = getFileVersion(file);
@@ -8309,7 +7227,7 @@ static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
-static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
+static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int i;
 
@@ -8348,7 +7266,7 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
-static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
+static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int level_identifier_size;
   int i;
@@ -8368,7 +7286,7 @@ static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
-static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
+static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int i, j;
   int chunk_size_expected =
@@ -8442,7 +7360,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
-    if (feof(file))
+    if (checkEndOfFile(file))
       break;
   }
 
@@ -8452,17 +7370,98 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
+void LoadTape_SokobanSolution(char *filename)
+{
+  File *file;
+  int move_delay = TILESIZE / level.initial_player_stepsize[0];
+
+  if (!(file = openFile(filename, MODE_READ)))
+  {
+    tape.no_valid_file = TRUE;
+
+    return;
+  }
+
+  while (!checkEndOfFile(file))
+  {
+    unsigned char c = getByteFromFile(file);
+
+    if (checkEndOfFile(file))
+      break;
+
+    switch (c)
+    {
+      case 'u':
+      case 'U':
+       tape.pos[tape.length].action[0] = MV_UP;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'd':
+      case 'D':
+       tape.pos[tape.length].action[0] = MV_DOWN;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'l':
+      case 'L':
+       tape.pos[tape.length].action[0] = MV_LEFT;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'r':
+      case 'R':
+       tape.pos[tape.length].action[0] = MV_RIGHT;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case '\n':
+      case '\r':
+      case '\t':
+      case ' ':
+       /* ignore white-space characters */
+       break;
+
+      default:
+       tape.no_valid_file = TRUE;
+
+       Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
+
+       break;
+    }
+  }
+
+  closeFile(file);
+
+  if (tape.no_valid_file)
+    return;
+
+  tape.length_frames  = GetTapeLengthFrames();
+  tape.length_seconds = GetTapeLengthSeconds();
+}
+
 void LoadTapeFromFilename(char *filename)
 {
   char cookie[MAX_LINE_LEN];
   char chunk_name[CHUNK_ID_LEN + 1];
-  FILE *file;
+  File *file;
   int chunk_size;
 
   /* always start with reliable default values */
   setTapeInfoToDefaults();
 
-  if (!(file = fopen(filename, MODE_READ)))
+  if (strSuffix(filename, ".sln"))
+  {
+    LoadTape_SokobanSolution(filename);
+
+    return;
+  }
+
+  if (!(file = openFile(filename, MODE_READ)))
   {
     tape.no_valid_file = TRUE;
 
@@ -8480,14 +7479,17 @@ void LoadTapeFromFilename(char *filename)
       tape.no_valid_file = TRUE;
 
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
   }
   else /* check for pre-2.0 file format with cookie string */
   {
     strcpy(cookie, chunk_name);
-    fgets(&cookie[4], MAX_LINE_LEN - 4, file);
+    if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
+      cookie[4] = '\0';
     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
       cookie[strlen(cookie) - 1] = '\0';
 
@@ -8496,7 +7498,9 @@ void LoadTapeFromFilename(char *filename)
       tape.no_valid_file = TRUE;
 
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
 
@@ -8505,7 +7509,9 @@ void LoadTapeFromFilename(char *filename)
       tape.no_valid_file = TRUE;
 
       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
-      fclose(file);
+
+      closeFile(file);
+
       return;
     }
 
@@ -8525,7 +7531,7 @@ void LoadTapeFromFilename(char *filename)
     {
       char *name;
       int size;
-      int (*loader)(FILE *, int, struct TapeInfo *);
+      int (*loader)(File *, int, struct TapeInfo *);
     }
     chunk_info[] =
     {
@@ -8575,13 +7581,14 @@ void LoadTapeFromFilename(char *filename)
     }
   }
 
-  fclose(file);
+  closeFile(file);
 
-  tape.length_seconds = GetTapeLength();
+  tape.length_frames  = GetTapeLengthFrames();
+  tape.length_seconds = GetTapeLengthSeconds();
 
 #if 0
-  printf("::: tape file version: %d\n", tape.file_version);
-  printf("::: tape game version: %d\n", tape.game_version);
+  printf("::: tape file version: %d\n",   tape.file_version);
+  printf("::: tape game version: %d\n",   tape.game_version);
   printf("::: tape engine version: %d\n", tape.engine_version);
 #endif
 }
@@ -8599,12 +7606,10 @@ void LoadSolutionTape(int nr)
 
   LoadTapeFromFilename(filename);
 
-#if 1
   if (TAPE_IS_EMPTY(tape) &&
       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
       level.native_sp_level->demo.is_available)
     CopyNativeTape_SP_to_RND(&level);
-#endif
 }
 
 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
@@ -8666,9 +7671,6 @@ void SaveTape(int nr)
 {
   char *filename = getTapeFilename(nr);
   FILE *file;
-#if 0
-  boolean new_tape = TRUE;
-#endif
   int num_participating_players = 0;
   int info_chunk_size;
   int body_chunk_size;
@@ -8676,16 +7678,6 @@ void SaveTape(int nr)
 
   InitTapeDirectory(leveldir_current->subdir);
 
-#if 0
-  /* if a tape still exists, ask to overwrite it */
-  if (fileExists(filename))
-  {
-    new_tape = FALSE;
-    if (!Request("Replace old tape ?", REQ_ASK))
-      return;
-  }
-#endif
-
   if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
@@ -8723,11 +7715,6 @@ void SaveTape(int nr)
   SetFilePermissions(filename, PERMS_PRIVATE);
 
   tape.changed = FALSE;
-
-#if 0
-  if (new_tape)
-    Request("Tape saved !", REQ_CONFIRM);
-#endif
 }
 
 boolean SaveTapeChecked(int nr)
@@ -8736,12 +7723,12 @@ boolean SaveTapeChecked(int nr)
   boolean new_tape = !fileExists(filename);
   boolean tape_saved = FALSE;
 
-  if (new_tape || Request("Replace old tape ?", REQ_ASK))
+  if (new_tape || Request("Replace old tape?", REQ_ASK))
   {
     SaveTape(nr);
 
     if (new_tape)
-      Request("Tape saved !", REQ_CONFIRM);
+      Request("Tape saved!", REQ_CONFIRM);
 
     tape_saved = TRUE;
   }
@@ -8761,13 +7748,13 @@ void DumpTape(struct TapeInfo *tape)
     return;
   }
 
-  printf_line("-", 79);
-  printf("Tape of Level %03d (file version %08d, game version %08d)\n",
-        tape->level_nr, tape->file_version, tape->game_version);
-  printf("                  (effective engine version %08d)\n",
-        tape->engine_version);
-  printf("Level series identifier: '%s'\n", tape->level_identifier);
-  printf_line("-", 79);
+  PrintLine("-", 79);
+  Print("Tape of Level %03d (file version %08d, game version %08d)\n",
+       tape->level_nr, tape->file_version, tape->game_version);
+  Print("                  (effective engine version %08d)\n",
+       tape->engine_version);
+  Print("Level series identifier: '%s'\n", tape->level_identifier);
+  PrintLine("-", 79);
 
   tape_frame_counter = 0;
 
@@ -8776,7 +7763,7 @@ void DumpTape(struct TapeInfo *tape)
     if (i >= MAX_TAPE_LEN)
       break;
 
-    printf("%04d: ", i);
+    Print("%04d: ", i);
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
@@ -8784,24 +7771,24 @@ void DumpTape(struct TapeInfo *tape)
       {
        int action = tape->pos[i].action[j];
 
-       printf("%d:%02x ", j, action);
-       printf("[%c%c%c%c|%c%c] - ",
-              (action & JOY_LEFT ? '<' : ' '),
-              (action & JOY_RIGHT ? '>' : ' '),
-              (action & JOY_UP ? '^' : ' '),
-              (action & JOY_DOWN ? 'v' : ' '),
-              (action & JOY_BUTTON_1 ? '1' : ' '),
-              (action & JOY_BUTTON_2 ? '2' : ' '));
+       Print("%d:%02x ", j, action);
+       Print("[%c%c%c%c|%c%c] - ",
+             (action & JOY_LEFT ? '<' : ' '),
+             (action & JOY_RIGHT ? '>' : ' '),
+             (action & JOY_UP ? '^' : ' '),
+             (action & JOY_DOWN ? 'v' : ' '),
+             (action & JOY_BUTTON_1 ? '1' : ' '),
+             (action & JOY_BUTTON_2 ? '2' : ' '));
       }
     }
 
-    printf("(%03d) ", tape->pos[i].delay);
-    printf("[%05d]\n", tape_frame_counter);
+    Print("(%03d) ", tape->pos[i].delay);
+    Print("[%05d]\n", tape_frame_counter);
 
     tape_frame_counter += tape->pos[i].delay;
   }
 
-  printf_line("-", 79);
+  PrintLine("-", 79);
 }
 
 
@@ -8829,7 +7816,8 @@ void LoadScore(int nr)
     return;
 
   /* check file identifier */
-  fgets(cookie, MAX_LINE_LEN, file);
+  if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
+    cookie[0] = '\0';
   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
     cookie[strlen(cookie) - 1] = '\0';
 
@@ -8842,10 +7830,12 @@ void LoadScore(int nr)
 
   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
   {
-    fscanf(file, "%d", &highscore[i].Score);
-    fgets(line, MAX_LINE_LEN, file);
+    if (fscanf(file, "%d", &highscore[i].Score) == EOF)
+      Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
+    if (fgets(line, MAX_LINE_LEN, file) == NULL)
+      line[0] = '\0';
 
-    if (line[strlen(line) - 1] == '\n')
+    if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
       line[strlen(line) - 1] = '\0';
 
     for (line_ptr = line; *line_ptr; line_ptr++)
@@ -8902,7 +7892,7 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_TOONS                      5
 #define SETUP_TOKEN_SCROLL_DELAY               6
 #define SETUP_TOKEN_SCROLL_DELAY_VALUE         7
-#define SETUP_TOKEN_SOFT_SCROLLING             8
+#define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE       8
 #define SETUP_TOKEN_FADE_SCREENS               9
 #define SETUP_TOKEN_AUTORECORD                 10
 #define SETUP_TOKEN_SHOW_TITLESCREEN           11
@@ -8913,21 +7903,31 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_TIME_LIMIT                 16
 #define SETUP_TOKEN_FULLSCREEN                 17
 #define SETUP_TOKEN_FULLSCREEN_MODE            18
-#define SETUP_TOKEN_ASK_ON_ESCAPE              19
-#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR       20
-#define SETUP_TOKEN_QUICK_SWITCH               21
-#define SETUP_TOKEN_INPUT_ON_FOCUS             22
-#define SETUP_TOKEN_PREFER_AGA_GRAPHICS                23
-#define SETUP_TOKEN_GAME_FRAME_DELAY           24
-#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS    25
-#define SETUP_TOKEN_GRAPHICS_SET               26
-#define SETUP_TOKEN_SOUNDS_SET                 27
-#define SETUP_TOKEN_MUSIC_SET                  28
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    29
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      30
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       31
-
-#define NUM_GLOBAL_SETUP_TOKENS                        32
+#define SETUP_TOKEN_WINDOW_SCALING_PERCENT     19
+#define SETUP_TOKEN_WINDOW_SCALING_QUALITY     20
+#define SETUP_TOKEN_ASK_ON_ESCAPE              21
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR       22
+#define SETUP_TOKEN_QUICK_SWITCH               23
+#define SETUP_TOKEN_INPUT_ON_FOCUS             24
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS                25
+#define SETUP_TOKEN_GAME_FRAME_DELAY           26
+#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS    27
+#define SETUP_TOKEN_SMALL_GAME_GRAPHICS                28
+#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS      29
+#define SETUP_TOKEN_GRAPHICS_SET               30
+#define SETUP_TOKEN_SOUNDS_SET                 31
+#define SETUP_TOKEN_MUSIC_SET                  32
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    33
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      34
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       35
+#define SETUP_TOKEN_VOLUME_SIMPLE              36
+#define SETUP_TOKEN_VOLUME_LOOPS               37
+#define SETUP_TOKEN_VOLUME_MUSIC               38
+#define SETUP_TOKEN_TOUCH_CONTROL_TYPE         39
+#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE                40
+#define SETUP_TOKEN_TOUCH_DROP_DISTANCE                41
+
+#define NUM_GLOBAL_SETUP_TOKENS                        42
 
 /* editor setup */
 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
@@ -8979,15 +7979,20 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4    6
 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL  7
 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT                8
-#define SETUP_TOKEN_SHORTCUT_TAPE_STOP         9
-#define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE                10
-#define SETUP_TOKEN_SHORTCUT_TAPE_RECORD       11
-#define SETUP_TOKEN_SHORTCUT_TAPE_PLAY         12
-#define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE      13
-#define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS       14
-#define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC       15
-
-#define NUM_SHORTCUT_SETUP_TOKENS              16
+#define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA                9
+#define SETUP_TOKEN_SHORTCUT_TAPE_STOP         10
+#define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE                11
+#define SETUP_TOKEN_SHORTCUT_TAPE_RECORD       12
+#define SETUP_TOKEN_SHORTCUT_TAPE_PLAY         13
+#define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE      14
+#define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS       15
+#define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC       16
+#define SETUP_TOKEN_SHORTCUT_SNAP_LEFT         17
+#define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT                18
+#define SETUP_TOKEN_SHORTCUT_SNAP_UP           19
+#define SETUP_TOKEN_SHORTCUT_SNAP_DOWN         20
+
+#define NUM_SHORTCUT_SETUP_TOKENS              21
 
 /* player setup */
 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK                0
@@ -9016,6 +8021,25 @@ void SaveScore(int nr)
 
 #define NUM_SYSTEM_SETUP_TOKENS                        3
 
+/* internal setup */
+#define SETUP_TOKEN_INT_PROGRAM_TITLE          0
+#define SETUP_TOKEN_INT_PROGRAM_AUTHOR         1
+#define SETUP_TOKEN_INT_PROGRAM_EMAIL          2
+#define SETUP_TOKEN_INT_PROGRAM_WEBSITE                3
+#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT      4
+#define SETUP_TOKEN_INT_PROGRAM_COMPANY                5
+#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE      6
+#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET   7
+#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET     8
+#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET      9
+#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
+#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE   11
+#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE    12
+#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES   13
+#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
+
+#define NUM_INTERNAL_SETUP_TOKENS              15
+
 /* options setup */
 #define SETUP_TOKEN_OPTIONS_VERBOSE            0
 
@@ -9028,6 +8052,7 @@ static struct SetupEditorCascadeInfo seci;
 static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
 static struct SetupSystemInfo syi;
+static struct SetupInternalInfo sxi;
 static struct OptionInfo soi;
 
 static struct TokenInfo global_setup_tokens[] =
@@ -9040,7 +8065,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.toons,                   "toons"                  },
   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"           },
   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"     },
-  { TYPE_SWITCH, &si.soft_scrolling,          "soft_scrolling"         },
+  { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"   },
   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"           },
   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"       },
@@ -9051,6 +8076,8 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.time_limit,              "time_limit"             },
   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"             },
   { TYPE_STRING, &si.fullscreen_mode,         "fullscreen_mode"                },
+  { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent" },
+  { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality" },
   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"          },
   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"   },
   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"    },
@@ -9058,27 +8085,24 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"    },
   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"       },
   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements"        },
+  { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"    },
+  { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"  },
   { TYPE_STRING, &si.graphics_set,            "graphics_set"           },
   { TYPE_STRING, &si.sounds_set,              "sounds_set"             },
   { TYPE_STRING, &si.music_set,               "music_set"              },
   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics"        },
   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"  },
   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"   },
+  { TYPE_INTEGER,&si.volume_simple,           "volume_simple"          },
+  { TYPE_INTEGER,&si.volume_loops,            "volume_loops"           },
+  { TYPE_INTEGER,&si.volume_music,            "volume_music"           },
+  { TYPE_STRING, &si.touch.control_type,      "touch.control_type"     },
+  { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"    },
+  { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"    },
 };
 
-static boolean not_used = FALSE;
 static struct TokenInfo editor_setup_tokens[] =
 {
-#if 1
-  { TYPE_SWITCH, &not_used,            "editor.el_boulderdash"         },
-  { TYPE_SWITCH, &not_used,            "editor.el_emerald_mine"        },
-  { TYPE_SWITCH, &not_used,            "editor.el_emerald_mine_club"   },
-  { TYPE_SWITCH, &not_used,            "editor.el_more"                },
-  { TYPE_SWITCH, &not_used,            "editor.el_sokoban"             },
-  { TYPE_SWITCH, &not_used,            "editor.el_supaplex"            },
-  { TYPE_SWITCH, &not_used,            "editor.el_diamond_caves"       },
-  { TYPE_SWITCH, &not_used,            "editor.el_dx_boulderdash"      },
-#else
   { TYPE_SWITCH, &sei.el_boulderdash,  "editor.el_boulderdash"         },
   { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine"        },
   { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
@@ -9087,15 +8111,10 @@ static struct TokenInfo editor_setup_tokens[] =
   { TYPE_SWITCH, &sei.el_supaplex,     "editor.el_supaplex"            },
   { TYPE_SWITCH, &sei.el_diamond_caves,        "editor.el_diamond_caves"       },
   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"     },
-#endif
   { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
   { TYPE_SWITCH, &sei.el_steel_chars,  "editor.el_steel_chars"         },
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
-#if 1
-  { TYPE_SWITCH, &not_used,            "editor.el_headlines"           },
-#else
   { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
-#endif
   { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined"        },
   { TYPE_SWITCH, &sei.el_dynamic,      "editor.el_dynamic"             },
   { TYPE_SWITCH, &sei.el_by_game,      "editor.el_by_game"             },
@@ -9133,6 +8152,7 @@ static struct TokenInfo shortcut_setup_tokens[] =
   { TYPE_KEY_X11, &ssi.focus_player[3],        "shortcut.focus_player_4"       },
   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"    },
   { TYPE_KEY_X11, &ssi.tape_eject,     "shortcut.tape_eject"           },
+  { TYPE_KEY_X11, &ssi.tape_extra,     "shortcut.tape_extra"           },
   { TYPE_KEY_X11, &ssi.tape_stop,      "shortcut.tape_stop"            },
   { TYPE_KEY_X11, &ssi.tape_pause,     "shortcut.tape_pause"           },
   { TYPE_KEY_X11, &ssi.tape_record,    "shortcut.tape_record"          },
@@ -9140,6 +8160,10 @@ static struct TokenInfo shortcut_setup_tokens[] =
   { TYPE_KEY_X11, &ssi.sound_simple,   "shortcut.sound_simple"         },
   { TYPE_KEY_X11, &ssi.sound_loops,    "shortcut.sound_loops"          },
   { TYPE_KEY_X11, &ssi.sound_music,    "shortcut.sound_music"          },
+  { TYPE_KEY_X11, &ssi.snap_left,      "shortcut.snap_left"            },
+  { TYPE_KEY_X11, &ssi.snap_right,     "shortcut.snap_right"           },
+  { TYPE_KEY_X11, &ssi.snap_up,                "shortcut.snap_up"              },
+  { TYPE_KEY_X11, &ssi.snap_down,      "shortcut.snap_down"            },
 };
 
 static struct TokenInfo player_setup_tokens[] =
@@ -9164,11 +8188,30 @@ static struct TokenInfo player_setup_tokens[] =
 
 static struct TokenInfo system_setup_tokens[] =
 {
-  { TYPE_STRING,  &syi.sdl_videodriver,        "system.sdl_videodriver"        },
-  { TYPE_STRING,  &syi.sdl_audiodriver,        "system.sdl_audiodriver"        },
+  { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"    },
+  { TYPE_STRING,  &syi.sdl_audiodriver,           "system.sdl_audiodriver"     },
   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size"        },
 };
 
+static struct TokenInfo internal_setup_tokens[] =
+{
+  { TYPE_STRING, &sxi.program_title,           "program_title"         },
+  { TYPE_STRING, &sxi.program_author,          "program_author"        },
+  { TYPE_STRING, &sxi.program_email,           "program_email"         },
+  { TYPE_STRING, &sxi.program_website,         "program_website"       },
+  { TYPE_STRING, &sxi.program_copyright,       "program_copyright"     },
+  { TYPE_STRING, &sxi.program_company,         "program_company"       },
+  { TYPE_STRING, &sxi.program_icon_file,       "program_icon_file"     },
+  { TYPE_STRING, &sxi.default_graphics_set,    "default_graphics_set"  },
+  { TYPE_STRING, &sxi.default_sounds_set,      "default_sounds_set"    },
+  { TYPE_STRING, &sxi.default_music_set,       "default_music_set"     },
+  { TYPE_STRING, &sxi.fallback_graphics_file,  "fallback_graphics_file"},
+  { TYPE_STRING, &sxi.fallback_sounds_file,    "fallback_sounds_file"  },
+  { TYPE_STRING, &sxi.fallback_music_file,     "fallback_music_file"   },
+  { TYPE_STRING, &sxi.default_level_series,    "default_level_series"  },
+  { TYPE_STRING, &sxi.choose_from_top_leveldir,        "choose_from_top_leveldir" },
+};
+
 static struct TokenInfo options_setup_tokens[] =
 {
   { TYPE_BOOLEAN, &soi.verbose,                "options.verbose"               },
@@ -9202,7 +8245,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->toons = TRUE;
   si->scroll_delay = TRUE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
-  si->soft_scrolling = TRUE;
+  si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
   si->fade_screens = TRUE;
   si->autorecord = TRUE;
   si->show_titlescreen = TRUE;
@@ -9213,6 +8256,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
   si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
+  si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
+  si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
   si->ask_on_escape = TRUE;
   si->ask_on_escape_editor = TRUE;
   si->quick_switch = FALSE;
@@ -9220,14 +8265,25 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->prefer_aga_graphics = TRUE;
   si->game_frame_delay = GAME_FRAME_DELAY;
   si->sp_show_border_elements = FALSE;
+  si->small_game_graphics = FALSE;
+  si->show_snapshot_buttons = FALSE;
+
+  si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+  si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
+  si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
 
-  si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
-  si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
-  si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
   si->override_level_graphics = FALSE;
   si->override_level_sounds = FALSE;
   si->override_level_music = FALSE;
 
+  si->volume_simple = 100;             /* percent */
+  si->volume_loops = 100;              /* percent */
+  si->volume_music = 100;              /* percent */
+
+  si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
+  si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;       /* percent */
+  si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;       /* percent */
+
   si->editor.el_boulderdash            = TRUE;
   si->editor.el_emerald_mine           = TRUE;
   si->editor.el_emerald_mine_club      = TRUE;
@@ -9257,6 +8313,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->shortcut.focus_player_all        = DEFAULT_KEY_FOCUS_PLAYER_ALL;
 
   si->shortcut.tape_eject      = DEFAULT_KEY_TAPE_EJECT;
+  si->shortcut.tape_extra      = DEFAULT_KEY_TAPE_EXTRA;
   si->shortcut.tape_stop       = DEFAULT_KEY_TAPE_STOP;
   si->shortcut.tape_pause      = DEFAULT_KEY_TAPE_PAUSE;
   si->shortcut.tape_record     = DEFAULT_KEY_TAPE_RECORD;
@@ -9266,6 +8323,11 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->shortcut.sound_loops     = DEFAULT_KEY_SOUND_LOOPS;
   si->shortcut.sound_music     = DEFAULT_KEY_SOUND_MUSIC;
 
+  si->shortcut.snap_left       = DEFAULT_KEY_SNAP_LEFT;
+  si->shortcut.snap_right      = DEFAULT_KEY_SNAP_RIGHT;
+  si->shortcut.snap_up         = DEFAULT_KEY_SNAP_UP;
+  si->shortcut.snap_down       = DEFAULT_KEY_SNAP_DOWN;
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     si->input[i].use_joystick = FALSE;
@@ -9290,14 +8352,30 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
 
+  si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
+  si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
+  si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
+  si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
+  si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
+  si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
+
+  si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
+
+  si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+  si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
+  si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
+
+  si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
+  si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
+  si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
+
+  si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
+  si->internal.choose_from_top_leveldir = FALSE;
+
   si->options.verbose = FALSE;
 
-#if defined(CREATE_SPECIAL_EDITION_RND_JUE)
-  si->handicap = FALSE;
+#if defined(PLATFORM_ANDROID)
   si->fullscreen = TRUE;
-  si->override_level_graphics = AUTO;
-  si->override_level_sounds = AUTO;
-  si->override_level_music = AUTO;
 #endif
 }
 
@@ -9375,6 +8453,13 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
                 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
   setup.system = syi;
 
+  /* internal setup */
+  sxi = setup.internal;
+  for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
+    setSetupInfo(internal_setup_tokens, i,
+                getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
+  setup.internal = sxi;
+
   /* options setup */
   soi = setup.options;
   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
@@ -9399,43 +8484,62 @@ static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
   setup.editor_cascade = seci;
 }
 
-void LoadSetup()
+void LoadSetupFromFilename(char *filename)
 {
-  char *filename = getSetupFilename();
-  SetupFileHash *setup_file_hash = NULL;
-
-  /* always start with reliable default values */
-  setSetupInfoToDefaults(&setup);
-
-  setup_file_hash = loadSetupFileHash(filename);
+  SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
 
   if (setup_file_hash)
   {
-    char *player_name_new;
-
-    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
     decodeSetupFileHash(setup_file_hash);
 
     freeSetupFileHash(setup_file_hash);
+  }
+  else
+  {
+    Error(ERR_WARN, "using default setup values");
+  }
+}
 
-    /* needed to work around problems with fixed length strings */
-    player_name_new = get_corrected_login_name(setup.player_name);
-    free(setup.player_name);
-    setup.player_name = player_name_new;
+static void LoadSetup_SpecialPostProcessing()
+{
+  char *player_name_new;
 
-    /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
-    if (setup.scroll_delay == FALSE)
-    {
-      setup.scroll_delay_value = MIN_SCROLL_DELAY;
-      setup.scroll_delay = TRUE;                       /* now always "on" */
-    }
+  /* needed to work around problems with fixed length strings */
+  player_name_new = get_corrected_login_name(setup.player_name);
+  free(setup.player_name);
+  setup.player_name = player_name_new;
 
-    /* make sure that scroll delay value stays inside valid range */
-    setup.scroll_delay_value =
-      MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
+  /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
+  if (setup.scroll_delay == FALSE)
+  {
+    setup.scroll_delay_value = MIN_SCROLL_DELAY;
+    setup.scroll_delay = TRUE;                 /* now always "on" */
   }
-  else
-    Error(ERR_WARN, "using default setup values");
+
+  /* make sure that scroll delay value stays inside valid range */
+  setup.scroll_delay_value =
+    MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
+}
+
+void LoadSetup()
+{
+  char *filename;
+
+  /* always start with reliable default values */
+  setSetupInfoToDefaults(&setup);
+
+  /* try to load setup values from default setup file */
+  filename = getDefaultSetupFilename();
+
+  if (fileExists(filename))
+    LoadSetupFromFilename(filename);
+
+  /* try to load setup values from user setup file */
+  filename = getSetupFilename();
+
+  LoadSetupFromFilename(filename);
+
+  LoadSetup_SpecialPostProcessing();
 }
 
 void LoadSetup_EditorCascade()
@@ -9450,7 +8554,6 @@ void LoadSetup_EditorCascade()
 
   if (setup_file_hash)
   {
-    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
     decodeSetupFileHash_EditorCascade(setup_file_hash);
 
     freeSetupFileHash(setup_file_hash);
@@ -9473,9 +8576,7 @@ void SaveSetup()
     return;
   }
 
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                              getCookie("SETUP")));
-  fprintf(file, "\n");
+  fprintFileHeader(file, SETUP_FILENAME);
 
   /* global setup */
   si = setup;
@@ -9483,7 +8584,9 @@ void SaveSetup()
   {
     /* just to make things nicer :) */
     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
-       i == SETUP_TOKEN_GRAPHICS_SET)
+       i == SETUP_TOKEN_GRAPHICS_SET ||
+       i == SETUP_TOKEN_VOLUME_SIMPLE ||
+       i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
       fprintf(file, "\n");
 
     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
@@ -9520,6 +8623,9 @@ void SaveSetup()
   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
 
+  /* internal setup */
+  /* (internal setup values not saved to user setup file) */
+
   /* options setup */
   soi = setup.options;
   fprintf(file, "\n");
@@ -9546,12 +8652,9 @@ void SaveSetup_EditorCascade()
     return;
   }
 
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                              getCookie("SETUP")));
-  fprintf(file, "\n");
+  fprintFileHeader(file, EDITORCASCADE_FILENAME);
 
   seci = setup.editor_cascade;
-  fprintf(file, "\n");
   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
 
@@ -9596,19 +8699,10 @@ void LoadCustomElementDescriptions()
 
 static int getElementFromToken(char *token)
 {
-#if 1
   char *value = getHashEntry(element_token_hash, token);
 
   if (value != NULL)
     return atoi(value);
-#else
-  int i;
-
-  /* !!! OPTIMIZE THIS BY USING HASH !!! */
-  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
-    if (strEqual(token, element_info[i].token_name))
-      return i;
-#endif
 
   Error(ERR_WARN, "unknown element token '%s'", token);
 
@@ -9626,25 +8720,8 @@ static int get_token_parameter_value(char *token, char *value_raw)
   if (suffix == NULL)
     suffix = token;
 
-#if 1
   if (strEqual(suffix, ".element"))
     return getElementFromToken(value_raw);
-#endif
-
-#if 0
-  if (strncmp(suffix, ".font", 5) == 0)
-  {
-    int i;
-
-    /* !!! OPTIMIZE THIS BY USING HASH !!! */
-    for (i = 0; i < NUM_FONTS; i++)
-      if (strEqual(value_raw, font_info[i].token_name))
-       return i;
-
-    /* if font not found, use reliable default value */
-    return FONT_INITIAL_1;
-  }
-#endif
 
   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
@@ -9652,24 +8729,8 @@ static int get_token_parameter_value(char *token, char *value_raw)
 
 void InitMenuDesignSettings_Static()
 {
-#if 0
-  static SetupFileHash *image_config_hash = NULL;
-#endif
   int i;
 
-#if 0
-  if (image_config_hash == NULL)
-  {
-    image_config_hash = newSetupFileHash();
-
-    for (i = 0; image_config[i].token != NULL; i++)
-      setHashEntry(image_config_hash,
-                  image_config[i].token,
-                  image_config[i].value);
-  }
-#endif
-
-#if 1
   /* always start with reliable default values from static default config */
   for (i = 0; image_config_vars[i].token != NULL; i++)
   {
@@ -9679,19 +8740,6 @@ void InitMenuDesignSettings_Static()
       *image_config_vars[i].value =
        get_token_parameter_value(image_config_vars[i].token, value);
   }
-
-#else
-
-  int j;
-
-  /* always start with reliable default values from static default config */
-  for (i = 0; image_config_vars[i].token != NULL; i++)
-    for (j = 0; image_config[j].token != NULL; j++)
-      if (strEqual(image_config_vars[i].token, image_config[j].token))
-       *image_config_vars[i].value =
-         get_token_parameter_value(image_config_vars[i].token,
-                                   image_config[j].value);
-#endif
 }
 
 static void InitMenuDesignSettings_SpecialPreProcessing()
@@ -9726,13 +8774,50 @@ static void InitMenuDesignSettings_SpecialPreProcessing()
     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
   }
+
+  /* special case: initialize "ARG_DEFAULT" values in static default config */
+  /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  {
+    viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
+    viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
+    if (i != GFX_SPECIAL_ARG_EDITOR)   /* editor value already initialized */
+      viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
+  }
 }
 
 static void InitMenuDesignSettings_SpecialPostProcessing()
 {
+  static struct
+  {
+    struct XY *dst, *src;
+  }
+  game_buttons_xy[] =
+  {
+    { &game.button.save,       &game.button.stop       },
+    { &game.button.pause2,     &game.button.pause      },
+    { &game.button.load,       &game.button.play       },
+    { &game.button.undo,       &game.button.stop       },
+    { &game.button.redo,       &game.button.play       },
+
+    { NULL,                    NULL                    }
+  };
+  int i;
+
   /* special case: initialize later added SETUP list size from LEVELS value */
   if (menu.list_size[GAME_MODE_SETUP] == -1)
     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
+
+  /* set default position for snapshot buttons to stop/pause/play buttons */
+  for (i = 0; game_buttons_xy[i].dst != NULL; i++)
+  {
+    if ((*game_buttons_xy[i].dst).x == -1 &&
+       (*game_buttons_xy[i].dst).y == -1)
+    {
+      (*game_buttons_xy[i].dst).x = (*game_buttons_xy[i].src).x;
+      (*game_buttons_xy[i].dst).y = (*game_buttons_xy[i].src).y;
+    }
+  }
 }
 
 static void LoadMenuDesignSettingsFromFilename(char *filename)
@@ -9775,10 +8860,6 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
   SetupFileHash *setup_file_hash;
   int i, j, k;
 
-#if 0
-  printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
-#endif
-
   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
     return;
 
@@ -9811,6 +8892,14 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
     if (value_2 != NULL)
       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
+
+    if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
+    {
+      char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
+
+      if (value_1 != NULL)
+       menu.list_size_info[i] = get_integer_from_string(value_1);
+    }
   }
 
   /* special case: initialize with default values that may be overwritten */
@@ -9863,6 +8952,78 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
                                                                  value_6);
   }
 
+  /* special case: initialize with default values that may be overwritten */
+  /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  {
+    char *token_01 = "viewport.playfield.x";
+    char *token_02 = "viewport.playfield.y";
+    char *token_03 = "viewport.playfield.width";
+    char *token_04 = "viewport.playfield.height";
+    char *token_05 = "viewport.playfield.border_size";
+    char *token_06 = "viewport.door_1.x";
+    char *token_07 = "viewport.door_1.y";
+    char *token_08 = "viewport.door_1.width";
+    char *token_09 = "viewport.door_1.height";
+    char *token_10 = "viewport.door_1.border_size";
+    char *token_11 = "viewport.door_2.x";
+    char *token_12 = "viewport.door_2.y";
+    char *token_13 = "viewport.door_2.width";
+    char *token_14 = "viewport.door_2.height";
+    char *token_15 = "viewport.door_2.border_size";
+    char *value_01 = getHashEntry(setup_file_hash, token_01);
+    char *value_02 = getHashEntry(setup_file_hash, token_02);
+    char *value_03 = getHashEntry(setup_file_hash, token_03);
+    char *value_04 = getHashEntry(setup_file_hash, token_04);
+    char *value_05 = getHashEntry(setup_file_hash, token_05);
+    char *value_06 = getHashEntry(setup_file_hash, token_06);
+    char *value_07 = getHashEntry(setup_file_hash, token_07);
+    char *value_08 = getHashEntry(setup_file_hash, token_08);
+    char *value_09 = getHashEntry(setup_file_hash, token_09);
+    char *value_10 = getHashEntry(setup_file_hash, token_10);
+    char *value_11 = getHashEntry(setup_file_hash, token_11);
+    char *value_12 = getHashEntry(setup_file_hash, token_12);
+    char *value_13 = getHashEntry(setup_file_hash, token_13);
+    char *value_14 = getHashEntry(setup_file_hash, token_14);
+    char *value_15 = getHashEntry(setup_file_hash, token_15);
+
+    if (value_01 != NULL)
+      viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
+    if (value_02 != NULL)
+      viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
+    if (value_03 != NULL)
+      viewport.playfield[i].width = get_token_parameter_value(token_03,
+                                                             value_03);
+    if (value_04 != NULL)
+      viewport.playfield[i].height = get_token_parameter_value(token_04,
+                                                              value_04);
+    if (value_05 != NULL)
+      viewport.playfield[i].border_size = get_token_parameter_value(token_05,
+                                                                   value_05);
+    if (value_06 != NULL)
+      viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
+    if (value_07 != NULL)
+      viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
+    if (value_08 != NULL)
+      viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
+    if (value_09 != NULL)
+      viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
+    if (value_10 != NULL)
+      viewport.door_1[i].border_size = get_token_parameter_value(token_10,
+                                                                value_10);
+    if (value_11 != NULL)
+      viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
+    if (value_12 != NULL)
+      viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
+    if (value_13 != NULL)
+      viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
+    if (value_14 != NULL)
+      viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
+    if (value_15 != NULL)
+      viewport.door_1[i].border_size = get_token_parameter_value(token_15,
+                                                                value_15);
+  }
+
   /* special case: initialize with default values that may be overwritten */
   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
@@ -9917,11 +9078,7 @@ void LoadMenuDesignSettings()
   InitMenuDesignSettings_Static();
   InitMenuDesignSettings_SpecialPreProcessing();
 
-#if 1
   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
-#else
-  if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
-#endif
   {
     /* first look for special settings configured in level series config */
     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
@@ -10060,10 +9217,6 @@ static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
     *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);
 
@@ -10077,10 +9230,6 @@ static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
     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);
 
@@ -10149,8 +9298,8 @@ void LoadMusicInfo()
   int num_music = getMusicListSize();
   int num_music_noconf = 0;
   int num_sounds = getSoundListSize();
-  DIR *dir;
-  struct dirent *dir_entry;
+  Directory *dir;
+  DirectoryEntry *dir_entry;
   struct FileInfo *music, *sound;
   struct MusicFileInfo *next, **new;
   int i;
@@ -10192,27 +9341,24 @@ void LoadMusicInfo()
     if (!FileIsMusic(music->filename))
       continue;
 
-#if 0
-    printf("::: -> '%s' (configured)\n", music->filename);
-#endif
-
     if (!music_info_listed(music_file_info, music->filename))
     {
       *new = get_music_file_info(music->filename, i);
+
       if (*new != NULL)
        new = &(*new)->next;
     }
   }
 
-  if ((dir = opendir(music_directory)) == NULL)
+  if ((dir = openDirectory(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 */
+  while ((dir_entry = readDirectory(dir)) != NULL)     /* loop all entries */
   {
-    char *basename = dir_entry->d_name;
+    char *basename = dir_entry->basename;
     boolean music_already_used = FALSE;
     int i;
 
@@ -10234,16 +9380,13 @@ void LoadMusicInfo()
     if (music_already_used)
       continue;
 
-    if (!FileIsMusic(basename))
+    if (!FileIsMusic(dir_entry->filename))
       continue;
 
-#if 0
-    printf("::: -> '%s' (found in directory)\n", basename);
-#endif
-
     if (!music_info_listed(music_file_info, basename))
     {
       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
+
       if (*new != NULL)
        new = &(*new)->next;
     }
@@ -10251,7 +9394,7 @@ void LoadMusicInfo()
     num_music_noconf++;
   }
 
-  closedir(dir);
+  closeDirectory(dir);
 
   for (i = 0; i < num_sounds; i++)
   {
@@ -10267,10 +9410,6 @@ void LoadMusicInfo()
     if (!FileIsSound(sound->filename))
       continue;
 
-#if 0
-    printf("::: -> '%s' (configured)\n", sound->filename);
-#endif
-
     if (!sound_info_listed(music_file_info, sound->filename))
     {
       *new = get_sound_file_info(sound->filename, i);
@@ -10278,11 +9417,6 @@ void LoadMusicInfo()
        new = &(*new)->next;
     }
   }
-
-#if 0
-  for (next = music_file_info; next != NULL; next = next->next)
-    printf("::: title == '%s'\n", next->title);
-#endif
 }
 
 void add_helpanim_entry(int element, int action, int direction, int delay,
@@ -10713,8 +9847,9 @@ void CreateLevelSketchImages()
     filename1 = getPath2(global.create_images_dir, basename1);
     filename2 = getPath2(global.create_images_dir, basename2);
 
-    getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
-    BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY, 0, 0);
+    getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+    BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
+              0, 0);
 
     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
@@ -10749,24 +9884,27 @@ void CreateLevelSketchImages()
 /* create and save images for custom and group elements (raw BMP format)     */
 /* ------------------------------------------------------------------------- */
 
-void CreateCustomElementImages()
+void CreateCustomElementImages(char *directory)
 {
 #if defined(TARGET_SDL)
-  char *filename = "graphics.classic/RocksCE.bmp";
-  Bitmap *bitmap;
+  char *src_basename = "RocksCE-template.ilbm";
+  char *dst_basename = "RocksCE.bmp";
+  char *src_filename = getPath2(directory, src_basename);
+  char *dst_filename = getPath2(directory, dst_basename);
   Bitmap *src_bitmap;
-  int dummy_graphic = IMG_CUSTOM_99;
+  Bitmap *bitmap;
   int yoffset_ce = 0;
   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
-  int src_x, src_y;
   int i;
 
+  SDLInitVideoDisplay();
+
+  src_bitmap = LoadImage(src_filename);
+
   bitmap = CreateBitmap(TILEX * 16 * 2,
                        TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
                        DEFAULT_DEPTH);
 
-  getGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
-
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int x = i % 16;
@@ -10777,18 +9915,22 @@ void CreateCustomElementImages()
     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
               TILEX * x, TILEY * y + yoffset_ce);
 
-    BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
-              TILEX * x + TILEX * 16, TILEY * y + yoffset_ce);
+    BlitBitmap(src_bitmap, bitmap, 0, TILEY,
+              TILEX, TILEY,
+              TILEX * x + TILEX * 16,
+              TILEY * y + yoffset_ce);
 
     for (j = 2; j >= 0; j--)
     {
       int c = ii % 10;
 
-      BlitBitmap(src_bitmap, bitmap, TILEX + c * 7, 0, 6, 10,
+      BlitBitmap(src_bitmap, bitmap,
+                TILEX + c * 7, 0, 6, 10,
                 TILEX * x + 6 + j * 7,
                 TILEY * y + 11 + yoffset_ce);
 
-      BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY, 6, 10,
+      BlitBitmap(src_bitmap, bitmap,
+                TILEX + c * 8, TILEY, 6, 10,
                 TILEX * 16 + TILEX * x + 6 + j * 8,
                 TILEY * y + 10 + yoffset_ce);
 
@@ -10806,8 +9948,10 @@ void CreateCustomElementImages()
     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
               TILEX * x, TILEY * y + yoffset_ge);
 
-    BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
-              TILEX * x + TILEX * 16, TILEY * y + yoffset_ge);
+    BlitBitmap(src_bitmap, bitmap, 0, TILEY,
+              TILEX, TILEY,
+              TILEX * x + TILEX * 16,
+              TILEY * y + yoffset_ge);
 
     for (j = 1; j >= 0; j--)
     {
@@ -10817,7 +9961,8 @@ void CreateCustomElementImages()
                 TILEX * x + 6 + j * 10,
                 TILEY * y + 11 + yoffset_ge);
 
-      BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY + 12, 6, 10,
+      BlitBitmap(src_bitmap, bitmap,
+                TILEX + c * 8, TILEY + 12, 6, 10,
                 TILEX * 16 + TILEX * x + 10 + j * 8,
                 TILEY * y + 10 + yoffset_ge);
 
@@ -10825,18 +9970,11 @@ void CreateCustomElementImages()
     }
   }
 
-  if (SDL_SaveBMP(bitmap->surface, filename) != 0)
-    Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
+  if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
+    Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
 
   FreeBitmap(bitmap);
 
   CloseAllAndExit(0);
 #endif
 }
-
-#if 0
-void CreateLevelSketchImages_TEST()
-{
-  void CreateCustomElementImages()
-}
-#endif