added option to disable warning about read-only levels when entering editor
[rocksndiamonds.git] / src / files.c
index be19967a77e4b5a63f03148318e45810ad03903a..e9634c5e03b3c833818ac8eaf60929852c5ca843 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // files.c
 // ============================================================================
@@ -58,7 +58,8 @@
 
 #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 2       // unused tape header bytes
+#define TAPE_CHUNK_HEAD_UNUSED 1       // unused tape header bytes
+#define TAPE_CHUNK_SCRN_SIZE   2       // size of screen size chunk
 
 #define LEVEL_CHUNK_CNT3_SIZE(x)        (LEVEL_CHUNK_CNT3_HEADER + (x))
 #define LEVEL_CHUNK_CUS3_SIZE(x)        (2 + (x) * LEVEL_CPART_CUS3_SIZE)
@@ -257,6 +258,12 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
     &li.solved_by_one_player,          FALSE
   },
 
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(12),
+    &li.time_score_base,               1
+  },
+
   {
     -1,                                        -1,
     -1,                                        -1,
@@ -307,6 +314,11 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(15),
     &li.lazy_relocation,               FALSE
   },
+  {
+    EL_PLAYER_1,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(16),
+    &li.finish_dig_collect,            TRUE
+  },
 
   // (these values are different for each player)
   {
@@ -768,9 +780,15 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.android_clone_time,            10
   },
   {
-    EL_EMC_ANDROID,                    -1,
+    EL_EMC_ANDROID,                    SAVE_CONF_NEVER,
     TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(1),
     &li.android_clone_element[0],      EL_EMPTY, NULL,
+    &li.num_android_clone_elements,    1, MAX_ANDROID_ELEMENTS_OLD
+  },
+  {
+    EL_EMC_ANDROID,                    -1,
+    TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(2),
+    &li.android_clone_element[0],      EL_EMPTY, NULL,
     &li.num_android_clone_elements,    1, MAX_ANDROID_ELEMENTS
   },
 
@@ -809,7 +827,7 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
   {
     EL_EMC_MAGIC_BALL,                 -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
-    &li.ball_state_initial,            FALSE
+    &li.ball_active_initial,           FALSE
   },
   {
     EL_EMC_MAGIC_BALL,                 -1,
@@ -2064,7 +2082,7 @@ static char *getPackedLevelBasename(int type)
 
   if ((dir = openDirectory(directory)) == NULL)
   {
-    Error(ERR_WARN, "cannot read current level directory '%s'", directory);
+    Warn("cannot read current level directory '%s'", directory);
 
     return basename;
   }
@@ -2328,7 +2346,7 @@ static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
 // functions for loading R'n'D level
 // ----------------------------------------------------------------------------
 
-static int getMappedElement(int element)
+int getMappedElement(int element)
 {
   // remap some (historic, now obsolete) elements
 
@@ -2369,7 +2387,7 @@ static int getMappedElement(int element)
     default:
       if (element >= NUM_FILE_ELEMENTS)
       {
-       Error(ERR_WARN, "invalid level element %d", element);
+       Warn("invalid level element %d", element);
 
        element = EL_UNKNOWN;
       }
@@ -2617,7 +2635,7 @@ static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
   }
   else
   {
-    Error(ERR_WARN, "cannot load content for element '%d'", element);
+    Warn("cannot load content for element '%d'", element);
   }
 
   return chunk_size;
@@ -2677,7 +2695,7 @@ static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
     if (IS_CUSTOM_ELEMENT(element))
       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
     else
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
 
     // older game versions that wrote level files with CUS1 chunks used
     // different default push delay values (not yet stored in level file)
@@ -2710,7 +2728,7 @@ static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
     if (IS_CUSTOM_ELEMENT(element))
       element_info[element].change->target_element = custom_target_element;
     else
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
   }
 
   level->file_has_custom_elements = TRUE;
@@ -2738,7 +2756,7 @@ static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
 
     if (!IS_CUSTOM_ELEMENT(element))
     {
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
 
       element = EL_INTERNAL_DUMMY;
     }
@@ -2824,9 +2842,10 @@ static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
 
   if (!IS_CUSTOM_ELEMENT(element))
   {
-    Error(ERR_WARN, "invalid custom element number %d", element);
+    Warn("invalid custom element number %d", element);
 
     ReadUnusedBytesFromFile(file, chunk_size - 2);
+
     return chunk_size;
   }
 
@@ -2972,9 +2991,10 @@ static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
 
   if (!IS_GROUP_ELEMENT(element))
   {
-    Error(ERR_WARN, "invalid group element number %d", element);
+    Warn("invalid group element number %d", element);
 
     ReadUnusedBytesFromFile(file, chunk_size - 2);
+
     return chunk_size;
   }
 
@@ -3036,9 +3056,8 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
 
        if (num_entities > max_num_entities)
        {
-         Error(ERR_WARN,
-               "truncating number of entities for element %d from %d to %d",
-               element, num_entities, max_num_entities);
+         Warn("truncating number of entities for element %d from %d to %d",
+              element, num_entities, max_num_entities);
 
          num_entities = max_num_entities;
        }
@@ -3047,8 +3066,7 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
                                  data_type == TYPE_CONTENT_LIST))
        {
          // for element and content lists, zero entities are not allowed
-         Error(ERR_WARN, "found empty list of entities for element %d",
-               element);
+         Warn("found empty list of entities for element %d", element);
 
          // do not set "num_entities" here to prevent reading behind buffer
 
@@ -3139,9 +3157,9 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
     int error_element = real_element;
 
-    Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
-         error_conf_chunk_bytes, error_conf_chunk_token,
-         error_element, EL_NAME(error_element));
+    Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
+        error_conf_chunk_bytes, error_conf_chunk_token,
+        error_element, EL_NAME(error_element));
   }
 
   return micro_chunk_size;
@@ -3258,8 +3276,8 @@ static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
 
   if (ei->num_change_pages == -1)
   {
-    Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
-         EL_NAME(element));
+    Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
+        EL_NAME(element));
 
     ei->num_change_pages = 1;
 
@@ -3346,7 +3364,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     if (level_info_only)
       return;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    Warn("cannot read level '%s' -- using empty level", filename);
 
     if (!setup.editor.use_template_for_new_levels)
       return;
@@ -3373,7 +3391,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3392,7 +3410,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3403,7 +3421,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+      Warn("unsupported version of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3464,15 +3482,17 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
       if (chunk_info[i].name == NULL)
       {
-       Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
-             chunk_name, filename);
+       Warn("unknown chunk '%s' in level file '%s'",
+            chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else if (chunk_info[i].size != -1 &&
               chunk_info[i].size != chunk_size)
       {
-       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
-             chunk_size, chunk_name, filename);
+       Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+            chunk_size, chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else
@@ -3486,8 +3506,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
        // information, so check them here
        if (chunk_size_expected != chunk_size)
        {
-         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
-               chunk_size, chunk_name, filename);
+         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+              chunk_size, chunk_name, filename);
        }
       }
     }
@@ -3515,118 +3535,98 @@ static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
     { 2, 2 },
   };
   struct LevelInfo_EM *level_em = level->native_em_level;
-  struct LEVEL *lev = level_em->lev;
-  struct PLAYER **ply = level_em->ply;
+  struct CAVE *cav = level_em->cav;
   int i, j, x, y;
 
-  lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
-  lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
+  cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
+  cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
+
+  cav->time_seconds    = level->time;
+  cav->gems_needed     = level->gems_needed;
 
-  lev->time_seconds     = level->time;
-  lev->required_initial = level->gems_needed;
+  cav->emerald_score   = level->score[SC_EMERALD];
+  cav->diamond_score   = level->score[SC_DIAMOND];
+  cav->alien_score     = level->score[SC_ROBOT];
+  cav->tank_score      = level->score[SC_SPACESHIP];
+  cav->bug_score       = level->score[SC_BUG];
+  cav->eater_score     = level->score[SC_YAMYAM];
+  cav->nut_score       = level->score[SC_NUT];
+  cav->dynamite_score  = level->score[SC_DYNAMITE];
+  cav->key_score       = level->score[SC_KEY];
+  cav->exit_score      = level->score[SC_TIME_BONUS];
 
-  lev->emerald_score   = level->score[SC_EMERALD];
-  lev->diamond_score   = level->score[SC_DIAMOND];
-  lev->alien_score     = level->score[SC_ROBOT];
-  lev->tank_score      = level->score[SC_SPACESHIP];
-  lev->bug_score       = level->score[SC_BUG];
-  lev->eater_score     = level->score[SC_YAMYAM];
-  lev->nut_score       = level->score[SC_NUT];
-  lev->dynamite_score  = level->score[SC_DYNAMITE];
-  lev->key_score       = level->score[SC_KEY];
-  lev->exit_score      = level->score[SC_TIME_BONUS];
+  cav->num_eater_arrays        = level->num_yamyam_contents;
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       lev->eater_array[i][y * 3 + x] =
-         map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
+       cav->eater_array[i][y * 3 + x] =
+         map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
 
-  lev->amoeba_time             = level->amoeba_speed;
-  lev->wonderwall_time_initial = level->time_magic_wall;
-  lev->wheel_time              = level->time_wheel;
+  cav->amoeba_time             = level->amoeba_speed;
+  cav->wonderwall_time         = level->time_magic_wall;
+  cav->wheel_time              = level->time_wheel;
 
-  lev->android_move_time       = level->android_move_time;
-  lev->android_clone_time      = level->android_clone_time;
-  lev->ball_random             = level->ball_random;
-  lev->ball_state_initial      = level->ball_state_initial;
-  lev->ball_time               = level->ball_time;
-  lev->num_ball_arrays         = level->num_ball_contents;
+  cav->android_move_time       = level->android_move_time;
+  cav->android_clone_time      = level->android_clone_time;
+  cav->ball_random             = level->ball_random;
+  cav->ball_active             = level->ball_active_initial;
+  cav->ball_time               = level->ball_time;
+  cav->num_ball_arrays         = level->num_ball_contents;
 
-  lev->lenses_score            = level->lenses_score;
-  lev->magnify_score           = level->magnify_score;
-  lev->slurp_score             = level->slurp_score;
+  cav->lenses_score            = level->lenses_score;
+  cav->magnify_score           = level->magnify_score;
+  cav->slurp_score             = level->slurp_score;
 
-  lev->lenses_time             = level->lenses_time;
-  lev->magnify_time            = level->magnify_time;
+  cav->lenses_time             = level->lenses_time;
+  cav->magnify_time            = level->magnify_time;
 
-  lev->wind_direction_initial =
+  cav->wind_direction =
     map_direction_RND_to_EM(level->wind_direction_initial);
-  lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
-                          lev->wind_time : 0);
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (j = 0; j < 8; j++)
-      lev->ball_array[i][j] =
-       map_element_RND_to_EM(level->
-                             ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+      cav->ball_array[i][j] =
+       map_element_RND_to_EM_cave(level->ball_content[i].
+                                  e[ball_xy[j][0]][ball_xy[j][1]]);
 
   map_android_clone_elements_RND_to_EM(level);
 
-  // first fill the complete playfield with the default border element
+  // first fill the complete playfield with the empty space element
   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
-      level_em->cave[x][y] = ZBORDER;
-
-  if (BorderElement == EL_STEELWALL)
-  {
-    for (y = 0; y < lev->height + 2; y++)
-      for (x = 0; x < lev->width + 2; x++)
-       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
-  }
+      cav->cave[x][y] = Cblank;
 
   // then copy the real level contents from level file into the playfield
-  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
   {
-    int new_element = map_element_RND_to_EM(level->field[x][y]);
-    int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
-    int xx = x + 1 + offset;
-    int yy = y + 1 + offset;
+    int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
 
     if (level->field[x][y] == EL_AMOEBA_DEAD)
-      new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
+      new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
 
-    level_em->cave[xx][yy] = new_element;
+    cav->cave[x][y] = new_element;
   }
 
   for (i = 0; i < MAX_PLAYERS; i++)
   {
-    ply[i]->x_initial = 0;
-    ply[i]->y_initial = 0;
+    cav->player_x[i] = -1;
+    cav->player_y[i] = -1;
   }
 
   // initialize player positions and delete players from the playfield
-  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
   {
     if (ELEM_IS_PLAYER(level->field[x][y]))
     {
       int player_nr = GET_PLAYER_NR(level->field[x][y]);
-      int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
-      int xx = x + 1 + offset;
-      int yy = y + 1 + offset;
 
-      ply[player_nr]->x_initial = xx;
-      ply[player_nr]->y_initial = yy;
+      cav->player_x[player_nr] = x;
+      cav->player_y[player_nr] = y;
 
-      level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
+      cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
     }
   }
-
-  if (BorderElement == EL_STEELWALL)
-  {
-    lev->width  += 2;
-    lev->height += 2;
-  }
 }
 
 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
@@ -3643,69 +3643,68 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
     { 2, 2 },
   };
   struct LevelInfo_EM *level_em = level->native_em_level;
-  struct LEVEL *lev = level_em->lev;
-  struct PLAYER **ply = level_em->ply;
+  struct CAVE *cav = level_em->cav;
   int i, j, x, y;
 
-  level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
-  level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
+  level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
+  level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
 
-  level->time        = lev->time_seconds;
-  level->gems_needed = lev->required_initial;
+  level->time        = cav->time_seconds;
+  level->gems_needed = cav->gems_needed;
 
   sprintf(level->name, "Level %d", level->file_info.nr);
 
-  level->score[SC_EMERALD]     = lev->emerald_score;
-  level->score[SC_DIAMOND]     = lev->diamond_score;
-  level->score[SC_ROBOT]       = lev->alien_score;
-  level->score[SC_SPACESHIP]   = lev->tank_score;
-  level->score[SC_BUG]         = lev->bug_score;
-  level->score[SC_YAMYAM]      = lev->eater_score;
-  level->score[SC_NUT]         = lev->nut_score;
-  level->score[SC_DYNAMITE]    = lev->dynamite_score;
-  level->score[SC_KEY]         = lev->key_score;
-  level->score[SC_TIME_BONUS]  = lev->exit_score;
+  level->score[SC_EMERALD]     = cav->emerald_score;
+  level->score[SC_DIAMOND]     = cav->diamond_score;
+  level->score[SC_ROBOT]       = cav->alien_score;
+  level->score[SC_SPACESHIP]   = cav->tank_score;
+  level->score[SC_BUG]         = cav->bug_score;
+  level->score[SC_YAMYAM]      = cav->eater_score;
+  level->score[SC_NUT]         = cav->nut_score;
+  level->score[SC_DYNAMITE]    = cav->dynamite_score;
+  level->score[SC_KEY]         = cav->key_score;
+  level->score[SC_TIME_BONUS]  = cav->exit_score;
 
-  level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
+  level->num_yamyam_contents   = cav->num_eater_arrays;
 
-  for (i = 0; i < level->num_yamyam_contents; i++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
        level->yamyam_content[i].e[x][y] =
-         map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
+         map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
 
-  level->amoeba_speed          = lev->amoeba_time;
-  level->time_magic_wall       = lev->wonderwall_time_initial;
-  level->time_wheel            = lev->wheel_time;
+  level->amoeba_speed          = cav->amoeba_time;
+  level->time_magic_wall       = cav->wonderwall_time;
+  level->time_wheel            = cav->wheel_time;
 
-  level->android_move_time     = lev->android_move_time;
-  level->android_clone_time    = lev->android_clone_time;
-  level->ball_random           = lev->ball_random;
-  level->ball_state_initial    = lev->ball_state_initial;
-  level->ball_time             = lev->ball_time;
-  level->num_ball_contents     = lev->num_ball_arrays;
+  level->android_move_time     = cav->android_move_time;
+  level->android_clone_time    = cav->android_clone_time;
+  level->ball_random           = cav->ball_random;
+  level->ball_active_initial   = cav->ball_active;
+  level->ball_time             = cav->ball_time;
+  level->num_ball_contents     = cav->num_ball_arrays;
 
-  level->lenses_score          = lev->lenses_score;
-  level->magnify_score         = lev->magnify_score;
-  level->slurp_score           = lev->slurp_score;
+  level->lenses_score          = cav->lenses_score;
+  level->magnify_score         = cav->magnify_score;
+  level->slurp_score           = cav->slurp_score;
 
-  level->lenses_time           = lev->lenses_time;
-  level->magnify_time          = lev->magnify_time;
+  level->lenses_time           = cav->lenses_time;
+  level->magnify_time          = cav->magnify_time;
 
   level->wind_direction_initial =
-    map_direction_EM_to_RND(lev->wind_direction_initial);
+    map_direction_EM_to_RND(cav->wind_direction);
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (j = 0; j < 8; j++)
       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
-       map_element_EM_to_RND(lev->ball_array[i][j]);
+       map_element_EM_to_RND_cave(cav->ball_array[i][j]);
 
   map_android_clone_elements_EM_to_RND(level);
 
   // convert the playfield (some elements need special treatment)
   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
   {
-    int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
+    int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
 
     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
       new_element = EL_AMOEBA_DEAD;
@@ -3717,12 +3716,15 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   {
     // in case of all players set to the same field, use the first player
     int nr = MAX_PLAYERS - i - 1;
-    int jx = ply[nr]->x_initial - 1;
-    int jy = ply[nr]->y_initial - 1;
+    int jx = cav->player_x[nr];
+    int jy = cav->player_y[nr];
 
     if (jx != -1 && jy != -1)
       level->field[jx][jy] = EL_PLAYER_1 + nr;
   }
+
+  // time score is counted for each 10 seconds left in Emerald Mine levels
+  level->time_score_base = 10;
 }
 
 
@@ -3835,7 +3837,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
       {
        num_invalid_elements++;
 
-       Error(ERR_DEBUG, "invalid element %d at position %d, %d",
+       Debug("level:native:SP", "invalid element %d at position %d, %d",
              element_old, x, y);
       }
 
@@ -3844,8 +3846,8 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
   }
 
   if (num_invalid_elements > 0)
-    Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
-         (!options.debug ? " (use '--debug' for more details)" : ""));
+    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] =
@@ -3881,8 +3883,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
     if (port_x < 0 || port_x >= level->fieldx ||
        port_y < 0 || port_y >= level->fieldy)
     {
-      Error(ERR_WARN, "special port position (%d, %d) out of bounds",
-           port_x, port_y);
+      Warn("special port position (%d, %d) out of bounds", port_x, port_y);
 
       continue;
     }
@@ -3892,7 +3893,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
        port_element > EL_SP_GRAVITY_PORT_UP)
     {
-      Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+      Warn("no special port at position (%d, %d)", port_x, port_y);
 
       continue;
     }
@@ -3958,8 +3959,8 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
 
     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);
+      Warn("tape truncated: size exceeds maximum SP demo size %d",
+          SP_MAX_TAPE_LEN);
 
       break;
     }
@@ -4003,7 +4004,7 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     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 };
+    byte action[MAX_TAPE_ACTIONS] = { tape_action };
     boolean success = 0;
     int j;
 
@@ -4012,8 +4013,8 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
 
     if (!success)
     {
-      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
-           MAX_TAPE_LEN);
+      Warn("SP demo truncated: size exceeds maximum tape size %d",
+          MAX_TAPE_LEN);
 
       break;
     }
@@ -4288,7 +4289,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x13f5:
-      element = EL_YAMYAM;
+      element = EL_YAMYAM_UP;
       break;
 
     case 0x1425:
@@ -5314,7 +5315,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1682:       // secret gate (red)
-      element = EL_GATE_1_GRAY;
+      element = EL_EM_GATE_1_GRAY;
       break;
 
     case 0x1683:       // gate (yellow)
@@ -5322,7 +5323,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1684:       // secret gate (yellow)
-      element = EL_GATE_2_GRAY;
+      element = EL_EM_GATE_2_GRAY;
       break;
 
     case 0x1685:       // gate (blue)
@@ -5330,7 +5331,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1686:       // secret gate (blue)
-      element = EL_GATE_4_GRAY;
+      element = EL_EM_GATE_4_GRAY;
       break;
 
     case 0x1687:       // gate (green)
@@ -5338,7 +5339,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1688:       // secret gate (green)
-      element = EL_GATE_3_GRAY;
+      element = EL_EM_GATE_3_GRAY;
       break;
 
     case 0x1689:       // gate (white)
@@ -5569,7 +5570,8 @@ static int getMappedElement_DC(int element)
        element = EL_INVISIBLE_SAND;
       else
       {
-       Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
+       Warn("unknown Diamond Caves element 0x%04x", element);
+
        element = EL_UNKNOWN;
       }
       break;
@@ -5617,7 +5619,7 @@ static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot decode level from stream -- using empty level");
+    Warn("cannot decode level from stream -- using empty level");
 
     return;
   }
@@ -5730,9 +5732,19 @@ static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
   level->extra_time            = header[56] | (header[57] << 8);
   level->shield_normal_time    = header[58] | (header[59] << 8);
 
+  // shield and extra time elements do not have a score
+  level->score[SC_SHIELD]      = 0;
+  level->extra_time_score      = 0;
+
+  // set time for normal and deadly shields to the same value
+  level->shield_deadly_time    = level->shield_normal_time;
+
   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
   // can slip down from flat walls, like normal walls and steel walls
   level->em_slippery_gems = TRUE;
+
+  // time score is counted for each 10 seconds left in Diamond Caves levels
+  level->time_score_base = 10;
 }
 
 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
@@ -5750,7 +5762,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     level->no_valid_file = TRUE;
 
     if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+      Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -5768,8 +5780,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
-           filename);
+      Warn("unknown DC level file '%s' -- using empty level", filename);
 
       return;
     }
@@ -5795,8 +5806,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
        {
          level->no_valid_file = TRUE;
 
-         Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
-               filename);
+         Warn("cannot fseek in file '%s' -- using empty level", filename);
 
          return;
        }
@@ -5814,8 +5824,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
-           filename);
+      Warn("unknown DC2 level file '%s' -- using empty level", filename);
 
       return;
     }
@@ -5890,7 +5899,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
     level->no_valid_file = TRUE;
 
     if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+      Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -6064,7 +6073,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -6259,7 +6268,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level)
   if (level->game_version < VERSION_IDENT(3,2,0,5))
   {
     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
-    level->score[SC_TIME_BONUS] /= 10;
+    level->time_score_base = 10;
   }
 
   if (leveldir_current->latest_engine)
@@ -6431,6 +6440,10 @@ static void LoadLevel_InitVersion(struct LevelInfo *level)
   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
   if (level->game_version < VERSION_IDENT(4,1,1,1))
     level->sb_objects_needed = FALSE;
+
+  // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
+  if (level->game_version <= VERSION_IDENT(4,2,2,0))
+    level->finish_dig_collect = FALSE;
 }
 
 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
@@ -6611,7 +6624,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level)
   // copy elements to runtime playfield array
   for (x = 0; x < MAX_LEV_FIELDX; x++)
     for (y = 0; y < MAX_LEV_FIELDY; y++)
-      Feld[x][y] = level->field[x][y];
+      Tile[x][y] = level->field[x][y];
 
   // initialize level size variables for faster access
   lev_fieldx = level->fieldx;
@@ -6646,7 +6659,7 @@ void LoadLevelTemplate(int nr)
 {
   if (!fileExists(getGlobalLevelTemplateFilename()))
   {
-    Error(ERR_WARN, "no level template found for this level");
+    Warn("no level template found for this level");
 
     return;
   }
@@ -6809,8 +6822,8 @@ static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   int chunk_size = 0;
   int x, y;
 
-  for (y = 0; y < level->fieldy; y++) 
-    for (x = 0; x < level->fieldx; x++) 
+  for (y = 0; y < level->fieldy; y++)
+    for (x = 0; x < level->fieldx; x++)
       if (level->encoding_16bit_field)
        chunk_size += putFile16BitBE(file, level->field[x][y]);
       else
@@ -6887,7 +6900,8 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     // chunk header already written -- write empty chunk data
     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
 
-    Error(ERR_WARN, "cannot save content for element '%d'", element);
+    Warn("cannot save content for element '%d'", element);
+
     return;
   }
 
@@ -6955,7 +6969,7 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
   }
 
   if (check != num_changed_custom_elements)    // should not happen
-    Error(ERR_WARN, "inconsistent number of custom element properties");
+    Warn("inconsistent number of custom element properties");
 }
 #endif
 
@@ -6984,7 +6998,7 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
   }
 
   if (check != num_changed_custom_elements)    // should not happen
-    Error(ERR_WARN, "inconsistent number of custom target elements");
+    Warn("inconsistent number of custom target elements");
 }
 #endif
 
@@ -7067,7 +7081,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
   }
 
   if (check != num_changed_custom_elements)    // should not happen
-    Error(ERR_WARN, "inconsistent number of custom element properties");
+    Warn("inconsistent number of custom element properties");
 }
 #endif
 
@@ -7445,7 +7459,8 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save level file '%s'", filename);
+    Warn("cannot save level file '%s'", filename);
+
     return;
   }
 
@@ -7570,7 +7585,7 @@ void DumpLevel(struct LevelInfo *level)
 {
   if (level->no_level_file || level->no_valid_file)
   {
-    Error(ERR_WARN, "cannot dump -- no valid level file found");
+    Warn("cannot dump -- no valid level file found");
 
     return;
   }
@@ -7625,6 +7640,8 @@ static void setTapeInfoToDefaults(void)
   // at least one (default: the first) player participates in every tape
   tape.num_participating_players = 1;
 
+  tape.property_bits = TAPE_PROPERTY_NONE;
+
   tape.level_nr = level_nr;
   tape.counter = 0;
   tape.changed = FALSE;
@@ -7633,9 +7650,48 @@ static void setTapeInfoToDefaults(void)
   tape.playing = FALSE;
   tape.pausing = FALSE;
 
+  tape.scr_fieldx = SCR_FIELDX_DEFAULT;
+  tape.scr_fieldy = SCR_FIELDY_DEFAULT;
+
   tape.no_valid_file = FALSE;
 }
 
+static int getTapePosSize(struct TapeInfo *tape)
+{
+  int tape_pos_size = 0;
+
+  if (tape->use_key_actions)
+    tape_pos_size += tape->num_participating_players;
+
+  if (tape->use_mouse_actions)
+    tape_pos_size += 3;                // x and y position and mouse button mask
+
+  tape_pos_size += 1;          // tape action delay value
+
+  return tape_pos_size;
+}
+
+static void setTapeActionFlags(struct TapeInfo *tape, int value)
+{
+  tape->use_key_actions = FALSE;
+  tape->use_mouse_actions = FALSE;
+
+  if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
+    tape->use_key_actions = TRUE;
+
+  if (value != TAPE_USE_KEY_ACTIONS_ONLY)
+    tape->use_mouse_actions = TRUE;
+}
+
+static int getTapeActionValue(struct TapeInfo *tape)
+{
+  return (tape->use_key_actions &&
+         tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
+         tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
+         tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
+         TAPE_ACTIONS_DEFAULT);
+}
+
 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
 {
   tape->file_version = getFileVersion(file);
@@ -7671,7 +7727,9 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
-    tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+    setTapeActionFlags(tape, getFile8Bit(file));
+
+    tape->property_bits = getFile8Bit(file);
 
     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
 
@@ -7685,6 +7743,14 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
+static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
+{
+  tape->scr_fieldx = getFile8Bit(file);
+  tape->scr_fieldy = getFile8Bit(file);
+
+  return chunk_size;
+}
+
 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int level_identifier_size;
@@ -7708,8 +7774,7 @@ 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 tape_pos_size =
-    (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
+  int tape_pos_size = getTapePosSize(tape);
   int chunk_size_expected = tape_pos_size * tape->length;
 
   if (chunk_size_expected != chunk_size)
@@ -7722,25 +7787,17 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   {
     if (i >= MAX_TAPE_LEN)
     {
-      Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+      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);
+       ReadUnusedBytesFromFile(file, tape_pos_size);
 
       break;
     }
 
-    if (tape->use_mouse)
-    {
-      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);
-
-      tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
-    }
-    else
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
       {
@@ -7751,6 +7808,13 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
+    if (tape->use_mouse_actions)
+    {
+      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);
+    }
+
     tape->pos[i].delay = getFile8Bit(file);
 
     if (tape->file_version == FILE_VERSION_1_0)
@@ -7869,7 +7933,7 @@ static void LoadTape_SokobanSolution(char *filename)
       default:
        tape.no_valid_file = TRUE;
 
-       Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
+       Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
 
        break;
     }
@@ -7918,7 +7982,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+      Warn("unknown format of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7937,7 +8001,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+      Warn("unknown format of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7948,7 +8012,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
+      Warn("unsupported version of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7977,6 +8041,7 @@ void LoadTapeFromFilename(char *filename)
     {
       { "VERS", TAPE_CHUNK_VERS_SIZE,  LoadTape_VERS },
       { "HEAD", TAPE_CHUNK_HEAD_SIZE,  LoadTape_HEAD },
+      { "SCRN", TAPE_CHUNK_SCRN_SIZE,  LoadTape_SCRN },
       { "INFO", -1,                    LoadTape_INFO },
       { "BODY", -1,                    LoadTape_BODY },
       {  NULL,  0,                     NULL }
@@ -7992,15 +8057,17 @@ void LoadTapeFromFilename(char *filename)
 
       if (chunk_info[i].name == NULL)
       {
-       Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
+       Warn("unknown chunk '%s' in tape file '%s'",
              chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else if (chunk_info[i].size != -1 &&
               chunk_info[i].size != chunk_size)
       {
-       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+       Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
              chunk_size, chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else
@@ -8014,7 +8081,7 @@ void LoadTapeFromFilename(char *filename)
        // information, so check them here
        if (chunk_size_expected != chunk_size)
        {
-         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
                chunk_size, chunk_name, filename);
        }
       }
@@ -8027,9 +8094,12 @@ void LoadTapeFromFilename(char *filename)
   tape.length_seconds = GetTapeLengthSeconds();
 
 #if 0
-  printf("::: tape file version: %d\n",   tape.file_version);
-  printf("::: tape game version: %d\n",   tape.game_version);
-  printf("::: tape engine version: %d\n", tape.engine_version);
+  Debug("files:LoadTapeFromFilename", "tape file version: %d",
+       tape.file_version);
+  Debug("files:LoadTapeFromFilename", "tape game version: %d",
+       tape.game_version);
+  Debug("files:LoadTapeFromFilename", "tape engine version: %d",
+       tape.engine_version);
 #endif
 }
 
@@ -8052,6 +8122,14 @@ void LoadSolutionTape(int nr)
     CopyNativeTape_SP_to_RND(&level);
 }
 
+static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
+{
+  // chunk required for team mode tapes with non-default screen size
+  return (tape->num_participating_players > 1 &&
+         (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
+          tape->scr_fieldy != SCR_FIELDY_DEFAULT));
+}
+
 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
 {
   putFileVersion(file, tape->file_version);
@@ -8074,7 +8152,9 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 
   putFile8Bit(file, store_participating_players);
 
-  putFile8Bit(file, (tape->use_mouse ? 1 : 0));
+  putFile8Bit(file, getTapeActionValue(tape));
+
+  putFile8Bit(file, tape->property_bits);
 
   // unused bytes not at the end here for 4-byte alignment of engine_version
   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
@@ -8082,6 +8162,12 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
   putFileVersion(file, tape->engine_version);
 }
 
+static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
+{
+  putFile8Bit(file, tape->scr_fieldx);
+  putFile8Bit(file, tape->scr_fieldy);
+}
+
 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
 {
   int level_identifier_size = strlen(tape->level_identifier) + 1;
@@ -8101,50 +8187,39 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    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
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
        if (tape->player_participates[j])
          putFile8Bit(file, tape->pos[i].action[j]);
     }
 
+    if (tape->use_mouse_actions)
+    {
+      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]);
+    }
+
     putFile8Bit(file, tape->pos[i].delay);
   }
 }
 
-void SaveTape(int nr)
+void SaveTapeToFilename(char *filename)
 {
-  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;
-
-  InitTapeDirectory(leveldir_current->subdir);
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save level recording file '%s'", filename);
+    Warn("cannot save level recording file '%s'", filename);
+
     return;
   }
 
-  tape.file_version = FILE_VERSION_ACTUAL;
-  tape.game_version = GAME_VERSION_ACTUAL;
-
-  // count number of participating players
-  for (i = 0; i < MAX_PLAYERS; i++)
-    if (tape.player_participates[i])
-      num_participating_players++;
-
-  tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
+  tape_pos_size = getTapePosSize(&tape);
 
   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
   body_chunk_size = tape_pos_size * tape.length;
@@ -8158,6 +8233,12 @@ void SaveTape(int nr)
   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
   SaveTape_HEAD(file, &tape);
 
+  if (checkSaveTape_SCRN(&tape))
+  {
+    putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
+    SaveTape_SCRN(file, &tape);
+  }
+
   putFileChunkBE(file, "INFO", info_chunk_size);
   SaveTape_INFO(file, &tape);
 
@@ -8167,6 +8248,26 @@ void SaveTape(int nr)
   fclose(file);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+void SaveTape(int nr)
+{
+  char *filename = getTapeFilename(nr);
+  int i;
+
+  InitTapeDirectory(leveldir_current->subdir);
+
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
+
+  tape.num_participating_players = 0;
+
+  // count number of participating players
+  for (i = 0; i < MAX_PLAYERS; i++)
+    if (tape.player_participates[i])
+      tape.num_participating_players++;
+
+  SaveTapeToFilename(filename);
 
   tape.changed = FALSE;
 }
@@ -8209,7 +8310,7 @@ void DumpTape(struct TapeInfo *tape)
 
   if (tape->no_valid_file)
   {
-    Error(ERR_WARN, "cannot dump -- no valid tape file found");
+    Warn("cannot dump -- no valid tape file found");
 
     return;
   }
@@ -8289,15 +8390,18 @@ void LoadScore(int nr)
 
   if (!checkCookieString(cookie, SCORE_COOKIE))
   {
-    Error(ERR_WARN, "unknown format of score file '%s'", filename);
+    Warn("unknown format of score file '%s'", filename);
+
     fclose(file);
+
     return;
   }
 
   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
   {
     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
-      Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
+      Warn("fscanf() failed; %s", strerror(errno));
+
     if (fgets(line, MAX_LINE_LEN, file) == NULL)
       line[0] = '\0';
 
@@ -8330,7 +8434,8 @@ void SaveScore(int nr)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save score for level %d", nr);
+    Warn("cannot save score for level %d", nr);
+
     return;
   }
 
@@ -8358,6 +8463,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_STRING,
     &setup.player_name,                                "player_name"
   },
+  {
+    TYPE_SWITCH,
+    &setup.multiple_users,                     "multiple_users"
+  },
   {
     TYPE_SWITCH,
     &setup.sound,                              "sound"
@@ -8382,6 +8491,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.scroll_delay,                       "scroll_delay"
   },
+  {
+    TYPE_SWITCH,
+    &setup.forced_scroll_delay,                        "forced_scroll_delay"
+  },
   {
     TYPE_INTEGER,
     &setup.scroll_delay_value,                 "scroll_delay_value"
@@ -8482,6 +8595,14 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.prefer_aga_graphics,                        "prefer_aga_graphics"
   },
+  {
+    TYPE_SWITCH,
+    &setup.prefer_lowpass_sounds,              "prefer_lowpass_sounds"
+  },
+  {
+    TYPE_SWITCH,
+    &setup.prefer_extra_panel_items,           "prefer_extra_panel_items"
+  },
   {
     TYPE_SWITCH,
     &setup.game_speed_extended,                        "game_speed_extended"
@@ -8626,6 +8747,10 @@ static struct TokenInfo editor_setup_tokens[] =
     TYPE_SWITCH,
     &setup.editor.show_element_token,          "editor.show_element_token"
   },
+  {
+    TYPE_SWITCH,
+    &setup.editor.show_read_only_warning,      "editor.show_read_only_warning"
+  },
 };
 
 static struct TokenInfo editor_cascade_setup_tokens[] =
@@ -8859,6 +8984,10 @@ static struct TokenInfo player_setup_tokens[] =
 
 static struct TokenInfo system_setup_tokens[] =
 {
+  {
+    TYPE_STRING,
+    &setup.system.sdl_renderdriver,            "system.sdl_renderdriver"
+  },
   {
     TYPE_STRING,
     &setup.system.sdl_videodriver,             "system.sdl_videodriver"
@@ -8951,6 +9080,10 @@ static struct TokenInfo internal_setup_tokens[] =
     TYPE_BOOLEAN,
     &setup.internal.show_scaling_in_title,     "show_scaling_in_title"
   },
+  {
+    TYPE_BOOLEAN,
+    &setup.internal.create_user_levelset,      "create_user_levelset"
+  },
   {
     TYPE_BOOLEAN,
     &setup.internal.menu_game,                 "menu_game"
@@ -9086,6 +9219,14 @@ static struct TokenInfo debug_setup_tokens[] =
     TYPE_BOOLEAN,
     &setup.debug.show_frames_per_second,       "debug.show_frames_per_second"
   },
+  {
+    TYPE_SWITCH3,
+    &setup.debug.xsn_mode,                     "debug.xsn_mode"
+  },
+  {
+    TYPE_INTEGER,
+    &setup.debug.xsn_percent,                  "debug.xsn_percent"
+  },
 };
 
 static struct TokenInfo options_setup_tokens[] =
@@ -9096,26 +9237,13 @@ static struct TokenInfo options_setup_tokens[] =
   },
 };
 
-static char *get_corrected_login_name(char *login_name)
-{
-  // needed because player name must be a fixed length string
-  char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
-
-  strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
-  login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
-
-  if (strlen(login_name) > MAX_PLAYER_NAME_LEN)                // name has been cut
-    if (strchr(login_name_new, ' '))
-      *strchr(login_name_new, ' ') = '\0';
-
-  return login_name_new;
-}
-
 static void setSetupInfoToDefaults(struct SetupInfo *si)
 {
   int i;
 
-  si->player_name = get_corrected_login_name(getLoginName());
+  si->player_name = getStringCopy(getDefaultUserName(user.nr));
+
+  si->multiple_users = TRUE;
 
   si->sound = TRUE;
   si->sound_loops = TRUE;
@@ -9123,6 +9251,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->sound_simple = TRUE;
   si->toons = TRUE;
   si->scroll_delay = TRUE;
+  si->forced_scroll_delay = FALSE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
@@ -9148,6 +9277,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->quick_switch = FALSE;
   si->input_on_focus = FALSE;
   si->prefer_aga_graphics = TRUE;
+  si->prefer_lowpass_sounds = FALSE;
+  si->prefer_extra_panel_items = TRUE;
   si->game_speed_extended = FALSE;
   si->game_frame_delay = GAME_FRAME_DELAY;
   si->sp_show_border_elements = FALSE;
@@ -9251,6 +9382,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->editor.show_element_token                = FALSE;
 
+  si->editor.show_read_only_warning    = TRUE;
+
   si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game       = DEFAULT_KEY_SAVE_GAME;
@@ -9299,6 +9432,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
   }
 
+  si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
@@ -9324,6 +9458,7 @@ 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.create_user_levelset = TRUE;
 
   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
@@ -9355,11 +9490,16 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->debug.show_frames_per_second = FALSE;
 
+  si->debug.xsn_mode = AUTO;
+  si->debug.xsn_percent = 0;
+
   si->options.verbose = FALSE;
 
 #if defined(PLATFORM_ANDROID)
   si->fullscreen = TRUE;
 #endif
+
+  setHideSetupEntry(&setup.debug.xsn_mode);
 }
 
 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
@@ -9406,10 +9546,21 @@ void setHideSetupEntry(void *setup_value)
 {
   char *hide_setup_token = getHideSetupToken(setup_value);
 
+  if (hide_setup_hash == NULL)
+    hide_setup_hash = newSetupFileHash();
+
   if (setup_value != NULL)
     setHashEntry(hide_setup_hash, hide_setup_token, "");
 }
 
+void removeHideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  if (setup_value != NULL)
+    removeHashEntry(hide_setup_hash, hide_setup_token);
+}
+
 boolean hideSetupEntry(void *setup_value)
 {
   char *hide_setup_token = getHideSetupToken(setup_value);
@@ -9431,6 +9582,8 @@ static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
   // check if this setup option should be hidden in the setup menu
   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
     setHideSetupEntry(token_info[token_nr].value);
+
+  free(token_hide_text);
 }
 
 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
@@ -9448,9 +9601,6 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   if (!setup_file_hash)
     return;
 
-  if (hide_setup_hash == NULL)
-    hide_setup_hash = newSetupFileHash();
-
   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
 
@@ -9557,6 +9707,43 @@ static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
                              editor_cascade_setup_tokens[i].text));
 }
 
+void LoadUserNames(void)
+{
+  int last_user_nr = user.nr;
+  int i;
+
+  if (global.user_names != NULL)
+  {
+    for (i = 0; i < MAX_PLAYER_NAMES; i++)
+      checked_free(global.user_names[i]);
+
+    checked_free(global.user_names);
+  }
+
+  global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
+
+  for (i = 0; i < MAX_PLAYER_NAMES; i++)
+  {
+    user.nr = i;
+
+    SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
+
+    if (setup_file_hash)
+    {
+      char *player_name = getHashEntry(setup_file_hash, "player_name");
+
+      global.user_names[i] = getFixedUserName(player_name);
+
+      freeSetupFileHash(setup_file_hash);
+    }
+
+    if (global.user_names[i] == NULL)
+      global.user_names[i] = getStringCopy(getDefaultUserName(i));
+  }
+
+  user.nr = last_user_nr;
+}
+
 void LoadSetupFromFilename(char *filename)
 {
   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
@@ -9569,7 +9756,7 @@ void LoadSetupFromFilename(char *filename)
   }
   else
   {
-    Error(ERR_DEBUG, "using default setup values");
+    Debug("setup", "using default setup values");
   }
 }
 
@@ -9578,7 +9765,7 @@ static void LoadSetup_SpecialPostProcessing(void)
   char *player_name_new;
 
   // needed to work around problems with fixed length strings
-  player_name_new = get_corrected_login_name(setup.player_name);
+  player_name_new = getFixedUserName(setup.player_name);
   free(setup.player_name);
   setup.player_name = player_name_new;
 
@@ -9686,7 +9873,7 @@ static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+    Warn("cannot read game controller mappings file '%s'", filename);
 
     return;
   }
@@ -9714,7 +9901,8 @@ void SaveSetup(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    Warn("cannot write setup file '%s'", filename);
+
     return;
   }
 
@@ -9723,7 +9911,8 @@ void SaveSetup(void)
   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
   {
     // just to make things nicer :)
-    if (global_setup_tokens[i].value == &setup.sound                   ||
+    if (global_setup_tokens[i].value == &setup.multiple_users          ||
+       global_setup_tokens[i].value == &setup.sound                    ||
        global_setup_tokens[i].value == &setup.graphics_set             ||
        global_setup_tokens[i].value == &setup.volume_simple            ||
        global_setup_tokens[i].value == &setup.network_mode             ||
@@ -9791,7 +9980,9 @@ void SaveSetup(void)
 
   fprintf(file, "\n");
   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
-    fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
+    if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
+       setup.debug.xsn_mode != AUTO)
+      fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
 
   fprintf(file, "\n");
   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
@@ -9812,8 +10003,10 @@ void SaveSetup_AutoSetup(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
+    Warn("cannot write auto setup file '%s'", filename);
+
     free(filename);
+
     return;
   }
 
@@ -9839,8 +10032,10 @@ void SaveSetup_EditorCascade(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
+    Warn("cannot write editor cascade state file '%s'", filename);
+
     free(filename);
+
     return;
   }
 
@@ -9863,7 +10058,7 @@ static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+    Warn("cannot write game controller mappings file '%s'", filename);
 
     return;
   }
@@ -9936,7 +10131,7 @@ static int getElementFromToken(char *token)
   if (value != NULL)
     return atoi(value);
 
-  Error(ERR_WARN, "unknown element token '%s'", token);
+  Warn("unknown element token '%s'", token);
 
   return EL_UNDEFINED;
 }
@@ -10064,17 +10259,43 @@ static boolean string_has_parameter(char *s, char *s_contained)
 
 static int get_anim_parameter_value(char *s)
 {
-  char *pattern_1 = "click:anim_";
+  int event_value[] =
+  {
+    ANIM_EVENT_CLICK,
+    ANIM_EVENT_INIT,
+    ANIM_EVENT_START,
+    ANIM_EVENT_END,
+    ANIM_EVENT_POST
+  };
+  char *pattern_1[] =
+  {
+    "click:anim_",
+    "init:anim_",
+    "start:anim_",
+    "end:anim_",
+    "post:anim_"
+  };
   char *pattern_2 = ".part_";
   char *matching_char = NULL;
   char *s_ptr = s;
+  int pattern_1_len = 0;
   int result = ANIM_EVENT_NONE;
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE(event_value); i++)
+  {
+    matching_char = strstr(s_ptr, pattern_1[i]);
+    pattern_1_len = strlen(pattern_1[i]);
+    result = event_value[i];
+
+    if (matching_char != NULL)
+      break;
+  }
 
-  matching_char = strstr(s_ptr, pattern_1);
   if (matching_char == NULL)
     return ANIM_EVENT_NONE;
 
-  s_ptr = matching_char + strlen(pattern_1);
+  s_ptr = matching_char + pattern_1_len;
 
   // check for main animation number ("anim_X" or "anim_XX")
   if (*s_ptr >= '0' && *s_ptr <= '9')
@@ -10142,6 +10363,9 @@ static int get_anim_parameter_values(char *s)
       string_has_parameter(s, "self"))
     event_value |= ANIM_EVENT_SELF;
 
+  if (string_has_parameter(s, "unclick:any"))
+    event_value |= ANIM_EVENT_UNCLICK_ANY;
+
   // if animation event found, add it to global animation event list
   if (event_value != ANIM_EVENT_NONE)
     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
@@ -10164,6 +10388,10 @@ static int get_anim_parameter_values(char *s)
 
 static int get_anim_action_parameter_value(char *token)
 {
+  // check most common default case first to massively speed things up
+  if (strEqual(token, ARG_UNDEFINED))
+    return ANIM_EVENT_ACTION_NONE;
+
   int result = getImageIDFromToken(token);
 
   if (result == -1)
@@ -10261,7 +10489,10 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
   {
     result = get_anim_parameter_values(value);
   }
-  else if (strEqual(suffix, ".init_event_action") ||
+  else if (strEqual(suffix, ".init_delay_action") ||
+          strEqual(suffix, ".anim_delay_action") ||
+          strEqual(suffix, ".post_delay_action") ||
+          strEqual(suffix, ".init_event_action") ||
           strEqual(suffix, ".anim_event_action"))
   {
     result = get_anim_action_parameter_value(value_raw);
@@ -10284,6 +10515,12 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
     if (string_has_parameter(value, "reverse"))
       result |= STYLE_REVERSE;
 
+    if (string_has_parameter(value, "leftmost_position"))
+      result |= STYLE_LEFTMOST_POSITION;
+
+    if (string_has_parameter(value, "block_clicks"))
+      result |= STYLE_BLOCK;
+
     if (string_has_parameter(value, "passthrough_clicks"))
       result |= STYLE_PASSTHROUGH;
 
@@ -10299,6 +10536,12 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
              FADE_MODE_DEFAULT);
   }
+  else if (strEqual(suffix, ".auto_delay_unit"))
+  {
+    result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
+             string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
+             AUTO_DELAY_UNIT_DEFAULT);
+  }
   else if (strPrefix(suffix, ".font"))         // (may also be ".font_xyz")
   {
     result = gfx.get_font_from_token_function(value);
@@ -10365,10 +10608,14 @@ static void InitMenuDesignSettings_SpecialPreProcessing(void)
     title_initial_first_default.post_delay;
   titlescreen_initial_first_default.auto_delay =
     title_initial_first_default.auto_delay;
+  titlescreen_initial_first_default.auto_delay_unit =
+    title_initial_first_default.auto_delay_unit;
   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;
+  titlescreen_first_default.auto_delay_unit =
+    title_first_default.auto_delay_unit;
   titlemessage_initial_first_default.fade_mode  =
     title_initial_first_default.fade_mode;
   titlemessage_initial_first_default.fade_delay =
@@ -10377,27 +10624,36 @@ static void InitMenuDesignSettings_SpecialPreProcessing(void)
     title_initial_first_default.post_delay;
   titlemessage_initial_first_default.auto_delay =
     title_initial_first_default.auto_delay;
+  titlemessage_initial_first_default.auto_delay_unit =
+    title_initial_first_default.auto_delay_unit;
   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;
+  titlemessage_first_default.auto_delay_unit =
+    title_first_default.auto_delay_unit;
 
   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_initial_default.auto_delay_unit =
+    title_initial_default.auto_delay_unit;
   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;
+  titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
   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;
-  titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
+  titlemessage_initial_default.auto_delay_unit =
+    title_initial_default.auto_delay_unit;
   titlemessage_default.fade_mode  = title_default.fade_mode;
   titlemessage_default.fade_delay = title_default.fade_delay;
   titlemessage_default.post_delay = title_default.post_delay;
   titlemessage_default.auto_delay = title_default.auto_delay;
+  titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
 
   // special case: initialize "ARG_DEFAULT" values in static default config
   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
@@ -10786,6 +11042,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     { TYPE_INTEGER,    &tfi.fade_delay,        ".fade_delay"           },
     { TYPE_INTEGER,    &tfi.post_delay,        ".post_delay"           },
     { TYPE_INTEGER,    &tfi.auto_delay,        ".auto_delay"           },
+    { TYPE_INTEGER,    &tfi.auto_delay_unit,   ".auto_delay_unit"      },
 
     { -1,              NULL,                   NULL                    }
   };
@@ -10808,6 +11065,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     { TYPE_INTEGER,    &tmi.fade_delay,        ".fade_delay"           },
     { TYPE_INTEGER,    &tmi.post_delay,        ".post_delay"           },
     { TYPE_INTEGER,    &tmi.auto_delay,        ".auto_delay"           },
+    { TYPE_INTEGER,    &tmi.auto_delay_unit,   ".auto_delay_unit"      },
 
     { -1,              NULL,                   NULL                    }
   };
@@ -11229,19 +11487,19 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
     {
       if (num_unknown_tokens == 0)
       {
-       Error(ERR_INFO_LINE, "-");
-       Error(ERR_INFO, "warning: unknown token(s) found in config file:");
-       Error(ERR_INFO, "- config file: '%s'", filename);
+       Warn("---");
+       Warn("unknown token(s) found in config file:");
+       Warn("- config file: '%s'", filename);
 
        num_unknown_tokens++;
       }
 
-      Error(ERR_INFO, "- token: '%s'", list->token);
+      Warn("- token: '%s'", list->token);
     }
   }
 
   if (num_unknown_tokens > 0)
-    Error(ERR_INFO_LINE, "-");
+    Warn("---");
 
   while (*num_elements % 4)    // pad with empty elements, if needed
     (*elements)[(*num_elements)++] = EL_EMPTY;
@@ -11251,8 +11509,8 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 
 #if 0
   for (i = 0; i < *num_elements; i++)
-    printf("editor: element '%s' [%d]\n",
-          element_info[(*elements)[i]].token_name, (*elements)[i]);
+    Debug("editor", "element '%s' [%d]\n",
+         element_info[(*elements)[i]].token_name, (*elements)[i]);
 #endif
 }
 
@@ -11431,7 +11689,8 @@ void LoadMusicInfo(void)
 
   if ((dir = openDirectory(music_directory)) == NULL)
   {
-    Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+    Warn("cannot read music directory '%s'", music_directory);
+
     return;
   }
 
@@ -11519,18 +11778,18 @@ static void print_unknown_token(char *filename, char *token, int token_nr)
 {
   if (token_nr == 0)
   {
-    Error(ERR_INFO_LINE, "-");
-    Error(ERR_INFO, "warning: unknown token(s) found in config file:");
-    Error(ERR_INFO, "- config file: '%s'", filename);
+    Warn("---");
+    Warn("unknown token(s) found in config file:");
+    Warn("- config file: '%s'", filename);
   }
 
-  Error(ERR_INFO, "- token: '%s'", token);
+  Warn("- token: '%s'", token);
 }
 
 static void print_unknown_token_end(int token_nr)
 {
   if (token_nr > 0)
-    Error(ERR_INFO_LINE, "-");
+    Warn("---");
 }
 
 void LoadHelpAnimInfo(void)
@@ -11734,12 +11993,12 @@ void LoadHelpAnimInfo(void)
 
 #if 0
   for (i = 0; i < num_list_entries; i++)
-    printf("::: '%s': %d, %d, %d => %d\n",
-          EL_NAME(helpanim_info[i].element),
-          helpanim_info[i].element,
-          helpanim_info[i].action,
-          helpanim_info[i].direction,
-          helpanim_info[i].delay);
+    Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
+         EL_NAME(helpanim_info[i].element),
+         helpanim_info[i].element,
+         helpanim_info[i].action,
+         helpanim_info[i].direction,
+         helpanim_info[i].delay);
 #endif
 }
 
@@ -11771,8 +12030,8 @@ void LoadHelpTextInfo(void)
 #if 0
   BEGIN_HASH_ITERATION(helptext_info, itr)
   {
-    printf("::: '%s' => '%s'\n",
-          HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+    Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
+         HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
   }
   END_HASH_ITERATION(hash, itr)
 #endif
@@ -11798,8 +12057,7 @@ void ConvertLevels(void)
                                               global.convert_leveldir);
 
   if (convert_leveldir == NULL)
-    Error(ERR_EXIT, "no such level identifier: '%s'",
-         global.convert_leveldir);
+    Fail("no such level identifier: '%s'", global.convert_leveldir);
 
   leveldir_current = convert_leveldir;
 
@@ -11926,13 +12184,13 @@ void CreateLevelSketchImages(void)
     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
 
     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
-      Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
+      Fail("cannot save level sketch image file '%s'", filename1);
 
     DrawSizedElement(0, 0, element, MINI_TILESIZE);
     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
 
     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
-      Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
+      Fail("cannot save level sketch image file '%s'", filename2);
 
     free(filename1);
     free(filename2);
@@ -11958,7 +12216,7 @@ void CreateLevelSketchImages(void)
   if (options.debug)
     fprintf(stderr, "\n");
 
-  Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
+  Info("%d normal and small images created", NUM_FILE_ELEMENTS);
 
   CloseAllAndExit(0);
 }
@@ -12056,7 +12314,7 @@ void CreateCustomElementImages(char *directory)
   }
 
   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
-    Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
+    Fail("cannot save CE graphics file '%s'", dst_filename);
 
   FreeBitmap(bitmap);