changed filename string handling for level file info structure
[rocksndiamonds.git] / src / files.c
index d1b60e47c704ccbaa6119f38035134a0c16404b9..b590641afc20b05848461be6327ae59081e52da5 100644 (file)
 
 #include "files.h"
 #include "init.h"
+#include "screens.h"
 #include "tools.h"
 #include "tape.h"
+#include "config.h"
 
 #define ENABLE_UNUSED_CODE     0       /* currently unused functions */
 #define ENABLE_HISTORIC_CHUNKS 0       /* only for historic reference */
@@ -56,7 +58,7 @@
 
 #define TAPE_CHUNK_VERS_SIZE   8       /* size of file version chunk */
 #define TAPE_CHUNK_HEAD_SIZE   20      /* size of tape file header   */
-#define TAPE_CHUNK_HEAD_UNUSED 3       /* unused tape header bytes   */
+#define TAPE_CHUNK_HEAD_UNUSED 2       /* unused tape header bytes   */
 
 #define LEVEL_CHUNK_CNT3_SIZE(x)        (LEVEL_CHUNK_CNT3_HEADER + (x))
 #define LEVEL_CHUNK_CUS3_SIZE(x)        (2 + (x) * LEVEL_CPART_CUS3_SIZE)
@@ -243,6 +245,12 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
     &li.auto_exit_sokoban,             FALSE
   },
 
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(10),
+    &li.auto_count_gems,               FALSE
+  },
+
   {
     -1,                                        -1,
     -1,                                        -1,
@@ -799,16 +807,69 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.num_ball_contents,             4, MAX_ELEMENT_CONTENTS
   },
 
-  /* ---------- unused values ----------------------------------------------- */
+  {
+    EL_MM_MCDUFFIN,                    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &li.mm_laser_red,                  FALSE
+  },
+  {
+    EL_MM_MCDUFFIN,                    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.mm_laser_green,                        FALSE
+  },
+  {
+    EL_MM_MCDUFFIN,                    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(3),
+    &li.mm_laser_blue,                 TRUE
+  },
 
   {
-    EL_UNKNOWN,                                SAVE_CONF_NEVER,
+    EL_DF_LASER,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &li.df_laser_red,                  TRUE
+  },
+  {
+    EL_DF_LASER,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.df_laser_green,                        TRUE
+  },
+  {
+    EL_DF_LASER,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(3),
+    &li.df_laser_blue,                 FALSE
+  },
+
+  {
+    EL_MM_FUSE_ACTIVE,                 -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
+    &li.mm_time_fuse,                  25
+  },
+  {
+    EL_MM_BOMB,                                -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
+    &li.mm_time_bomb,                  75
+  },
+  {
+    EL_MM_GRAY_BALL,                   -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
-    &li.score[SC_UNKNOWN_14],          10
+    &li.mm_time_ball,                  75
   },
+  {
+    EL_MM_STEEL_BLOCK,                 -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
+    &li.mm_time_block,                 75
+  },
+  {
+    EL_MM_LIGHTBALL,                   -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
+    &li.score[SC_ELEM_BONUS],          10
+  },
+
+  /* ---------- unused values ----------------------------------------------- */
+
   {
     EL_UNKNOWN,                                SAVE_CONF_NEVER,
-    TYPE_INTEGER,                      CONF_VALUE_16_BIT(2),
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
     &li.score[SC_UNKNOWN_15],          10
   },
 
@@ -1318,6 +1379,8 @@ filetype_id_list[] =
   { LEVEL_FILE_TYPE_DX,                "DX"    },
   { LEVEL_FILE_TYPE_SB,                "SB"    },
   { LEVEL_FILE_TYPE_DC,                "DC"    },
+  { LEVEL_FILE_TYPE_MM,                "MM"    },
+  { LEVEL_FILE_TYPE_MM,                "DF"    },
   { -1,                                NULL    },
 };
 
@@ -1600,9 +1663,11 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
 
   setLevelInfoToDefaults_EM();
   setLevelInfoToDefaults_SP();
+  setLevelInfoToDefaults_MM();
 
   level->native_em_level = &native_em_level;
   level->native_sp_level = &native_sp_level;
+  level->native_mm_level = &native_mm_level;
 
   level->file_version = FILE_VERSION_ACTUAL;
   level->game_version = GAME_VERSION_ACTUAL;
@@ -1633,6 +1698,9 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
 
   BorderElement = EL_STEELWALL;
 
+  /* detect custom elements when loading them */
+  level->file_has_custom_elements = FALSE;
+
   /* set all bug compatibility flags to "false" => do not emulate this bug */
   level->use_action_after_change_bug = FALSE;
 
@@ -1765,14 +1833,19 @@ static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
 }
 
 static void setLevelInfoToDefaults(struct LevelInfo *level,
-                                  boolean level_info_only)
+                                  boolean level_info_only,
+                                  boolean reset_file_status)
 {
   setLevelInfoToDefaults_Level(level);
 
   if (!level_info_only)
     setLevelInfoToDefaults_Elements(level);
 
-  level->no_valid_file = FALSE;
+  if (reset_file_status)
+  {
+    level->no_valid_file = FALSE;
+    level->no_level_file = FALSE;
+  }
 
   level->changed = FALSE;
 }
@@ -1782,14 +1855,46 @@ static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
   level_file_info->nr = 0;
   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
   level_file_info->packed = FALSE;
-  level_file_info->basename = NULL;
-  level_file_info->filename = NULL;
+
+  setString(&level_file_info->basename, NULL);
+  setString(&level_file_info->filename, NULL);
 }
 
+int getMappedElement_SB(int, boolean);
+
 static void ActivateLevelTemplate()
 {
   int x, y;
 
+  if (check_special_flags("load_xsb_to_ces"))
+  {
+    /* fill smaller playfields with padding "beyond border wall" elements */
+    if (level.fieldx < level_template.fieldx ||
+       level.fieldy < level_template.fieldy)
+    {
+      short field[level.fieldx][level.fieldy];
+      int new_fieldx = MAX(level.fieldx, level_template.fieldx);
+      int new_fieldy = MAX(level.fieldy, level_template.fieldy);
+      int pos_fieldx = (new_fieldx - level.fieldx) / 2;
+      int pos_fieldy = (new_fieldy - level.fieldy) / 2;
+
+      /* copy old playfield (which is smaller than the visible area) */
+      for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
+       field[x][y] = level.field[x][y];
+
+      /* fill new, larger playfield with "beyond border wall" elements */
+      for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
+       level.field[x][y] = getMappedElement_SB('_', TRUE);
+
+      /* copy the old playfield to the middle of the new playfield */
+      for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
+       level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
+
+      level.fieldx = new_fieldx;
+      level.fieldy = new_fieldy;
+    }
+  }
+
   /* Currently there is no special action needed to activate the template
      data, because 'element_info' property settings overwrite the original
      level data, while all other variables do not change. */
@@ -1830,13 +1935,14 @@ static void ActivateLevelTemplate()
 
 static char *getLevelFilenameFromBasename(char *basename)
 {
-  static char *filename = NULL;
+  static char *filename[2] = { NULL, NULL };
+  int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
 
-  checked_free(filename);
+  checked_free(filename[pos]);
 
-  filename = getPath2(getCurrentLevelDir(), basename);
+  filename[pos] = getPath2(getCurrentLevelDir(), basename);
 
-  return filename;
+  return filename[pos];
 }
 
 static int getFileTypeFromBasename(char *basename)
@@ -1877,6 +1983,26 @@ static int getFileTypeFromBasename(char *basename)
   return LEVEL_FILE_TYPE_UNKNOWN;
 }
 
+static int getFileTypeFromMagicBytes(char *filename, int type)
+{
+  File *file;
+
+  if ((file = openFile(filename, MODE_READ)))
+  {
+    char chunk_name[CHUNK_ID_LEN + 1];
+
+    getFileChunkBE(file, chunk_name, NULL);
+
+    if (strEqual(chunk_name, "MMII") ||
+       strEqual(chunk_name, "MIRR"))
+      type = LEVEL_FILE_TYPE_MM;
+
+    closeFile(file);
+  }
+
+  return type;
+}
+
 static boolean checkForPackageFromBasename(char *basename)
 {
   /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
@@ -1890,7 +2016,7 @@ static char *getSingleLevelBasenameExt(int nr, char *extension)
   static char basename[MAX_FILENAME_LEN];
 
   if (nr < 0)
-    sprintf(basename, "template.%s", extension);
+    sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
   else
     sprintf(basename, "%03d.%s", nr, extension);
 
@@ -1963,8 +2089,9 @@ static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
 {
   lfi->type = type;
   lfi->packed = FALSE;
-  lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
-  lfi->filename = getLevelFilenameFromBasename(lfi->basename);
+
+  setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
+  setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
 }
 #endif
 
@@ -1980,8 +2107,9 @@ static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
 
   lfi->type = type;
   lfi->packed = FALSE;
-  lfi->basename = basename;
-  lfi->filename = getLevelFilenameFromBasename(lfi->basename);
+
+  setString(&lfi->basename, basename);
+  setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
 }
 
 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
@@ -1989,8 +2117,9 @@ static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
 {
   lfi->type = type;
   lfi->packed = TRUE;
-  lfi->basename = getPackedLevelBasename(lfi->type);
-  lfi->filename = getLevelFilenameFromBasename(lfi->basename);
+
+  setString(&lfi->basename, getPackedLevelBasename(lfi->type));
+  setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
 }
 
 static int getFiletypeFromID(char *filetype_id)
@@ -2022,31 +2151,47 @@ static int getFiletypeFromID(char *filetype_id)
   return filetype;
 }
 
-static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+char *getLocalLevelTemplateFilename()
 {
-  int nr = lfi->nr;
+  return getDefaultLevelFilename(-1);
+}
 
-  /* special case: level number is negative => check for level template file */
-  if (nr < 0)
+char *getGlobalLevelTemplateFilename()
+{
+  /* global variable "leveldir_current" must be modified in the loop below */
+  LevelDirTree *leveldir_current_last = leveldir_current;
+  char *filename = NULL;
+
+  /* check for template level in path from current to topmost tree node */
+
+  while (leveldir_current != NULL)
   {
-    /* global variable "leveldir_current" must be modified in the loop below */
-    LevelDirTree *leveldir_current_last = leveldir_current;
+    filename = getDefaultLevelFilename(-1);
 
-    /* check for template level in path from current to topmost tree node */
+    if (fileExists(filename))
+      break;
 
-    while (leveldir_current != NULL)
-    {
-      setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
-                                          "template.%s", LEVELFILE_EXTENSION);
+    leveldir_current = leveldir_current->node_parent;
+  }
 
-      if (fileExists(lfi->filename))
-       break;
+  /* restore global variable "leveldir_current" modified in above loop */
+  leveldir_current = leveldir_current_last;
 
-      leveldir_current = leveldir_current->node_parent;
-    }
+  return filename;
+}
+
+static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+{
+  int nr = lfi->nr;
+
+  /* special case: level number is negative => check for level template file */
+  if (nr < 0)
+  {
+    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                        getSingleLevelBasename(-1));
 
-    /* restore global variable "leveldir_current" modified in above loop */
-    leveldir_current = leveldir_current_last;
+    /* replace local level template filename with global template filename */
+    setString(&lfi->filename, getGlobalLevelTemplateFilename());
 
     /* no fallback if template file not existing */
     return;
@@ -2065,6 +2210,16 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
     if (fileExists(lfi->filename))
       return;
   }
+  else if (leveldir_current->level_filetype != NULL)
+  {
+    int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
+    /* check for specified native level file with standard file name */
+    setLevelFileInfo_FormatLevelFilename(lfi, filetype,
+                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
+    if (fileExists(lfi->filename))
+      return;
+  }
 
   /* check for native Rocks'n'Diamonds level file */
   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
@@ -2117,6 +2272,9 @@ static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
 {
   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
     lfi->type = getFileTypeFromBasename(lfi->basename);
+
+  if (lfi->type == LEVEL_FILE_TYPE_RND)
+    lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
 }
 
 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
@@ -2491,6 +2649,8 @@ static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
     element_info[element].push_delay_random = 8;
   }
 
+  level->file_has_custom_elements = TRUE;
+
   return chunk_size;
 }
 
@@ -2517,6 +2677,8 @@ static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
       Error(ERR_WARN, "invalid custom element number %d", element);
   }
 
+  level->file_has_custom_elements = TRUE;
+
   return chunk_size;
 }
 
@@ -2608,6 +2770,8 @@ static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
     ei->modified_settings = TRUE;
   }
 
+  level->file_has_custom_elements = TRUE;
+
   return chunk_size;
 }
 
@@ -2756,6 +2920,8 @@ static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
   /* mark this custom element as modified */
   ei->modified_settings = TRUE;
 
+  level->file_has_custom_elements = TRUE;
+
   return chunk_size;
 }
 
@@ -2800,6 +2966,8 @@ static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
   /* mark this group element as modified */
   element_info[element].modified_settings = TRUE;
 
+  level->file_has_custom_elements = TRUE;
+
   return chunk_size;
 }
 
@@ -3092,6 +3260,8 @@ static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
       break;
   }
 
+  level->file_has_custom_elements = TRUE;
+
   return real_chunk_size;
 }
 
@@ -3117,6 +3287,8 @@ static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
   *ei = xx_ei;
   *group = xx_group;
 
+  level->file_has_custom_elements = TRUE;
+
   return real_chunk_size;
 }
 
@@ -3133,11 +3305,26 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
   if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
+    level->no_level_file = TRUE;
 
-    if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (level_info_only)
+      return;
 
-    return;
+    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+
+    if (!setup.editor.use_template_for_new_levels)
+      return;
+
+    /* if level file not found, try to initialize level data from template */
+    filename = getGlobalLevelTemplateFilename();
+
+    if (!(file = openFile(filename, MODE_READ)))
+      return;
+
+    /* default: for empty levels, use level template for custom elements */
+    level->use_custom_template = TRUE;
+
+    level->no_valid_file = FALSE;
   }
 
   getFileChunkBE(file, chunk_name, NULL);
@@ -3595,7 +3782,8 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
 {
   struct LevelInfo_SP *level_sp = level->native_sp_level;
   LevelInfoType *header = &level_sp->header;
-  int i, x, y;
+  boolean num_invalid_elements = 0;
+  int i, j, x, y;
 
   level->fieldx = level_sp->width;
   level->fieldy = level_sp->height;
@@ -3608,20 +3796,39 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
 
       if (element_new == EL_UNKNOWN)
-       Error(ERR_WARN, "invalid element %d at position %d, %d",
+      {
+       num_invalid_elements++;
+
+       Error(ERR_DEBUG, "invalid element %d at position %d, %d",
              element_old, x, y);
+      }
 
       level->field[x][y] = element_new;
     }
   }
 
+  if (num_invalid_elements > 0)
+    Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
+         (!options.debug ? " (use '--debug' for more details)" : ""));
+
   for (i = 0; i < MAX_PLAYERS; i++)
     level->initial_player_gravity[i] =
       (header->InitialGravity == 1 ? TRUE : FALSE);
 
+  /* skip leading spaces */
   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
-    level->name[i] = header->LevelTitle[i];
-  level->name[SP_LEVEL_NAME_LEN] = '\0';
+    if (header->LevelTitle[i] != ' ')
+      break;
+
+  /* copy level title */
+  for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
+    level->name[j] = header->LevelTitle[i];
+  level->name[j] = '\0';
+
+  /* cut trailing spaces */
+  for (; j > 0; j--)
+    if (level->name[j - 1] == ' ' && level->name[j] == '\0')
+      level->name[j - 1] = '\0';
 
   level->gems_needed = header->InfotronsNeeded;
 
@@ -3706,10 +3913,20 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
   level_sp->header.DemoRandomSeed = tape.random_seed;
 
   demo->length = 0;
+
   for (i = 0; i < tape.length; i++)
   {
     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
     int demo_repeat = tape.pos[i].delay;
+    int demo_entries = (demo_repeat + 15) / 16;
+
+    if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
+           SP_MAX_TAPE_LEN);
+
+      break;
+    }
 
     for (j = 0; j < demo_repeat / 16; j++)
       demo->data[demo->length++] = 0xf0 | demo_action;
@@ -3718,8 +3935,6 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
   }
 
-  demo->data[demo->length++] = 0xff;
-
   demo->is_available = TRUE;
 }
 
@@ -3739,22 +3954,117 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     return;
 
   tape.level_nr = demo->level_nr;      /* (currently not used) */
-  tape.length = demo->length - 1;      /* without "end of demo" byte */
   tape.random_seed = level_sp->header.DemoRandomSeed;
 
   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
 
-  for (i = 0; i < demo->length - 1; i++)
+  tape.counter = 0;
+  tape.pos[tape.counter].delay = 0;
+
+  for (i = 0; i < demo->length; i++)
   {
     int demo_action = demo->data[i] & 0x0f;
     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+    int tape_action = map_key_SP_to_RND(demo_action);
+    int tape_repeat = demo_repeat + 1;
+    byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+    boolean success = 0;
+    int j;
 
-    tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
-    tape.pos[i].delay = demo_repeat + 1;
+    for (j = 0; j < tape_repeat; j++)
+      success = TapeAddAction(action);
+
+    if (!success)
+    {
+      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+           MAX_TAPE_LEN);
+
+      break;
+    }
   }
 
-  tape.length_frames  = GetTapeLengthFrames();
-  tape.length_seconds = GetTapeLengthSeconds();
+  TapeHaltRecording();
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading MM level                                            */
+/* ------------------------------------------------------------------------- */
+
+void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
+{
+  struct LevelInfo_MM *level_mm = level->native_mm_level;
+  int x, y;
+
+  level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
+  level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
+
+  level_mm->time = level->time;
+  level_mm->kettles_needed = level->gems_needed;
+  level_mm->auto_count_kettles = level->auto_count_gems;
+
+  level_mm->laser_red = level->mm_laser_red;
+  level_mm->laser_green = level->mm_laser_green;
+  level_mm->laser_blue = level->mm_laser_blue;
+
+  strcpy(level_mm->name, level->name);
+  strcpy(level_mm->author, level->author);
+
+  level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
+  level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
+  level_mm->score[SC_KEY]        = level->score[SC_KEY];
+  level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
+  level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
+
+  level_mm->amoeba_speed = level->amoeba_speed;
+  level_mm->time_fuse    = level->mm_time_fuse;
+  level_mm->time_bomb    = level->mm_time_bomb;
+  level_mm->time_ball    = level->mm_time_ball;
+  level_mm->time_block   = level->mm_time_block;
+
+  for (x = 0; x < level->fieldx; x++)
+    for (y = 0; y < level->fieldy; y++)
+      Ur[x][y] =
+       level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
+}
+
+void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
+{
+  struct LevelInfo_MM *level_mm = level->native_mm_level;
+  int x, y;
+
+  level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
+  level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
+
+  level->time = level_mm->time;
+  level->gems_needed = level_mm->kettles_needed;
+  level->auto_count_gems = level_mm->auto_count_kettles;
+
+  level->mm_laser_red = level_mm->laser_red;
+  level->mm_laser_green = level_mm->laser_green;
+  level->mm_laser_blue = level_mm->laser_blue;
+
+  strcpy(level->name, level_mm->name);
+
+  /* only overwrite author from 'levelinfo.conf' if author defined in level */
+  if (!strEqual(level_mm->author, ANONYMOUS_NAME))
+    strcpy(level->author, level_mm->author);
+
+  level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
+  level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
+  level->score[SC_KEY]        = level_mm->score[SC_KEY];
+  level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
+  level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
+
+  level->amoeba_speed  = level_mm->amoeba_speed;
+  level->mm_time_fuse  = level_mm->time_fuse;
+  level->mm_time_bomb  = level_mm->time_bomb;
+  level->mm_time_ball  = level_mm->time_ball;
+  level->mm_time_block = level_mm->time_block;
+
+  for (x = 0; x < level->fieldx; x++)
+    for (y = 0; y < level->fieldy; y++)
+      level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
 }
 
 
@@ -5756,32 +6066,6 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
   {
     /* special global settings can now be set in level template */
 
-    /* fill smaller playfields with padding "beyond border wall" elements */
-    if (level->fieldx < SCR_FIELDX ||
-       level->fieldy < SCR_FIELDY)
-    {
-      short field[level->fieldx][level->fieldy];
-      int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
-      int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
-      int pos_fieldx = (new_fieldx - level->fieldx) / 2;
-      int pos_fieldy = (new_fieldy - level->fieldy) / 2;
-
-      /* copy old playfield (which is smaller than the visible area) */
-      for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
-       field[x][y] = level->field[x][y];
-
-      /* fill new, larger playfield with "beyond border wall" elements */
-      for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
-       level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
-
-      /* copy the old playfield to the middle of the new playfield */
-      for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
-       level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
-
-      level->fieldx = new_fieldx;
-      level->fieldy = new_fieldy;
-    }
-
     level->use_custom_template = TRUE;
   }
 }
@@ -5813,12 +6097,22 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
     level->no_valid_file = TRUE;
 }
 
+static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info,
+                                    boolean level_info_only)
+{
+  if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
+    level->no_valid_file = TRUE;
+}
+
 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
 {
   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
     CopyNativeLevel_RND_to_EM(level);
   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
     CopyNativeLevel_RND_to_SP(level);
+  else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+    CopyNativeLevel_RND_to_MM(level);
 }
 
 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
@@ -5827,6 +6121,8 @@ void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
     CopyNativeLevel_EM_to_RND(level);
   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
     CopyNativeLevel_SP_to_RND(level);
+  else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+    CopyNativeLevel_MM_to_RND(level);
 }
 
 void SaveNativeLevel(struct LevelInfo *level)
@@ -5853,7 +6149,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
                                  boolean level_info_only)
 {
   /* always start with reliable default values */
-  setLevelInfoToDefaults(level, level_info_only);
+  setLevelInfoToDefaults(level, level_info_only, TRUE);
 
   switch (level_file_info->type)
   {
@@ -5871,6 +6167,11 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
       level->game_engine_type = GAME_ENGINE_TYPE_SP;
       break;
 
+    case LEVEL_FILE_TYPE_MM:
+      LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
+      level->game_engine_type = GAME_ENGINE_TYPE_MM;
+      break;
+
     case LEVEL_FILE_TYPE_DC:
       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
       break;
@@ -5886,11 +6187,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
 
   /* if level file is invalid, restore level structure to default values */
   if (level->no_valid_file)
-  {
-    setLevelInfoToDefaults(level, level_info_only);
-
-    level->no_valid_file = TRUE;       /* but keep "no valid file" flag */
-  }
+    setLevelInfoToDefaults(level, level_info_only, FALSE);
 
   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
     level->game_engine_type = GAME_ENGINE_TYPE_RND;
@@ -5908,12 +6205,13 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
 
   level_file_info.nr = 0;                      /* unknown level number */
   level_file_info.type = LEVEL_FILE_TYPE_RND;  /* no others supported yet */
-  level_file_info.filename = filename;
+
+  setString(&level_file_info.filename, filename);
 
   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
 }
 
-static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
+static void LoadLevel_InitVersion(struct LevelInfo *level)
 {
   int i, j;
 
@@ -6086,9 +6384,25 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
     level->em_explodes_by_fire = TRUE;
 }
 
-static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
+static void LoadLevel_InitStandardElements(struct LevelInfo *level)
 {
-  int i, j, x, y;
+  int i, x, y;
+
+  /* map elements that have changed in newer versions */
+  level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
+                                                   level->game_version);
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (x = 0; x < 3; x++)
+      for (y = 0; y < 3; y++)
+       level->yamyam_content[i].e[x][y] =
+         getMappedElementByVersion(level->yamyam_content[i].e[x][y],
+                                   level->game_version);
+
+}
+
+static void LoadLevel_InitCustomElements(struct LevelInfo *level)
+{
+  int i, j;
 
   /* map custom element change events that have changed in newer versions
      (these following values were accidentally changed in version 3.0.1)
@@ -6202,23 +6516,34 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
-  /* map elements that have changed in newer versions */
-  level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
-                                                   level->game_version);
-  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
-    for (x = 0; x < 3; x++)
-      for (y = 0; y < 3; y++)
-       level->yamyam_content[i].e[x][y] =
-         getMappedElementByVersion(level->yamyam_content[i].e[x][y],
-                                   level->game_version);
+  /* set some other uninitialized values of custom elements in older levels */
+  if (level->game_version < VERSION_IDENT(3,1,0,0))
+  {
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      element_info[element].access_direction = MV_ALL_DIRECTIONS;
+
+      element_info[element].explosion_delay = 17;
+      element_info[element].ignition_delay = 8;
+    }
+  }
+}
+
+static void LoadLevel_InitElements(struct LevelInfo *level)
+{
+  LoadLevel_InitStandardElements(level);
+
+  if (level->file_has_custom_elements)
+    LoadLevel_InitCustomElements(level);
 
   /* initialize element properties for level editor etc. */
   InitElementPropertiesEngine(level->game_version);
-  InitElementPropertiesAfterLoading(level->game_version);
   InitElementPropertiesGfxElement();
 }
 
-static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
+static void LoadLevel_InitPlayfield(struct LevelInfo *level)
 {
   int x, y;
 
@@ -6250,7 +6575,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
     SetBorderElement();
 }
 
-static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
+static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
 {
   struct LevelFileInfo *level_file_info = &level->file_info;
 
@@ -6260,36 +6585,37 @@ static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
 
 void LoadLevelTemplate(int nr)
 {
-  char *filename;
+  if (!fileExists(getGlobalLevelTemplateFilename()))
+  {
+    Error(ERR_WARN, "no level template found for this level");
 
-  setLevelFileInfo(&level_template.file_info, nr);
-  filename = level_template.file_info.filename;
+    return;
+  }
+
+  setLevelFileInfo(&level_template.file_info, nr);
 
   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
 
-  LoadLevel_InitVersion(&level_template, filename);
-  LoadLevel_InitElements(&level_template, filename);
+  LoadLevel_InitVersion(&level_template);
+  LoadLevel_InitElements(&level_template);
 
   ActivateLevelTemplate();
 }
 
 void LoadLevel(int nr)
 {
-  char *filename;
-
   setLevelFileInfo(&level.file_info, nr);
-  filename = level.file_info.filename;
 
   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
 
   if (level.use_custom_template)
     LoadLevelTemplate(-1);
 
-  LoadLevel_InitVersion(&level, filename);
-  LoadLevel_InitElements(&level, filename);
-  LoadLevel_InitPlayfield(&level, filename);
+  LoadLevel_InitVersion(&level);
+  LoadLevel_InitElements(&level);
+  LoadLevel_InitPlayfield(&level);
 
-  LoadLevel_InitNativeEngines(&level, filename);
+  LoadLevel_InitNativeEngines(&level);
 }
 
 void LoadLevelInfoOnly(int nr)
@@ -7027,7 +7353,8 @@ static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
   return chunk_size;
 }
 
-static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
+static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
+                                 boolean save_as_template)
 {
   int chunk_size;
   int i;
@@ -7091,7 +7418,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   }
 
   /* if not using template level, check for non-default custom/group elements */
-  if (!level->use_custom_template)
+  if (!level->use_custom_template || save_as_template)
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
@@ -7127,14 +7454,14 @@ void SaveLevel(int nr)
 {
   char *filename = getDefaultLevelFilename(nr);
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, FALSE);
 }
 
 void SaveLevelTemplate()
 {
-  char *filename = getDefaultLevelFilename(-1);
+  char *filename = getLocalLevelTemplateFilename();
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, TRUE);
 }
 
 boolean SaveLevelChecked(int nr)
@@ -7158,7 +7485,7 @@ boolean SaveLevelChecked(int nr)
 
 void DumpLevel(struct LevelInfo *level)
 {
-  if (level->no_valid_file)
+  if (level->no_level_file || level->no_valid_file)
   {
     Error(ERR_WARN, "cannot dump -- no valid level file found");
 
@@ -7261,6 +7588,8 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
+    tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
 
     engine_version = getFileVersion(file);
@@ -7296,8 +7625,9 @@ static int LoadTape_INFO(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 =
-    (tape->num_participating_players + 1) * tape->length;
+  int tape_pos_size =
+    (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
+  int chunk_size_expected = tape_pos_size * tape->length;
 
   if (chunk_size_expected != chunk_size)
   {
@@ -7308,14 +7638,34 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   for (i = 0; i < tape->length; i++)
   {
     if (i >= MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+           MAX_TAPE_LEN);
+
+      // tape too large; read and ignore remaining tape data from this chunk
+      for (;i < tape->length; i++)
+       ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+
       break;
+    }
 
-    for (j = 0; j < MAX_PLAYERS; j++)
+    if (tape->use_mouse)
     {
-      tape->pos[i].action[j] = MV_NONE;
+      tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
 
-      if (tape->player_participates[j])
-       tape->pos[i].action[j] = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
+    }
+    else
+    {
+      for (j = 0; j < MAX_PLAYERS; j++)
+      {
+       tape->pos[i].action[j] = MV_NONE;
+
+       if (tape->player_participates[j])
+         tape->pos[i].action[j] = getFile8Bit(file);
+      }
     }
 
     tape->pos[i].delay = getFile8Bit(file);
@@ -7372,7 +7722,7 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   }
 
   if (i != tape->length)
-    chunk_size = (tape->num_participating_players + 1) * i;
+    chunk_size = tape_pos_size * i;
 
   return chunk_size;
 }
@@ -7641,6 +7991,8 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 
   putFile8Bit(file, store_participating_players);
 
+  putFile8Bit(file, (tape->use_mouse ? 1 : 0));
+
   /* unused bytes not at the end here for 4-byte alignment of engine_version */
   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
 
@@ -7666,9 +8018,18 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    for (j = 0; j < MAX_PLAYERS; j++)
-      if (tape->player_participates[j])
-       putFile8Bit(file, tape->pos[i].action[j]);
+    if (tape->use_mouse)
+    {
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
+    }
+    else
+    {
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (tape->player_participates[j])
+         putFile8Bit(file, tape->pos[i].action[j]);
+    }
 
     putFile8Bit(file, tape->pos[i].delay);
   }
@@ -7679,6 +8040,7 @@ void SaveTape(int nr)
   char *filename = getTapeFilename(nr);
   FILE *file;
   int num_participating_players = 0;
+  int tape_pos_size;
   int info_chunk_size;
   int body_chunk_size;
   int i;
@@ -7699,8 +8061,10 @@ void SaveTape(int nr)
     if (tape.player_participates[i])
       num_participating_players++;
 
+  tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
+
   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
-  body_chunk_size = (num_participating_players + 1) * tape.length;
+  body_chunk_size = tape_pos_size * tape.length;
 
   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
@@ -7724,18 +8088,18 @@ void SaveTape(int nr)
   tape.changed = FALSE;
 }
 
-boolean SaveTapeChecked(int nr)
+static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
 {
   char *filename = getTapeFilename(nr);
   boolean new_tape = !fileExists(filename);
   boolean tape_saved = FALSE;
 
-  if (new_tape || Request("Replace old tape?", REQ_ASK))
+  if (new_tape || Request(msg_replace, REQ_ASK))
   {
     SaveTape(nr);
 
     if (new_tape)
-      Request("Tape saved!", REQ_CONFIRM);
+      Request(msg_saved, REQ_CONFIRM);
 
     tape_saved = TRUE;
   }
@@ -7743,6 +8107,17 @@ boolean SaveTapeChecked(int nr)
   return tape_saved;
 }
 
+boolean SaveTapeChecked(int nr)
+{
+  return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
+}
+
+boolean SaveTapeChecked_LevelSolved(int nr)
+{
+  return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
+                               "Level solved! Tape saved!");
+}
+
 void DumpTape(struct TapeInfo *tape)
 {
   int tape_frame_counter;
@@ -7862,6 +8237,7 @@ void LoadScore(int nr)
 void SaveScore(int nr)
 {
   int i;
+  int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
   char *filename = getScoreFilename(nr);
   FILE *file;
 
@@ -7880,7 +8256,7 @@ void SaveScore(int nr)
 
   fclose(file);
 
-  SetFilePermissions(filename, PERMS_PUBLIC);
+  SetFilePermissions(filename, permissions);
 }
 
 
@@ -7891,175 +8267,245 @@ void SaveScore(int nr)
 #define TOKEN_STR_PLAYER_PREFIX                        "player_"
 
 /* global setup */
-#define SETUP_TOKEN_PLAYER_NAME                        0
-#define SETUP_TOKEN_SOUND                      1
-#define SETUP_TOKEN_SOUND_LOOPS                        2
-#define SETUP_TOKEN_SOUND_MUSIC                        3
-#define SETUP_TOKEN_SOUND_SIMPLE               4
-#define SETUP_TOKEN_TOONS                      5
-#define SETUP_TOKEN_SCROLL_DELAY               6
-#define SETUP_TOKEN_SCROLL_DELAY_VALUE         7
-#define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE       8
-#define SETUP_TOKEN_FADE_SCREENS               9
-#define SETUP_TOKEN_AUTORECORD                 10
-#define SETUP_TOKEN_SHOW_TITLESCREEN           11
-#define SETUP_TOKEN_QUICK_DOORS                        12
-#define SETUP_TOKEN_TEAM_MODE                  13
-#define SETUP_TOKEN_HANDICAP                   14
-#define SETUP_TOKEN_SKIP_LEVELS                        15
-#define SETUP_TOKEN_TIME_LIMIT                 16
-#define SETUP_TOKEN_FULLSCREEN                 17
-#define SETUP_TOKEN_FULLSCREEN_MODE            18
-#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
+enum
+{
+  SETUP_TOKEN_PLAYER_NAME = 0,
+  SETUP_TOKEN_SOUND,
+  SETUP_TOKEN_SOUND_LOOPS,
+  SETUP_TOKEN_SOUND_MUSIC,
+  SETUP_TOKEN_SOUND_SIMPLE,
+  SETUP_TOKEN_TOONS,
+  SETUP_TOKEN_SCROLL_DELAY,
+  SETUP_TOKEN_SCROLL_DELAY_VALUE,
+  SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
+  SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
+  SETUP_TOKEN_FADE_SCREENS,
+  SETUP_TOKEN_AUTORECORD,
+  SETUP_TOKEN_SHOW_TITLESCREEN,
+  SETUP_TOKEN_QUICK_DOORS,
+  SETUP_TOKEN_TEAM_MODE,
+  SETUP_TOKEN_HANDICAP,
+  SETUP_TOKEN_SKIP_LEVELS,
+  SETUP_TOKEN_INCREMENT_LEVELS,
+  SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
+  SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
+  SETUP_TOKEN_TIME_LIMIT,
+  SETUP_TOKEN_FULLSCREEN,
+  SETUP_TOKEN_WINDOW_SCALING_PERCENT,
+  SETUP_TOKEN_WINDOW_SCALING_QUALITY,
+  SETUP_TOKEN_SCREEN_RENDERING_MODE,
+  SETUP_TOKEN_ASK_ON_ESCAPE,
+  SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
+  SETUP_TOKEN_QUICK_SWITCH,
+  SETUP_TOKEN_INPUT_ON_FOCUS,
+  SETUP_TOKEN_PREFER_AGA_GRAPHICS,
+  SETUP_TOKEN_GAME_FRAME_DELAY,
+  SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
+  SETUP_TOKEN_SMALL_GAME_GRAPHICS,
+  SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
+  SETUP_TOKEN_GRAPHICS_SET,
+  SETUP_TOKEN_SOUNDS_SET,
+  SETUP_TOKEN_MUSIC_SET,
+  SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
+  SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
+  SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
+  SETUP_TOKEN_VOLUME_SIMPLE,
+  SETUP_TOKEN_VOLUME_LOOPS,
+  SETUP_TOKEN_VOLUME_MUSIC,
+  SETUP_TOKEN_NETWORK_MODE,
+  SETUP_TOKEN_NETWORK_PLAYER_NR,
+  SETUP_TOKEN_TOUCH_CONTROL_TYPE,
+  SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
+  SETUP_TOKEN_TOUCH_DROP_DISTANCE,
+  SETUP_TOKEN_TOUCH_TRANSPARENCY,
+  SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
+  SETUP_TOKEN_TOUCH_DRAW_PRESSED,
+  SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
+  SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
+  SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
+  SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
+
+  NUM_GLOBAL_SETUP_TOKENS
+};
+
+/* auto setup */
+enum
+{
+  SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
+
+  NUM_AUTO_SETUP_TOKENS
+};
 
 /* editor setup */
-#define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
-#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE     1
-#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB        2
-#define SETUP_TOKEN_EDITOR_EL_MORE             3
-#define SETUP_TOKEN_EDITOR_EL_SOKOBAN          4
-#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX         5
-#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES    6
-#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   7
-#define SETUP_TOKEN_EDITOR_EL_CHARS            8
-#define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS      9
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM           10
-#define SETUP_TOKEN_EDITOR_EL_HEADLINES                11
-#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     12
-#define SETUP_TOKEN_EDITOR_EL_DYNAMIC          13
-#define SETUP_TOKEN_EDITOR_EL_BY_GAME          14
-#define SETUP_TOKEN_EDITOR_EL_BY_TYPE          15
-#define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN  16
-
-#define NUM_EDITOR_SETUP_TOKENS                        17
+enum
+{
+  SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
+  SETUP_TOKEN_EDITOR_EL_CUSTOM,
+  SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
+  SETUP_TOKEN_EDITOR_EL_DYNAMIC,
+  SETUP_TOKEN_EDITOR_EL_HEADLINES,
+  SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
+
+  NUM_EDITOR_SETUP_TOKENS
+};
 
 /* editor cascade setup */
-#define SETUP_TOKEN_EDITOR_CASCADE_BD          0
-#define SETUP_TOKEN_EDITOR_CASCADE_EM          1
-#define SETUP_TOKEN_EDITOR_CASCADE_EMC         2
-#define SETUP_TOKEN_EDITOR_CASCADE_RND         3
-#define SETUP_TOKEN_EDITOR_CASCADE_SB          4
-#define SETUP_TOKEN_EDITOR_CASCADE_SP          5
-#define SETUP_TOKEN_EDITOR_CASCADE_DC          6
-#define SETUP_TOKEN_EDITOR_CASCADE_DX          7
-#define SETUP_TOKEN_EDITOR_CASCADE_TEXT                8
-#define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT   9
-#define SETUP_TOKEN_EDITOR_CASCADE_CE          10
-#define SETUP_TOKEN_EDITOR_CASCADE_GE          11
-#define SETUP_TOKEN_EDITOR_CASCADE_REF         12
-#define SETUP_TOKEN_EDITOR_CASCADE_USER                13
-#define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC     14
-
-#define NUM_EDITOR_CASCADE_SETUP_TOKENS                15
+enum
+{
+  SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
+  SETUP_TOKEN_EDITOR_CASCADE_EM,
+  SETUP_TOKEN_EDITOR_CASCADE_EMC,
+  SETUP_TOKEN_EDITOR_CASCADE_RND,
+  SETUP_TOKEN_EDITOR_CASCADE_SB,
+  SETUP_TOKEN_EDITOR_CASCADE_SP,
+  SETUP_TOKEN_EDITOR_CASCADE_DC,
+  SETUP_TOKEN_EDITOR_CASCADE_DX,
+  SETUP_TOKEN_EDITOR_CASCADE_TEXT,
+  SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
+  SETUP_TOKEN_EDITOR_CASCADE_CE,
+  SETUP_TOKEN_EDITOR_CASCADE_GE,
+  SETUP_TOKEN_EDITOR_CASCADE_REF,
+  SETUP_TOKEN_EDITOR_CASCADE_USER,
+  SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
+
+  NUM_EDITOR_CASCADE_SETUP_TOKENS
+};
 
 /* shortcut setup */
-#define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
-#define SETUP_TOKEN_SHORTCUT_LOAD_GAME         1
-#define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE      2
-#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1    3
-#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2    4
-#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3    5
-#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_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
+enum
+{
+  SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
+  SETUP_TOKEN_SHORTCUT_LOAD_GAME,
+  SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
+  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
+  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
+  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
+  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
+  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
+  SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
+  SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
+  SETUP_TOKEN_SHORTCUT_TAPE_STOP,
+  SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
+  SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
+  SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
+  SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
+  SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
+  SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
+  SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
+  SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
+  SETUP_TOKEN_SHORTCUT_SNAP_UP,
+  SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
+
+  NUM_SHORTCUT_SETUP_TOKENS
+};
 
 /* player setup */
-#define SETUP_TOKEN_PLAYER_USE_JOYSTICK                0
-#define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME     1
-#define SETUP_TOKEN_PLAYER_JOY_XLEFT           2
-#define SETUP_TOKEN_PLAYER_JOY_XMIDDLE         3
-#define SETUP_TOKEN_PLAYER_JOY_XRIGHT          4
-#define SETUP_TOKEN_PLAYER_JOY_YUPPER          5
-#define SETUP_TOKEN_PLAYER_JOY_YMIDDLE         6
-#define SETUP_TOKEN_PLAYER_JOY_YLOWER          7
-#define SETUP_TOKEN_PLAYER_JOY_SNAP            8
-#define SETUP_TOKEN_PLAYER_JOY_DROP            9
-#define SETUP_TOKEN_PLAYER_KEY_LEFT            10
-#define SETUP_TOKEN_PLAYER_KEY_RIGHT           11
-#define SETUP_TOKEN_PLAYER_KEY_UP              12
-#define SETUP_TOKEN_PLAYER_KEY_DOWN            13
-#define SETUP_TOKEN_PLAYER_KEY_SNAP            14
-#define SETUP_TOKEN_PLAYER_KEY_DROP            15
-
-#define NUM_PLAYER_SETUP_TOKENS                        16
+enum
+{
+  SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
+  SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
+  SETUP_TOKEN_PLAYER_JOY_XLEFT,
+  SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
+  SETUP_TOKEN_PLAYER_JOY_XRIGHT,
+  SETUP_TOKEN_PLAYER_JOY_YUPPER,
+  SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
+  SETUP_TOKEN_PLAYER_JOY_YLOWER,
+  SETUP_TOKEN_PLAYER_JOY_SNAP,
+  SETUP_TOKEN_PLAYER_JOY_DROP,
+  SETUP_TOKEN_PLAYER_KEY_LEFT,
+  SETUP_TOKEN_PLAYER_KEY_RIGHT,
+  SETUP_TOKEN_PLAYER_KEY_UP,
+  SETUP_TOKEN_PLAYER_KEY_DOWN,
+  SETUP_TOKEN_PLAYER_KEY_SNAP,
+  SETUP_TOKEN_PLAYER_KEY_DROP,
+
+  NUM_PLAYER_SETUP_TOKENS
+};
 
 /* system setup */
-#define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER     0
-#define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER     1
-#define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
+enum
+{
+  SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
+  SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
+  SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
 
-#define NUM_SYSTEM_SETUP_TOKENS                        3
+  NUM_SYSTEM_SETUP_TOKENS
+};
 
 /* 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
+enum
+{
+  SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
+  SETUP_TOKEN_INT_PROGRAM_VERSION,
+  SETUP_TOKEN_INT_PROGRAM_AUTHOR,
+  SETUP_TOKEN_INT_PROGRAM_EMAIL,
+  SETUP_TOKEN_INT_PROGRAM_WEBSITE,
+  SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
+  SETUP_TOKEN_INT_PROGRAM_COMPANY,
+  SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
+  SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
+  SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
+  SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
+  SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
+  SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
+  SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
+  SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
+  SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
+  SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
+  SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
+  SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
+
+  NUM_INTERNAL_SETUP_TOKENS
+};
+
+/* debug setup */
+enum
+{
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
+  SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
+  SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
+
+  NUM_DEBUG_SETUP_TOKENS
+};
 
 /* options setup */
-#define SETUP_TOKEN_OPTIONS_VERBOSE            0
+enum
+{
+  SETUP_TOKEN_OPTIONS_VERBOSE = 0,
 
-#define NUM_OPTIONS_SETUP_TOKENS               1
+  NUM_OPTIONS_SETUP_TOKENS
+};
 
 
 static struct SetupInfo si;
+static struct SetupAutoSetupInfo sasi;
 static struct SetupEditorInfo sei;
 static struct SetupEditorCascadeInfo seci;
 static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
 static struct SetupSystemInfo syi;
 static struct SetupInternalInfo sxi;
+static struct SetupDebugInfo sdi;
 static struct OptionInfo soi;
 
 static struct TokenInfo global_setup_tokens[] =
@@ -8073,6 +8519,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"           },
   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"     },
   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"   },
+  { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory" },
   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"           },
   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"       },
@@ -8080,11 +8527,14 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.team_mode,               "team_mode"              },
   { TYPE_SWITCH, &si.handicap,                "handicap"               },
   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"            },
+  { TYPE_SWITCH, &si.increment_levels,        "increment_levels"       },
+  { TYPE_SWITCH, &si.auto_play_next_level,    "auto_play_next_level"   },
+  { TYPE_SWITCH, &si.skip_scores_after_game,  "skip_scores_after_game" },
   { 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_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"  },
   { 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"    },
@@ -8103,29 +8553,32 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"          },
   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"           },
   { TYPE_INTEGER,&si.volume_music,            "volume_music"           },
+  { TYPE_SWITCH, &si.network_mode,            "network_mode"           },
+  { TYPE_PLAYER, &si.network_player_nr,       "network_player"         },
   { 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"    },
+  { TYPE_INTEGER,&si.touch.transparency,      "touch.transparency"     },
+  { TYPE_INTEGER,&si.touch.draw_outlined,     "touch.draw_outlined"    },
+  { TYPE_INTEGER,&si.touch.draw_pressed,      "touch.draw_pressed"     },
+  { TYPE_INTEGER,&si.touch.grid_xsize[0],     "touch.virtual_buttons.0.xsize" },
+  { TYPE_INTEGER,&si.touch.grid_ysize[0],     "touch.virtual_buttons.0.ysize" },
+  { TYPE_INTEGER,&si.touch.grid_xsize[1],     "touch.virtual_buttons.1.xsize" },
+  { TYPE_INTEGER,&si.touch.grid_ysize[1],     "touch.virtual_buttons.1.ysize" },
+};
+
+static struct TokenInfo auto_setup_tokens[] =
+{
+  { TYPE_INTEGER,&sasi.editor_zoom_tilesize,   "editor.zoom_tilesize"  },
 };
 
 static struct TokenInfo editor_setup_tokens[] =
 {
-  { 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"},
-  { TYPE_SWITCH, &sei.el_more,         "editor.el_more"                },
-  { TYPE_SWITCH, &sei.el_sokoban,      "editor.el_sokoban"             },
-  { 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"     },
-  { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
-  { TYPE_SWITCH, &sei.el_steel_chars,  "editor.el_steel_chars"         },
+  { TYPE_SWITCH, &sei.el_classic,      "editor.el_classic"             },
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
-  { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
   { 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"             },
-  { TYPE_SWITCH, &sei.el_by_type,      "editor.el_by_type"             },
+  { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"   },
 };
 
@@ -8139,6 +8592,8 @@ static struct TokenInfo editor_cascade_setup_tokens[] =
   { TYPE_SWITCH, &seci.el_sp,          "editor.cascade.el_sp"          },
   { TYPE_SWITCH, &seci.el_dc,          "editor.cascade.el_dc"          },
   { TYPE_SWITCH, &seci.el_dx,          "editor.cascade.el_dx"          },
+  { TYPE_SWITCH, &seci.el_mm,          "editor.cascade.el_mm"          },
+  { TYPE_SWITCH, &seci.el_df,          "editor.cascade.el_df"          },
   { TYPE_SWITCH, &seci.el_chars,       "editor.cascade.el_chars"       },
   { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
   { TYPE_SWITCH, &seci.el_ce,          "editor.cascade.el_ce"          },
@@ -8203,6 +8658,7 @@ static struct TokenInfo system_setup_tokens[] =
 static struct TokenInfo internal_setup_tokens[] =
 {
   { TYPE_STRING, &sxi.program_title,           "program_title"         },
+  { TYPE_STRING, &sxi.program_version,         "program_version"       },
   { TYPE_STRING, &sxi.program_author,          "program_author"        },
   { TYPE_STRING, &sxi.program_email,           "program_email"         },
   { TYPE_STRING, &sxi.program_website,         "program_website"       },
@@ -8217,6 +8673,36 @@ static struct TokenInfo internal_setup_tokens[] =
   { TYPE_STRING, &sxi.fallback_music_file,     "fallback_music_file"   },
   { TYPE_STRING, &sxi.default_level_series,    "default_level_series"  },
   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir,        "choose_from_top_leveldir" },
+  { TYPE_BOOLEAN,&sxi.show_scaling_in_title,   "show_scaling_in_title" },
+  { TYPE_INTEGER,&sxi.default_window_width,    "default_window_width"  },
+  { TYPE_INTEGER,&sxi.default_window_height,   "default_window_height" },
+};
+
+static struct TokenInfo debug_setup_tokens[] =
+{
+  { TYPE_INTEGER, &sdi.frame_delay[0],         "debug.frame_delay_0"   },
+  { TYPE_INTEGER, &sdi.frame_delay[1],         "debug.frame_delay_1"   },
+  { TYPE_INTEGER, &sdi.frame_delay[2],         "debug.frame_delay_2"   },
+  { TYPE_INTEGER, &sdi.frame_delay[3],         "debug.frame_delay_3"   },
+  { TYPE_INTEGER, &sdi.frame_delay[4],         "debug.frame_delay_4"   },
+  { TYPE_INTEGER, &sdi.frame_delay[5],         "debug.frame_delay_5"   },
+  { TYPE_INTEGER, &sdi.frame_delay[6],         "debug.frame_delay_6"   },
+  { TYPE_INTEGER, &sdi.frame_delay[7],         "debug.frame_delay_7"   },
+  { TYPE_INTEGER, &sdi.frame_delay[8],         "debug.frame_delay_8"   },
+  { TYPE_INTEGER, &sdi.frame_delay[9],         "debug.frame_delay_9"   },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[0],     "debug.key.frame_delay_0" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[1],     "debug.key.frame_delay_1" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[2],     "debug.key.frame_delay_2" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[3],     "debug.key.frame_delay_3" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[4],     "debug.key.frame_delay_4" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[5],     "debug.key.frame_delay_5" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[6],     "debug.key.frame_delay_6" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[7],     "debug.key.frame_delay_7" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[8],     "debug.key.frame_delay_8" },
+  { TYPE_KEY_X11, &sdi.frame_delay_key[9],     "debug.key.frame_delay_9" },
+  { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
+  { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
+  { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
 };
 
 static struct TokenInfo options_setup_tokens[] =
@@ -8253,6 +8739,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->scroll_delay = TRUE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
+  si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
   si->fade_screens = TRUE;
   si->autorecord = TRUE;
   si->show_titlescreen = TRUE;
@@ -8260,11 +8747,14 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->team_mode = FALSE;
   si->handicap = TRUE;
   si->skip_levels = TRUE;
+  si->increment_levels = TRUE;
+  si->auto_play_next_level = TRUE;
+  si->skip_scores_after_game = FALSE;
   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->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
   si->ask_on_escape = TRUE;
   si->ask_on_escape_editor = TRUE;
   si->quick_switch = FALSE;
@@ -8287,9 +8777,64 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->volume_loops = 100;              /* percent */
   si->volume_music = 100;              /* percent */
 
+  si->network_mode = FALSE;
+  si->network_player_nr = 0;           /* first player */
+
   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->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;         /* percent */
+  si->touch.draw_outlined = TRUE;
+  si->touch.draw_pressed = TRUE;
+
+  for (i = 0; i < 2; i++)
+  {
+    char *default_grid_button[6][2] =
+    {
+      { "      ", "  ^^  " },
+      { "      ", "  ^^  " },
+      { "      ", "<<  >>" },
+      { "      ", "<<  >>" },
+      { "111222", "  vv  " },
+      { "111222", "  vv  " }
+    };
+    int grid_xsize = DEFAULT_GRID_XSIZE(i);
+    int grid_ysize = DEFAULT_GRID_YSIZE(i);
+    int min_xsize = MIN(6, grid_xsize);
+    int min_ysize = MIN(6, grid_ysize);
+    int startx = grid_xsize - min_xsize;
+    int starty = grid_ysize - min_ysize;
+    int x, y;
+
+    // virtual buttons grid can only be set to defaults if video is initialized
+    // (this will be repeated if virtual buttons are not loaded from setup file)
+    if (video.initialized)
+    {
+      si->touch.grid_xsize[i] = grid_xsize;
+      si->touch.grid_ysize[i] = grid_ysize;
+    }
+    else
+    {
+      si->touch.grid_xsize[i] = -1;
+      si->touch.grid_ysize[i] = -1;
+    }
+
+    for (x = 0; x < MAX_GRID_XSIZE; x++)
+      for (y = 0; y < MAX_GRID_YSIZE; y++)
+       si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
+
+    for (x = 0; x < min_xsize; x++)
+      for (y = 0; y < min_ysize; y++)
+       si->touch.grid_button[i][x][starty + y] =
+         default_grid_button[y][0][x];
+
+    for (x = 0; x < min_xsize; x++)
+      for (y = 0; y < min_ysize; y++)
+       si->touch.grid_button[i][startx + x][starty + y] =
+         default_grid_button[y][1][x];
+  }
+
+  si->touch.grid_initialized           = video.initialized;
 
   si->editor.el_boulderdash            = TRUE;
   si->editor.el_emerald_mine           = TRUE;
@@ -8299,15 +8844,24 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_supaplex               = TRUE;
   si->editor.el_diamond_caves          = TRUE;
   si->editor.el_dx_boulderdash         = TRUE;
+
+  si->editor.el_mirror_magic           = TRUE;
+  si->editor.el_deflektor              = TRUE;
+
   si->editor.el_chars                  = TRUE;
   si->editor.el_steel_chars            = TRUE;
+
+  si->editor.el_classic                        = TRUE;
   si->editor.el_custom                 = TRUE;
 
-  si->editor.el_headlines = TRUE;
-  si->editor.el_user_defined = FALSE;
-  si->editor.el_dynamic = TRUE;
+  si->editor.el_user_defined           = FALSE;
+  si->editor.el_dynamic                        = TRUE;
 
-  si->editor.show_element_token = FALSE;
+  si->editor.el_headlines              = TRUE;
+
+  si->editor.show_element_token                = FALSE;
+
+  si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game       = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game       = DEFAULT_KEY_LOAD_GAME;
@@ -8360,6 +8914,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
 
   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
+  si->internal.program_version   = getStringCopy(getProgramRealVersionString());
   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
@@ -8378,6 +8933,37 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
   si->internal.choose_from_top_leveldir = FALSE;
+  si->internal.show_scaling_in_title = TRUE;
+
+  si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
+  si->internal.default_window_height = WIN_YSIZE_DEFAULT;
+
+  si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
+  si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
+  si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
+  si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
+  si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
+  si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
+  si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
+  si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
+  si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
+  si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
+
+  si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
+  si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
+  si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
+  si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
+  si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
+  si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
+  si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
+  si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
+  si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
+  si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
+
+  si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
+  si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
+
+  si->debug.show_frames_per_second = FALSE;
 
   si->options.verbose = FALSE;
 
@@ -8386,6 +8972,11 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 #endif
 }
 
+static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
+{
+  si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
+}
+
 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
 {
   si->editor_cascade.el_bd             = TRUE;
@@ -8397,6 +8988,9 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
   si->editor_cascade.el_dc             = TRUE;
   si->editor_cascade.el_dx             = TRUE;
 
+  si->editor_cascade.el_mm             = TRUE;
+  si->editor_cascade.el_df             = TRUE;
+
   si->editor_cascade.el_chars          = FALSE;
   si->editor_cascade.el_steel_chars    = FALSE;
   si->editor_cascade.el_ce             = FALSE;
@@ -8406,6 +9000,65 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
   si->editor_cascade.el_dynamic                = FALSE;
 }
 
+#define MAX_HIDE_SETUP_TOKEN_SIZE              20
+
+static char *getHideSetupToken(void *setup_value)
+{
+  static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
+
+  if (setup_value != NULL)
+    snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
+
+  return hide_setup_token;
+}
+
+void setHideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  if (setup_value != NULL)
+    setHashEntry(hide_setup_hash, hide_setup_token, "");
+}
+
+static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
+{
+  /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
+  void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
+
+  setHideSetupEntry(setup_value);
+}
+
+boolean hideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  return (setup_value != NULL &&
+         getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
+}
+
+static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
+                                     struct TokenInfo *token_info,
+                                     int token_nr, char *token_text)
+{
+  char *token_hide_text = getStringCat2(token_text, ".hide");
+  char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
+
+  /* set the value of this setup option in the setup option structure */
+  setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
+
+  /* check if this setup option should be hidden in the setup menu */
+  if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
+    setHideSetupEntryRaw(token_text, token_info[token_nr].value);
+}
+
+static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
+                                     struct TokenInfo *token_info,
+                                     int token_nr)
+{
+  setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
+                           token_info[token_nr].text);
+}
+
 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 {
   int i, pnr;
@@ -8413,25 +9066,64 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   if (!setup_file_hash)
     return;
 
+  if (hide_setup_hash == NULL)
+    hide_setup_hash = newSetupFileHash();
+
   /* global setup */
   si = setup;
   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
-    setSetupInfo(global_setup_tokens, i,
-                getHashEntry(setup_file_hash, global_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
   setup = si;
 
+  /* virtual buttons setup */
+  setup.touch.grid_initialized = TRUE;
+  for (i = 0; i < 2; i++)
+  {
+    int grid_xsize = setup.touch.grid_xsize[i];
+    int grid_ysize = setup.touch.grid_ysize[i];
+    int x, y;
+
+    // if virtual buttons are not loaded from setup file, repeat initializing
+    // virtual buttons grid with default values later when video is initialized
+    if (grid_xsize == -1 ||
+       grid_ysize == -1)
+    {
+      setup.touch.grid_initialized = FALSE;
+
+      continue;
+    }
+
+    for (y = 0; y < grid_ysize; y++)
+    {
+      char token_string[MAX_LINE_LEN];
+
+      sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
+
+      char *value_string = getHashEntry(setup_file_hash, token_string);
+
+      if (value_string == NULL)
+       continue;
+
+      for (x = 0; x < grid_xsize; x++)
+      {
+       char c = value_string[x];
+
+       setup.touch.grid_button[i][x][y] =
+         (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
+      }
+    }
+  }
+
   /* editor setup */
   sei = setup.editor;
   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
-    setSetupInfo(editor_setup_tokens, i,
-                getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
   setup.editor = sei;
 
   /* shortcut setup */
   ssi = setup.shortcut;
   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
-    setSetupInfo(shortcut_setup_tokens, i,
-                getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
   setup.shortcut = ssi;
 
   /* player setup */
@@ -8447,8 +9139,8 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
       char full_token[100];
 
       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
-      setSetupInfo(player_setup_tokens, i,
-                  getHashEntry(setup_file_hash, full_token));
+      setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
+                               full_token);
     }
     setup.input[pnr] = sii;
   }
@@ -8456,23 +9148,44 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   /* system setup */
   syi = setup.system;
   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
-    setSetupInfo(system_setup_tokens, i,
-                getHashEntry(setup_file_hash, system_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
   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));
+    setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
   setup.internal = sxi;
 
+  /* debug setup */
+  sdi = setup.debug;
+  for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
+    setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
+  setup.debug = sdi;
+
   /* options setup */
   soi = setup.options;
   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
-    setSetupInfo(options_setup_tokens, i,
-                getHashEntry(setup_file_hash, options_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
   setup.options = soi;
+
+  setHideRelatedSetupEntries();
+}
+
+static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
+{
+  int i;
+
+  if (!setup_file_hash)
+    return;
+
+  /* auto setup */
+  sasi = setup.auto_setup;
+  for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
+    setSetupInfo(auto_setup_tokens, i,
+                getHashEntry(setup_file_hash,
+                             auto_setup_tokens[i].text));
+  setup.auto_setup = sasi;
 }
 
 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
@@ -8503,7 +9216,7 @@ void LoadSetupFromFilename(char *filename)
   }
   else
   {
-    Error(ERR_WARN, "using default setup values");
+    Error(ERR_DEBUG, "using default setup values");
   }
 }
 
@@ -8549,6 +9262,26 @@ void LoadSetup()
   LoadSetup_SpecialPostProcessing();
 }
 
+void LoadSetup_AutoSetup()
+{
+  char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
+  SetupFileHash *setup_file_hash = NULL;
+
+  /* always start with reliable default values */
+  setSetupInfoToDefaults_AutoSetup(&setup);
+
+  setup_file_hash = loadSetupFileHash(filename);
+
+  if (setup_file_hash)
+  {
+    decodeSetupFileHash_AutoSetup(setup_file_hash);
+
+    freeSetupFileHash(setup_file_hash);
+  }
+
+  free(filename);
+}
+
 void LoadSetup_EditorCascade()
 {
   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
@@ -8569,6 +9302,55 @@ void LoadSetup_EditorCascade()
   free(filename);
 }
 
+static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
+                                          char *mapping_line)
+{
+  char mapping_guid[MAX_LINE_LEN];
+  char *mapping_start, *mapping_end;
+
+  // get GUID from game controller mapping line: copy complete line
+  strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
+  mapping_guid[MAX_LINE_LEN - 1] = '\0';
+
+  // get GUID from game controller mapping line: cut after GUID part
+  mapping_start = strchr(mapping_guid, ',');
+  if (mapping_start != NULL)
+    *mapping_start = '\0';
+
+  // cut newline from game controller mapping line
+  mapping_end = strchr(mapping_line, '\n');
+  if (mapping_end != NULL)
+    *mapping_end = '\0';
+
+  // add mapping entry to game controller mappings hash
+  setHashEntry(mappings_hash, mapping_guid, mapping_line);
+}
+
+static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
+                                                char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+
+    return;
+  }
+
+  while (!feof(file))
+  {
+    char line[MAX_LINE_LEN];
+
+    if (!fgets(line, MAX_LINE_LEN, file))
+      break;
+
+    addGameControllerMappingToHash(mappings_hash, line);
+  }
+
+  fclose(file);
+}
+
 void SaveSetup()
 {
   char *filename = getSetupFilename();
@@ -8593,12 +9375,44 @@ void SaveSetup()
     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
        i == SETUP_TOKEN_GRAPHICS_SET ||
        i == SETUP_TOKEN_VOLUME_SIMPLE ||
-       i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
+       i == SETUP_TOKEN_NETWORK_MODE ||
+       i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
+       i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
+       i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
       fprintf(file, "\n");
 
     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
   }
 
+  /* virtual buttons setup */
+  for (i = 0; i < 2; i++)
+  {
+    int grid_xsize = setup.touch.grid_xsize[i];
+    int grid_ysize = setup.touch.grid_ysize[i];
+    int x, y;
+
+    fprintf(file, "\n");
+
+    for (y = 0; y < grid_ysize; y++)
+    {
+      char token_string[MAX_LINE_LEN];
+      char value_string[MAX_LINE_LEN];
+
+      sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
+
+      for (x = 0; x < grid_xsize; x++)
+      {
+       char c = setup.touch.grid_button[i][x][y];
+
+       value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
+      }
+
+      value_string[grid_xsize] = '\0';
+
+      fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
+    }
+  }
+
   /* editor setup */
   sei = setup.editor;
   fprintf(file, "\n");
@@ -8633,6 +9447,12 @@ void SaveSetup()
   /* internal setup */
   /* (internal setup values not saved to user setup file) */
 
+  /* debug setup */
+  sdi = setup.debug;
+  fprintf(file, "\n");
+  for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
+    fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
+
   /* options setup */
   soi = setup.options;
   fprintf(file, "\n");
@@ -8644,6 +9464,34 @@ void SaveSetup()
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
 
+void SaveSetup_AutoSetup()
+{
+  char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
+  FILE *file;
+  int i;
+
+  InitUserDataDirectory();
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintFileHeader(file, AUTOSETUP_FILENAME);
+
+  sasi = setup.auto_setup;
+  for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
+    fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
+
+  fclose(file);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+
+  free(filename);
+}
+
 void SaveSetup_EditorCascade()
 {
   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
@@ -8672,6 +9520,47 @@ void SaveSetup_EditorCascade()
   free(filename);
 }
 
+static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
+                                                 char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+
+    return;
+  }
+
+  BEGIN_HASH_ITERATION(mappings_hash, itr)
+  {
+    fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
+  }
+  END_HASH_ITERATION(mappings_hash, itr)
+
+  fclose(file);
+}
+
+void SaveSetup_AddGameControllerMapping(char *mapping)
+{
+  char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
+  SetupFileHash *mappings_hash = newSetupFileHash();
+
+  InitUserDataDirectory();
+
+  // load existing personal game controller mappings
+  LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
+
+  // add new mapping to personal game controller mappings
+  addGameControllerMappingToHash(mappings_hash, mapping);
+
+  // save updated personal game controller mappings
+  SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
+
+  freeSetupFileHash(mappings_hash);
+  free(filename);
+}
+
 void LoadCustomElementDescriptions()
 {
   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
@@ -8757,6 +9646,39 @@ static void InitMenuDesignSettings_SpecialPreProcessing()
 
   /* special case: initialize "ARG_DEFAULT" values in static default config */
   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
+  titlescreen_initial_first_default.fade_mode  =
+    title_initial_first_default.fade_mode;
+  titlescreen_initial_first_default.fade_delay =
+    title_initial_first_default.fade_delay;
+  titlescreen_initial_first_default.post_delay =
+    title_initial_first_default.post_delay;
+  titlescreen_initial_first_default.auto_delay =
+    title_initial_first_default.auto_delay;
+  titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
+  titlescreen_first_default.fade_delay = title_first_default.fade_delay;
+  titlescreen_first_default.post_delay = title_first_default.post_delay;
+  titlescreen_first_default.auto_delay = title_first_default.auto_delay;
+  titlemessage_initial_first_default.fade_mode  =
+    title_initial_first_default.fade_mode;
+  titlemessage_initial_first_default.fade_delay =
+    title_initial_first_default.fade_delay;
+  titlemessage_initial_first_default.post_delay =
+    title_initial_first_default.post_delay;
+  titlemessage_initial_first_default.auto_delay =
+    title_initial_first_default.auto_delay;
+  titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
+  titlemessage_first_default.fade_delay = title_first_default.fade_delay;
+  titlemessage_first_default.post_delay = title_first_default.post_delay;
+  titlemessage_first_default.auto_delay = title_first_default.auto_delay;
+
+  titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
+  titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
+  titlescreen_initial_default.post_delay = title_initial_default.post_delay;
+  titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
+  titlescreen_default.fade_mode  = title_default.fade_mode;
+  titlescreen_default.fade_delay = title_default.fade_delay;
+  titlescreen_default.post_delay = title_default.post_delay;
+  titlescreen_default.auto_delay = title_default.auto_delay;
   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
@@ -8770,6 +9692,13 @@ static void InitMenuDesignSettings_SpecialPreProcessing()
   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
   {
+    titlescreen_initial_first[i] = titlescreen_initial_first_default;
+    titlescreen_first[i] = titlescreen_first_default;
+    titlemessage_initial_first[i] = titlemessage_initial_first_default;
+    titlemessage_first[i] = titlemessage_first_default;
+
+    titlescreen_initial[i] = titlescreen_initial_default;
+    titlescreen[i] = titlescreen_default;
     titlemessage_initial[i] = titlemessage_initial_default;
     titlemessage[i] = titlemessage_default;
   }
@@ -8825,12 +9754,48 @@ static void InitMenuDesignSettings_SpecialPostProcessing()
 
   /* 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 = *game_buttons_xy[i].src;
+}
+
+static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
+{
+  static struct
+  {
+    struct XYTileSize *dst, *src;
+    int graphic;
+  }
+  editor_buttons_xy[] =
+  {
+    {
+      &editor.button.element_left,     &editor.palette.element_left,
+      IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
+    },
+    {
+      &editor.button.element_middle,   &editor.palette.element_middle,
+      IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
+    },
+    {
+      &editor.button.element_right,    &editor.palette.element_right,
+      IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
+    },
+
+    { NULL,                    NULL                    }
+  };
+  int i;
+
+  /* set default position for element buttons to element graphics */
+  for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
+  {
+    if ((*editor_buttons_xy[i].dst).x == -1 &&
+       (*editor_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;
+      struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
+
+      gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
+
+      *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
     }
   }
 }
@@ -8877,6 +9842,10 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
   }
   title_info[] =
   {
+    /* initialize first titles from "enter screen" definitions, if defined */
+    { &title_initial_first_default,    "menu.enter_screen.TITLE"       },
+    { &title_first_default,            "menu.enter_screen.TITLE"       },
+
     /* initialize title screens from "next screen" definitions, if defined */
     { &title_initial_default,          "menu.next_screen.TITLE"        },
     { &title_default,                  "menu.next_screen.TITLE"        },
@@ -8890,15 +9859,37 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
   }
   titlemessage_arrays[] =
   {
-    /* initialize title messages from "next screen" definitions, if defined */
+    /* initialize first titles from "enter screen" definitions, if defined */
+    { titlescreen_initial_first,       "menu.enter_screen.TITLE"       },
+    { titlescreen_first,               "menu.enter_screen.TITLE"       },
+    { titlemessage_initial_first,      "menu.enter_screen.TITLE"       },
+    { titlemessage_first,              "menu.enter_screen.TITLE"       },
+
+    /* initialize titles from "next screen" definitions, if defined */
+    { titlescreen_initial,             "menu.next_screen.TITLE"        },
+    { titlescreen,                     "menu.next_screen.TITLE"        },
     { titlemessage_initial,            "menu.next_screen.TITLE"        },
     { titlemessage,                    "menu.next_screen.TITLE"        },
 
-    /* overwrite title messages with title definitions, if defined */
+    /* overwrite titles with title definitions, if defined */
+    { titlescreen_initial_first,       "[title_initial]"               },
+    { titlescreen_first,               "[title]"                       },
+    { titlemessage_initial_first,      "[title_initial]"               },
+    { titlemessage_first,              "[title]"                       },
+
+    { titlescreen_initial,             "[title_initial]"               },
+    { titlescreen,                     "[title]"                       },
     { titlemessage_initial,            "[title_initial]"               },
     { titlemessage,                    "[title]"                       },
 
-    /* overwrite title messages with title message definitions, if defined */
+    /* overwrite titles with title screen/message definitions, if defined */
+    { titlescreen_initial_first,       "[titlescreen_initial]"         },
+    { titlescreen_first,               "[titlescreen]"                 },
+    { titlemessage_initial_first,      "[titlemessage_initial]"        },
+    { titlemessage_first,              "[titlemessage]"                },
+
+    { titlescreen_initial,             "[titlescreen_initial]"         },
+    { titlescreen,                     "[titlescreen]"                 },
     { titlemessage_initial,            "[titlemessage_initial]"        },
     { titlemessage,                    "[titlemessage]"                },
 
@@ -8962,6 +9953,40 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
   }
 
+  /* special case: initialize with default values that may be overwritten */
+  /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
+  for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
+  {
+    char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
+    char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
+    char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
+    char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
+    char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
+    char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
+    char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
+    char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
+    char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
+
+    if (value_1 != NULL)
+      menu.left_spacing_info[i]      = get_integer_from_string(value_1);
+    if (value_2 != NULL)
+      menu.right_spacing_info[i]     = get_integer_from_string(value_2);
+    if (value_3 != NULL)
+      menu.top_spacing_info[i]       = get_integer_from_string(value_3);
+    if (value_4 != NULL)
+      menu.bottom_spacing_info[i]    = get_integer_from_string(value_4);
+    if (value_5 != NULL)
+      menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
+    if (value_6 != NULL)
+      menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
+    if (value_7 != NULL)
+      menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
+    if (value_8 != NULL)
+      menu.line_spacing_info[i]      = get_integer_from_string(value_8);
+    if (value_9 != NULL)
+      menu.extra_spacing_info[i]     = get_integer_from_string(value_9);
+  }
+
   /* special case: initialize with default values that may be overwritten */
   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
@@ -9112,7 +10137,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
 
        tfi = *info;
 
-       *(boolean *)title_tokens[j].value = (boolean)parameter_value;
+       *(int *)title_tokens[j].value = (int)parameter_value;
 
        *info = tfi;
       }
@@ -9142,9 +10167,9 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
          tmi = array[k];
 
          if (titlemessage_tokens[j].type == TYPE_INTEGER)
-           *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
-         else
            *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
+         else
+           *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
 
          array[k] = tmi;
        }
@@ -9192,6 +10217,11 @@ void LoadMenuDesignSettings()
   InitMenuDesignSettings_SpecialPostProcessing();
 }
 
+void LoadMenuDesignSettings_AfterGraphics()
+{
+  InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
+}
+
 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 {
   char *filename = getEditorSetupFilename();
@@ -9829,15 +10859,15 @@ void ConvertLevels()
 
   convert_level_nr = convert_leveldir->first_level;
 
-  printf_line("=", 79);
-  printf("Converting levels\n");
-  printf_line("-", 79);
-  printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
-  printf("Level series name:       '%s'\n", convert_leveldir->name);
-  printf("Level series author:     '%s'\n", convert_leveldir->author);
-  printf("Number of levels:        %d\n",   convert_leveldir->levels);
-  printf_line("=", 79);
-  printf("\n");
+  PrintLine("=", 79);
+  Print("Converting levels\n");
+  PrintLine("-", 79);
+  Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
+  Print("Level series name:       '%s'\n", convert_leveldir->name);
+  Print("Level series author:     '%s'\n", convert_leveldir->author);
+  Print("Number of levels:        %d\n",   convert_leveldir->levels);
+  PrintLine("=", 79);
+  Print("\n");
 
   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
     levels_failed[i] = FALSE;
@@ -9849,16 +10879,16 @@ void ConvertLevels()
 
     level_nr = convert_level_nr++;
 
-    printf("Level %03d: ", level_nr);
+    Print("Level %03d: ", level_nr);
 
     LoadLevel(level_nr);
-    if (level.no_valid_file)
+    if (level.no_level_file || level.no_valid_file)
     {
-      printf("(no level)\n");
+      Print("(no level)\n");
       continue;
     }
 
-    printf("converting level ... ");
+    Print("converting level ... ");
 
     level_filename = getDefaultLevelFilename(level_nr);
     new_level = !fileExists(level_filename);
@@ -9869,28 +10899,28 @@ void ConvertLevels()
 
       num_levels_converted++;
 
-      printf("converted.\n");
+      Print("converted.\n");
     }
     else
     {
       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
        levels_failed[level_nr] = TRUE;
 
-      printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
+      Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
     }
 
     num_levels_handled++;
   }
 
-  printf("\n");
-  printf_line("=", 79);
-  printf("Number of levels handled: %d\n", num_levels_handled);
-  printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
+  Print("\n");
+  PrintLine("=", 79);
+  Print("Number of levels handled: %d\n", num_levels_handled);
+  Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
         (num_levels_handled ?
          num_levels_converted * 100 / num_levels_handled : 0));
-  printf_line("-", 79);
-  printf("Summary (for automatic parsing by scripts):\n");
-  printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
+  PrintLine("-", 79);
+  Print("Summary (for automatic parsing by scripts):\n");
+  Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
         convert_leveldir->identifier, num_levels_converted,
         num_levels_handled,
         (num_levels_handled ?
@@ -9898,14 +10928,14 @@ void ConvertLevels()
 
   if (num_levels_handled != num_levels_converted)
   {
-    printf(", FAILED:");
+    Print(", FAILED:");
     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
       if (levels_failed[i])
-       printf(" %03d", i);
+       Print(" %03d", i);
   }
 
-  printf("\n");
-  printf_line("=", 79);
+  Print("\n");
+  PrintLine("=", 79);
 
   CloseAllAndExit(0);
 }
@@ -9996,6 +11026,8 @@ void CreateCustomElementImages(char *directory)
 
   SDLInitVideoDisplay();
 
+  ReCreateBitmap(&backbuffer, video.width, video.height);
+
   src_bitmap = LoadImage(src_filename);
 
   bitmap = CreateBitmap(TILEX * 16 * 2,