rnd-20040322-B-src
[rocksndiamonds.git] / src / files.c
index e365cd1f8c8dd4f28916a38b8541d6b43e9fa990..cfce89f8b639a74d7d16f5d8fb8a6d4a1fff3de1 100644 (file)
@@ -29,7 +29,7 @@
 #define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
 #define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
 #define LEVEL_HEADER_SIZE      80      /* size of level file header  */
-#define LEVEL_HEADER_UNUSED    13      /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED          /* unused level header bytes  */
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
 #define LEVEL_CHUNK_CNT3_HEADER        16      /* size of level CNT3 header  */
@@ -92,7 +92,10 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->can_change = FALSE;
 
   change->events = CE_BITMASK_DEFAULT;
-  change->sides = CH_SIDE_ANY;
+
+  change->trigger_player = CH_PLAYER_ANY;
+  change->trigger_side = CH_SIDE_ANY;
+  change->trigger_page = CH_PAGE_ANY;
 
   change->target_element = EL_EMPTY_SPACE;
 
@@ -103,15 +106,15 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->trigger_element = EL_EMPTY_SPACE;
 
   change->explode = FALSE;
-  change->use_content = FALSE;
-  change->only_complete = FALSE;
-  change->use_random_change = FALSE;
-  change->random = 100;
-  change->power = CP_NON_DESTRUCTIVE;
+  change->use_target_content = FALSE;
+  change->only_if_complete = FALSE;
+  change->use_random_replace = FALSE;
+  change->random_percentage = 100;
+  change->replace_when = CP_WHEN_EMPTY;
 
   for (x = 0; x < 3; x++)
     for (y = 0; y < 3; y++)
-      change->content[x][y] = EL_EMPTY_SPACE;
+      change->target_content[x][y] = EL_EMPTY_SPACE;
 
   change->direct_action = 0;
   change->other_action = 0;
@@ -123,6 +126,8 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 
 static void setLevelInfoToDefaults(struct LevelInfo *level)
 {
+  static boolean clipboard_elements_initialized = FALSE;
+
   int i, j, x, y;
 
   level->file_version = FILE_VERSION_ACTUAL;
@@ -141,15 +146,28 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->time = 100;
   level->gems_needed = 0;
+
   level->amoeba_speed = 10;
+
   level->time_magic_wall = 10;
   level->time_wheel = 10;
   level->time_light = 10;
   level->time_timegate = 10;
+
   level->amoeba_content = EL_DIAMOND;
+
   level->double_speed = FALSE;
   level->initial_gravity = FALSE;
   level->em_slippery_gems = FALSE;
+  level->block_last_field = FALSE;
+  level->sp_block_last_field = TRUE;
+  level->instant_relocation = FALSE;
+
+  level->can_move_into_acid_bits = ~0; /* everything can move into acid */
+  level->dont_collide_with_bits = ~0;  /* always deadly when colliding  */
+
+  level->use_spring_bug = FALSE;
+  level->use_step_counter = FALSE;
 
   level->use_custom_template = FALSE;
 
@@ -185,10 +203,16 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   {
     int element = i;
 
+    /* never initialize clipboard elements after the very first time */
+    if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
+      continue;
+
     setElementChangePages(&element_info[element], 1);
     setElementChangeInfoToDefaults(element_info[element].change);
 
-    if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
+    if (IS_CUSTOM_ELEMENT(element) ||
+       IS_GROUP_ELEMENT(element) ||
+       IS_INTERNAL_ELEMENT(element))
     {
       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
        element_info[element].description[j] = '\0';
@@ -204,18 +228,23 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
       element_info[element].gfx_element = EL_EMPTY_SPACE;
     }
 
-    if (IS_CUSTOM_ELEMENT(element))
+    if (IS_CUSTOM_ELEMENT(element) ||
+       IS_INTERNAL_ELEMENT(element))
     {
+      element_info[element].access_direction = MV_ALL_DIRECTIONS;
+
       element_info[element].collect_score = 10;                /* special default */
       element_info[element].collect_count = 1;         /* special default */
 
       element_info[element].push_delay_fixed = -1;     /* initialize later */
       element_info[element].push_delay_random = -1;    /* initialize later */
+      element_info[element].drop_delay_fixed = 0;
+      element_info[element].drop_delay_random = 0;
       element_info[element].move_delay_fixed = 0;
       element_info[element].move_delay_random = 0;
 
       element_info[element].move_pattern = MV_ALL_DIRECTIONS;
-      element_info[element].move_direction_initial = MV_AUTOMATIC;
+      element_info[element].move_direction_initial = MV_START_AUTOMATIC;
       element_info[element].move_stepsize = TILEX / 8;
       element_info[element].move_enter_element = EL_EMPTY_SPACE;
       element_info[element].move_leave_element = EL_EMPTY_SPACE;
@@ -223,12 +252,16 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
       element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
 
+      element_info[element].explosion_delay = 16;
+      element_info[element].ignition_delay = 8;
+
       for (x = 0; x < 3; x++)
        for (y = 0; y < 3; y++)
          element_info[element].content[x][y] = EL_EMPTY_SPACE;
 
       element_info[element].access_type = 0;
       element_info[element].access_layer = 0;
+      element_info[element].access_protected = 0;
       element_info[element].walk_to_action = 0;
       element_info[element].smash_targets = 0;
       element_info[element].deadliness = 0;
@@ -244,9 +277,14 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
       for (j = 0; j < NUM_EP_BITFIELDS; j++)
        Properties[element][j] = EP_BITMASK_DEFAULT;
 
+      /* now set default properties */
+      SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
+
       element_info[element].modified_settings = FALSE;
     }
-    else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
+
+    if (IS_GROUP_ELEMENT(element) ||
+       IS_INTERNAL_ELEMENT(element))
     {
       /* initialize memory for list of elements in group */
       if (element_info[element].group == NULL)
@@ -258,12 +296,16 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
       /* default: only one element in group */
       element_info[element].group->num_elements = 1;
+
+      element_info[element].group->choice_mode = ANIM_RANDOM;
     }
   }
 
+  clipboard_elements_initialized = TRUE;
+
   BorderElement = EL_STEELWALL;
 
-  level->no_level_file = FALSE;
+  level->no_valid_file = FALSE;
 
   if (leveldir_current == NULL)                /* only when dumping level */
     return;
@@ -523,7 +565,7 @@ static struct LevelFileInfo *getLevelFileInfo(int nr)
 
 int getMappedElement(int element)
 {
-  /* map some (historic, now obsolete) elements */
+  /* remap some (historic, now obsolete) elements */
 
 #if 1
   switch (element)
@@ -564,7 +606,7 @@ int getMappedElement(int element)
       {
        Error(ERR_WARN, "invalid level element %d", element);
 
-       element = EL_CHAR_QUESTION;
+       element = EL_UNKNOWN;
       }
       break;
   }
@@ -573,7 +615,7 @@ int getMappedElement(int element)
   {
     Error(ERR_WARN, "invalid level element %d", element);
 
-    element = EL_CHAR_QUESTION;
+    element = EL_UNKNOWN;
   }
   else if (element == EL_PLAYER_OBSOLETE)
     element = EL_PLAYER_1;
@@ -584,6 +626,32 @@ int getMappedElement(int element)
   return element;
 }
 
+int getMappedElementByVersion(int element, int game_version)
+{
+  /* remap some elements due to certain game version */
+
+  if (game_version <= VERSION_IDENT(2,2,0,0))
+  {
+    /* map game font elements */
+    element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
+              element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
+              element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
+              element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
+  }
+
+  if (game_version < VERSION_IDENT(3,0,0,0))
+  {
+    /* map Supaplex gravity tube elements */
+    element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
+              element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
+              element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
+              element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
+              element);
+  }
+
+  return element;
+}
+
 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
 {
   level->file_version = getFileVersion(file);
@@ -626,6 +694,16 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
 
   level->use_custom_template   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
+  level->block_last_field      = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+  level->sp_block_last_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+  level->can_move_into_acid_bits = getFile32BitBE(file);
+  level->dont_collide_with_bits = getFile8Bit(file);
+
+  level->use_spring_bug                = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+  level->use_step_counter      = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
+  level->instant_relocation    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
   return chunk_size;
@@ -905,16 +983,16 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
       getMappedElement(getFile16BitBE(file));
 
     element_info[element].change->explode = getFile8Bit(file);
-    element_info[element].change->use_content = getFile8Bit(file);
-    element_info[element].change->only_complete = getFile8Bit(file);
-    element_info[element].change->use_random_change = getFile8Bit(file);
+    element_info[element].change->use_target_content = getFile8Bit(file);
+    element_info[element].change->only_if_complete = getFile8Bit(file);
+    element_info[element].change->use_random_replace = getFile8Bit(file);
 
-    element_info[element].change->random = getFile8Bit(file);
-    element_info[element].change->power = getFile8Bit(file);
+    element_info[element].change->random_percentage = getFile8Bit(file);
+    element_info[element].change->replace_when = getFile8Bit(file);
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       element_info[element].change->content[x][y] =
+       element_info[element].change->target_content[x][y] =
          getMappedElement(getFile16BitBE(file));
 
     element_info[element].slippery_type = getFile8Bit(file);
@@ -975,11 +1053,14 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   ei->collect_score = getFile8Bit(file);
   ei->collect_count = getFile8Bit(file);
 
-  ei->push_delay_fixed = getFile16BitBE(file);
-  ei->push_delay_random = getFile16BitBE(file);
+  ei->drop_delay_fixed = getFile8Bit(file);
+  ei->push_delay_fixed = getFile8Bit(file);
+  ei->drop_delay_random = getFile8Bit(file);
+  ei->push_delay_random = getFile8Bit(file);
   ei->move_delay_fixed = getFile16BitBE(file);
   ei->move_delay_random = getFile16BitBE(file);
 
+  /* bits 0 - 15 of "move_pattern" ... */
   ei->move_pattern = getFile16BitBE(file);
   ei->move_direction_initial = getFile8Bit(file);
   ei->move_stepsize = getFile8Bit(file);
@@ -994,8 +1075,16 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
   ei->move_leave_type = getFile8Bit(file);
 
+  /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
+  ei->move_pattern |= (getFile16BitBE(file) << 16);
+
+  ei->access_direction = getFile8Bit(file);
+
+  ei->explosion_delay = getFile8Bit(file);
+  ei->ignition_delay = getFile8Bit(file);
+
   /* some free bytes for future custom property values and padding */
-  ReadUnusedBytesFromFile(file, 7);
+  ReadUnusedBytesFromFile(file, 2);
 
   /* read change property values */
 
@@ -1019,26 +1108,36 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
     change->trigger_element = getMappedElement(getFile16BitBE(file));
 
     change->explode = getFile8Bit(file);
-    change->use_content = getFile8Bit(file);
-    change->only_complete = getFile8Bit(file);
-    change->use_random_change = getFile8Bit(file);
+    change->use_target_content = getFile8Bit(file);
+    change->only_if_complete = getFile8Bit(file);
+    change->use_random_replace = getFile8Bit(file);
 
-    change->random = getFile8Bit(file);
-    change->power = getFile8Bit(file);
+    change->random_percentage = getFile8Bit(file);
+    change->replace_when = getFile8Bit(file);
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       change->content[x][y] = getMappedElement(getFile16BitBE(file));
+       change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
 
     change->can_change = getFile8Bit(file);
 
-    change->sides = getFile8Bit(file);
+    change->trigger_side = getFile8Bit(file);
 
-    if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
-      change->sides = CH_SIDE_ANY;
+#if 1
+    change->trigger_player = getFile8Bit(file);
+    change->trigger_page = getFile8Bit(file);
+
+    change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
+                           CH_PAGE_ANY : (1 << change->trigger_page));
+
+    /* some free bytes for future change property values and padding */
+    ReadUnusedBytesFromFile(file, 6);
+
+#else
 
     /* some free bytes for future change property values and padding */
     ReadUnusedBytesFromFile(file, 8);
+#endif
   }
 
   /* mark this custom element as modified */
@@ -1077,8 +1176,10 @@ static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
   ei->use_gfx_element = getFile8Bit(file);
   ei->gfx_element = getMappedElement(getFile16BitBE(file));
 
+  group->choice_mode = getFile8Bit(file);
+
   /* some free bytes for future values and padding */
-  ReadUnusedBytesFromFile(file, 4);
+  ReadUnusedBytesFromFile(file, 3);
 
   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
     group->element[i] = getMappedElement(getFile16BitBE(file));
@@ -1100,7 +1201,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     if (level != &level_template)
       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
@@ -1116,6 +1217,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "CAVE") != 0)
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
       fclose(file);
       return;
@@ -1130,6 +1233,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
       fclose(file);
       return;
@@ -1137,6 +1242,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
       fclose(file);
       return;
@@ -1268,7 +1375,7 @@ static int map_em_element_yam(int element)
 
     default:
       Error(ERR_WARN, "invalid level element %d", element);
-      return EL_CHAR_QUESTION;
+      return EL_UNKNOWN;
   }
 }
 
@@ -1282,10 +1389,13 @@ static int map_em_element_field(int element)
   switch (element)
   {
     case 0x00: return EL_ROCK;
+    case 0x01: return EL_ROCK;                         /* EMC */
     case 0x02: return EL_DIAMOND;
     case 0x03: return EL_DIAMOND;
     case 0x04: return EL_ROBOT;
     case 0x05: return EL_ROBOT;                        /* EMC */
+    case 0x06: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x07: return EL_EMPTY_SPACE;                  /* EMC */
     case 0x08: return EL_SPACESHIP_UP;
     case 0x09: return EL_SPACESHIP_RIGHT;
     case 0x0a: return EL_SPACESHIP_DOWN;
@@ -1294,7 +1404,9 @@ static int map_em_element_field(int element)
     case 0x0d: return EL_SPACESHIP_RIGHT;
     case 0x0e: return EL_SPACESHIP_DOWN;
     case 0x0f: return EL_SPACESHIP_LEFT;
+
     case 0x10: return EL_BOMB;
+    case 0x11: return EL_BOMB;                         /* EMC */
     case 0x12: return EL_EMERALD;
     case 0x13: return EL_EMERALD;
     case 0x14: return EL_BUG_UP;
@@ -1306,34 +1418,115 @@ static int map_em_element_field(int element)
     case 0x1a: return EL_BUG_DOWN;
     case 0x1b: return EL_BUG_LEFT;
     case 0x1c: return EL_AMOEBA_DROP;
+    case 0x1d: return EL_AMOEBA_DROP;                  /* EMC */
+    case 0x1e: return EL_AMOEBA_DROP;                  /* EMC */
+    case 0x1f: return EL_AMOEBA_DROP;                  /* EMC */
+
     case 0x20: return EL_ROCK;
+    case 0x21: return EL_BOMB;                         /* EMC */
+    case 0x22: return EL_DIAMOND;                      /* EMC */
+    case 0x23: return EL_EMERALD;                      /* EMC */
     case 0x24: return EL_MAGIC_WALL;
     case 0x25: return EL_NUT;
+    case 0x26: return EL_NUT;                          /* EMC */
+    case 0x27: return EL_NUT;                          /* EMC */
 
       /* looks like magic wheel, but is _always_ activated */
     case 0x28: return EL_ROBOT_WHEEL;                  /* EMC */
 
-    case 0x29: return EL_YAMYAM;
-    case 0x2a: return EL_YAMYAM;
-    case 0x2b: return EL_YAMYAM;                       /* EMC */
-    case 0x2c: return EL_YAMYAM;                       /* EMC */
+    case 0x29: return EL_YAMYAM;       /* up */
+    case 0x2a: return EL_YAMYAM;       /* down */
+    case 0x2b: return EL_YAMYAM;       /* left */      /* EMC */
+    case 0x2c: return EL_YAMYAM;       /* right */     /* EMC */
     case 0x2d: return EL_QUICKSAND_FULL;
+    case 0x2e: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x2f: return EL_EMPTY_SPACE;                  /* EMC */
+
+    case 0x30: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x31: return EL_SAND;                         /* EMC */
+    case 0x32: return EL_SAND;                         /* EMC */
+    case 0x33: return EL_SAND;                         /* EMC */
+    case 0x34: return EL_QUICKSAND_FULL;               /* EMC */
+    case 0x35: return EL_QUICKSAND_FULL;               /* EMC */
+    case 0x36: return EL_QUICKSAND_FULL;               /* EMC */
+    case 0x37: return EL_SAND;                         /* EMC */
+    case 0x38: return EL_ROCK;                         /* EMC */
     case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
     case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
-    case 0x3b: return EL_DYNAMITE_ACTIVE;
-    case 0x3c: return EL_DYNAMITE_ACTIVE;
-    case 0x3d: return EL_DYNAMITE_ACTIVE;
-    case 0x3e: return EL_DYNAMITE_ACTIVE;
+    case 0x3b: return EL_DYNAMITE_ACTIVE;      /* 1 */
+    case 0x3c: return EL_DYNAMITE_ACTIVE;      /* 2 */
+    case 0x3d: return EL_DYNAMITE_ACTIVE;      /* 3 */
+    case 0x3e: return EL_DYNAMITE_ACTIVE;      /* 4 */
     case 0x3f: return EL_ACID_POOL_BOTTOM;
-    case 0x40: return EL_EXIT_OPEN;
-    case 0x41: return EL_EXIT_OPEN;
-    case 0x42: return EL_EXIT_OPEN;
-    case 0x43: return EL_BALLOON;
-    case 0x4e: return EL_INVISIBLE_WALL;
-    case 0x65: return EL_ACID;                         /* EMC */
-    case 0x73: return EL_SAND;                         /* EMC */
+
+    case 0x40: return EL_EXIT_OPEN;    /* 1 */
+    case 0x41: return EL_EXIT_OPEN;    /* 2 */
+    case 0x42: return EL_EXIT_OPEN;    /* 3 */
+    case 0x43: return EL_BALLOON;                      /* EMC */
+    case 0x44: return EL_UNKNOWN;                      /* EMC ("plant") */
+    case 0x45: return EL_SPRING;                       /* EMC */
+    case 0x46: return EL_SPRING;       /* falling */   /* EMC */
+    case 0x47: return EL_SPRING;       /* left */      /* EMC */
+    case 0x48: return EL_SPRING;       /* right */     /* EMC */
+    case 0x49: return EL_UNKNOWN;                      /* EMC ("ball 1") */
+    case 0x4a: return EL_UNKNOWN;                      /* EMC ("ball 2") */
+    case 0x4b: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x4c: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x4d: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x4e: return EL_INVISIBLE_WALL;               /* EMC (? "android") */
+    case 0x4f: return EL_UNKNOWN;                      /* EMC ("android") */
+
+    case 0x50: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x51: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x52: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x53: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x54: return EL_UNKNOWN;                      /* EMC ("android") */
+    case 0x55: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x56: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x57: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x58: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x59: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5a: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5b: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5c: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5d: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5e: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x5f: return EL_EMPTY_SPACE;                  /* EMC */
+
+    case 0x60: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x61: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x62: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x63: return EL_SPRING;       /* left */      /* EMC */
+    case 0x64: return EL_SPRING;       /* right */     /* EMC */
+    case 0x65: return EL_ACID;         /* 1 */         /* EMC */
+    case 0x66: return EL_ACID;         /* 2 */         /* EMC */
+    case 0x67: return EL_ACID;         /* 3 */         /* EMC */
+    case 0x68: return EL_ACID;         /* 4 */         /* EMC */
+    case 0x69: return EL_ACID;         /* 5 */         /* EMC */
+    case 0x6a: return EL_ACID;         /* 6 */         /* EMC */
+    case 0x6b: return EL_ACID;         /* 7 */         /* EMC */
+    case 0x6c: return EL_ACID;         /* 8 */         /* EMC */
+    case 0x6d: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x6e: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x6f: return EL_EMPTY_SPACE;                  /* EMC */
+
+    case 0x70: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x71: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x72: return EL_NUT;          /* left */      /* EMC */
+    case 0x73: return EL_SAND;                         /* EMC (? "nut") */
     case 0x74: return EL_STEELWALL;
-    case 0x7b: return EL_ACID;
+    case 0x75: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x76: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x77: return EL_BOMB;         /* left */      /* EMC */
+    case 0x78: return EL_BOMB;         /* right */     /* EMC */
+    case 0x79: return EL_ROCK;         /* left */      /* EMC */
+    case 0x7a: return EL_ROCK;         /* right */     /* EMC */
+    case 0x7b: return EL_ACID;                         /* (? EMC "blank") */
+    case 0x7c: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x7d: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x7e: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0x7f: return EL_EMPTY_SPACE;                  /* EMC */
+
     case 0x80: return EL_EMPTY;
     case 0x81: return EL_WALL_SLIPPERY;
     case 0x82: return EL_SAND;
@@ -1347,63 +1540,101 @@ static int map_em_element_field(int element)
     case 0x8a: return EL_EM_GATE_2;
     case 0x8b: return EL_EM_GATE_4;
     case 0x8c: return EL_EM_GATE_3;
-    case 0x8d: return EL_INVISIBLE_WALL;               /* EMC */
+    case 0x8d: return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
     case 0x8e: return EL_EM_GATE_1_GRAY;
     case 0x8f: return EL_EM_GATE_2_GRAY;
+
     case 0x90: return EL_EM_GATE_4_GRAY;
     case 0x91: return EL_EM_GATE_3_GRAY;
     case 0x92: return EL_MAGIC_WALL;
-    case 0x94: return EL_QUICKSAND_EMPTY;
+    case 0x93: return EL_ROBOT_WHEEL;
+    case 0x94: return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
     case 0x95: return EL_ACID_POOL_TOPLEFT;
     case 0x96: return EL_ACID_POOL_TOPRIGHT;
     case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
     case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
-    case 0x99: return EL_ACID;
-    case 0x9a: return EL_AMOEBA_DEAD;
-    case 0x9b: return EL_AMOEBA_DEAD;
-    case 0x9c: return EL_AMOEBA_DEAD;
-    case 0x9d: return EL_AMOEBA_DEAD;
+    case 0x99: return EL_ACID;                 /* (? EMC "fake blank") */
+    case 0x9a: return EL_AMOEBA_DEAD;          /* 1 */
+    case 0x9b: return EL_AMOEBA_DEAD;          /* 2 */
+    case 0x9c: return EL_AMOEBA_DEAD;          /* 3 */
+    case 0x9d: return EL_AMOEBA_DEAD;          /* 4 */
     case 0x9e: return EL_EXIT_CLOSED;
-    case 0x9f: return EL_CHAR_LESS;                    /* EMC */
-    case 0x93: return EL_ROBOT_WHEEL;
-
-      /* looks like normal dust, but behaves like wall */
-    case 0xa0: return EL_WALL;                         /* EMC */
-
-    case 0xa8: return EL_EMC_WALL_1;                   /* EMC */
-    case 0xa9: return EL_EMC_WALL_2;                   /* EMC */
-    case 0xaa: return EL_EMC_WALL_3;                   /* EMC */
-    case 0xab: return EL_EMC_WALL_7;                   /* EMC */
+    case 0x9f: return EL_CHAR_LESS;            /* arrow left */
+
+      /* looks like normal sand, but behaves like wall */
+    case 0xa0: return EL_UNKNOWN;              /* EMC ("fake grass") */
+    case 0xa1: return EL_UNKNOWN;              /* EMC ("lenses") */
+    case 0xa2: return EL_UNKNOWN;              /* EMC ("magnify") */
+    case 0xa3: return EL_UNKNOWN;              /* EMC ("fake blank") */
+    case 0xa4: return EL_UNKNOWN;              /* EMC ("fake grass") */
+    case 0xa5: return EL_UNKNOWN;              /* EMC ("switch") */
+    case 0xa6: return EL_UNKNOWN;              /* EMC ("switch") */
+    case 0xa7: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xa8: return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
+    case 0xa9: return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
+    case 0xaa: return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
+    case 0xab: return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
+    case 0xac: return EL_CHAR_COMMA;                   /* EMC */
+    case 0xad: return EL_CHAR_QUOTEDBL;                /* EMC */
     case 0xae: return EL_CHAR_MINUS;                   /* EMC */
     case 0xaf: return EL_DYNAMITE;
-    case 0xb0: return EL_EMC_STEELWALL_1;              /* EMC */
-    case 0xb1: return EL_EMC_WALL_8;                   /* EMC */
 
-      /* (exact steel wall) */
-    case 0xb3: return EL_STEELWALL;                    /* EMC */
-
-    case 0xb4: return EL_WALL_SLIPPERY;                /* EMC */
-    case 0xb5: return EL_EMC_WALL_6;                   /* EMC */
-    case 0xb6: return EL_EMC_WALL_5;                   /* EMC */
-    case 0xb7: return EL_EMC_WALL_4;                   /* EMC */
+    case 0xb0: return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
+    case 0xb1: return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
+    case 0xb2: return EL_UNKNOWN;                      /* EMC ("decor 7") */
+    case 0xb3: return EL_STEELWALL;            /* 2 */ /* EMC */
+    case 0xb4: return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
+    case 0xb5: return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
+    case 0xb6: return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
+    case 0xb7: return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
     case 0xb8: return EL_BALLOON_SWITCH_ANY;           /* EMC */
     case 0xb9: return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
     case 0xba: return EL_BALLOON_SWITCH_DOWN;          /* EMC */
     case 0xbb: return EL_BALLOON_SWITCH_LEFT;          /* EMC */
     case 0xbc: return EL_BALLOON_SWITCH_UP;            /* EMC */
-    case 0xbd: return EL_SAND;                         /* EMC */
+    case 0xbd: return EL_SAND;                         /* EMC ("dirt") */
+    case 0xbe: return EL_UNKNOWN;                      /* EMC ("plant") */
+    case 0xbf: return EL_UNKNOWN;                      /* EMC ("key 5") */
+
+    case 0xc0: return EL_UNKNOWN;                      /* EMC ("key 6") */
+    case 0xc1: return EL_UNKNOWN;                      /* EMC ("key 7") */
+    case 0xc2: return EL_UNKNOWN;                      /* EMC ("key 8") */
+    case 0xc3: return EL_UNKNOWN;                      /* EMC ("door 5") */
+    case 0xc4: return EL_UNKNOWN;                      /* EMC ("door 6") */
+    case 0xc5: return EL_UNKNOWN;                      /* EMC ("door 7") */
+    case 0xc6: return EL_UNKNOWN;                      /* EMC ("door 8") */
+    case 0xc7: return EL_UNKNOWN;                      /* EMC ("bumper") */
+
+      /* characters: see above */
+
     case 0xec: return EL_CHAR_PERIOD;
     case 0xed: return EL_CHAR_EXCLAM;
     case 0xee: return EL_CHAR_COLON;
     case 0xef: return EL_CHAR_QUESTION;
-    case 0xf0: return EL_CHAR_GREATER;
-    case 0xf1: return EL_CHAR_COPYRIGHT;
-    case 0xfe: return EL_PLAYER_1;
-    case 0xff: return EL_PLAYER_2;
+
+    case 0xf0: return EL_CHAR_GREATER;                 /* arrow right */
+    case 0xf1: return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
+    case 0xf2: return EL_UNKNOWN;              /* EMC ("fake door 5") */
+    case 0xf3: return EL_UNKNOWN;              /* EMC ("fake door 6") */
+    case 0xf4: return EL_UNKNOWN;              /* EMC ("fake door 7") */
+    case 0xf5: return EL_UNKNOWN;              /* EMC ("fake door 8") */
+    case 0xf6: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xf7: return EL_EMPTY_SPACE;                  /* EMC */
+
+    case 0xf8: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xf9: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xfa: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xfb: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xfc: return EL_EMPTY_SPACE;                  /* EMC */
+    case 0xfd: return EL_EMPTY_SPACE;                  /* EMC */
+
+    case 0xfe: return EL_PLAYER_1;                     /* EMC: "blank" */
+    case 0xff: return EL_PLAYER_2;                     /* EMC: "blank" */
 
     default:
+      /* should never happen (all 8-bit value cases should be handled) */
       Error(ERR_WARN, "invalid level element %d", element);
-      return EL_CHAR_QUESTION;
+      return EL_UNKNOWN;
   }
 }
 
@@ -1426,7 +1657,7 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
@@ -1552,7 +1783,7 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
        Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
        Error(ERR_WARN, "invalid level element %d", element_old);
 
-       element_new = EL_CHAR_QUESTION;
+       element_new = EL_UNKNOWN;
       }
 
       level->field[x][y] = element_new;
@@ -1618,7 +1849,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
@@ -1628,7 +1859,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
   /* position file stream to the requested level inside the level package */
   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
 
@@ -1873,6 +2104,39 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
     if (level->game_version == VERSION_IDENT(2,0,1,0))
       level->em_slippery_gems = TRUE;
+
+    if (level->game_version < VERSION_IDENT(2,2,0,0))
+      level->use_spring_bug = TRUE;
+
+    if (level->game_version < VERSION_IDENT(3,1,0,0))
+    {
+      int i, j;
+
+      level->can_move_into_acid_bits = 0; /* nothing can move into acid */
+      level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
+
+      setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
+      setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
+      setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
+      setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
+
+      for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
+
+      for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+      {
+       int element = EL_CUSTOM_START + i;
+       struct ElementInfo *ei = &element_info[element];
+
+       for (j = 0; j < ei->num_change_pages; j++)
+       {
+         struct ElementChangeInfo *change = &ei->change_page[j];
+
+         change->trigger_player = CH_PLAYER_ANY;
+         change->trigger_page = CH_PAGE_ANY;
+       }
+      }
+    }
   }
   else
   {
@@ -1918,7 +2182,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
 {
-  int i, j;
+  int i, j, x, y;
 
   /* map custom element change events that have changed in newer versions
      (these following values were accidentally changed in version 3.0.1) */
@@ -1977,6 +2241,24 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
+  /* correct custom element values (for old levels without these options) */
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    if (ei->access_direction == MV_NO_MOVING)
+      ei->access_direction = MV_ALL_DIRECTIONS;
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      if (change->trigger_side == CH_SIDE_NONE)
+       change->trigger_side = CH_SIDE_ANY;
+    }
+  }
+
 #if 0
   /* set default push delay values (corrected since version 3.0.7-1) */
   if (level->game_version < VERSION_IDENT(3,0,7,1))
@@ -2002,6 +2284,16 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 #endif
 
+  /* 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][x][y] =
+         getMappedElementByVersion(level->yamyam_content[i][x][y],
+                                   level->game_version);
+
   /* initialize element properties for level editor etc. */
   InitElementPropertiesEngine(level->game_version);
 }
@@ -2017,6 +2309,9 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
     {
       int element = level->field[x][y];
 
+#if 1
+      element = getMappedElementByVersion(element, level->game_version);
+#else
       if (level->game_version <= VERSION_IDENT(2,2,0,0))
       {
        /* map game font elements */
@@ -2035,6 +2330,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
                   element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
                   element);
       }
+#endif
 
       level->field[x][y] = element;
     }
@@ -2136,6 +2432,16 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
 
   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
 
+  putFile8Bit(file, (level->block_last_field ? 1 : 0));
+  putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
+  putFile32BitBE(file, level->can_move_into_acid_bits);
+  putFile8Bit(file, level->dont_collide_with_bits);
+
+  putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
+  putFile8Bit(file, (level->use_step_counter ? 1 : 0));
+
+  putFile8Bit(file, (level->instant_relocation ? 1 : 0));
+
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
 
@@ -2361,12 +2667,12 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
        putFile16BitBE(file, element_info[element].change->trigger_element);
 
        putFile8Bit(file, element_info[element].change->explode);
-       putFile8Bit(file, element_info[element].change->use_content);
-       putFile8Bit(file, element_info[element].change->only_complete);
-       putFile8Bit(file, element_info[element].change->use_random_change);
+       putFile8Bit(file, element_info[element].change->use_target_content);
+       putFile8Bit(file, element_info[element].change->only_if_complete);
+       putFile8Bit(file, element_info[element].change->use_random_replace);
 
-       putFile8Bit(file, element_info[element].change->random);
-       putFile8Bit(file, element_info[element].change->power);
+       putFile8Bit(file, element_info[element].change->random_percentage);
+       putFile8Bit(file, element_info[element].change->replace_when);
 
        for (y = 0; y < 3; y++)
          for (x = 0; x < 3; x++)
@@ -2413,12 +2719,15 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
   putFile8Bit(file, ei->collect_score);
   putFile8Bit(file, ei->collect_count);
 
-  putFile16BitBE(file, ei->push_delay_fixed);
-  putFile16BitBE(file, ei->push_delay_random);
+  putFile8Bit(file, ei->drop_delay_fixed);
+  putFile8Bit(file, ei->push_delay_fixed);
+  putFile8Bit(file, ei->drop_delay_random);
+  putFile8Bit(file, ei->push_delay_random);
   putFile16BitBE(file, ei->move_delay_fixed);
   putFile16BitBE(file, ei->move_delay_random);
 
-  putFile16BitBE(file, ei->move_pattern);
+  /* bits 0 - 15 of "move_pattern" ... */
+  putFile16BitBE(file, ei->move_pattern & 0xffff);
   putFile8Bit(file, ei->move_direction_initial);
   putFile8Bit(file, ei->move_stepsize);
 
@@ -2432,8 +2741,16 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
   putFile16BitBE(file, ei->move_leave_element);
   putFile8Bit(file, ei->move_leave_type);
 
+  /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
+  putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
+
+  putFile8Bit(file, ei->access_direction);
+
+  putFile8Bit(file, ei->explosion_delay);
+  putFile8Bit(file, ei->ignition_delay);
+
   /* some free bytes for future custom property values and padding */
-  WriteUnusedBytesToFile(file, 7);
+  WriteUnusedBytesToFile(file, 2);
 
   /* write change property values */
 
@@ -2452,23 +2769,34 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
     putFile16BitBE(file, change->trigger_element);
 
     putFile8Bit(file, change->explode);
-    putFile8Bit(file, change->use_content);
-    putFile8Bit(file, change->only_complete);
-    putFile8Bit(file, change->use_random_change);
+    putFile8Bit(file, change->use_target_content);
+    putFile8Bit(file, change->only_if_complete);
+    putFile8Bit(file, change->use_random_replace);
 
-    putFile8Bit(file, change->random);
-    putFile8Bit(file, change->power);
+    putFile8Bit(file, change->random_percentage);
+    putFile8Bit(file, change->replace_when);
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       putFile16BitBE(file, change->content[x][y]);
+       putFile16BitBE(file, change->target_content[x][y]);
 
     putFile8Bit(file, change->can_change);
 
-    putFile8Bit(file, change->sides);
+    putFile8Bit(file, change->trigger_side);
+
+#if 1
+    putFile8Bit(file, change->trigger_player);
+    putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
+                      log_2(change->trigger_page)));
+
+    /* some free bytes for future change property values and padding */
+    WriteUnusedBytesToFile(file, 6);
+
+#else
 
     /* some free bytes for future change property values and padding */
     WriteUnusedBytesToFile(file, 8);
+#endif
   }
 }
 
@@ -2488,8 +2816,10 @@ static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
   putFile8Bit(file, ei->use_gfx_element);
   putFile16BitBE(file, ei->gfx_element);
 
+  putFile8Bit(file, group->choice_mode);
+
   /* some free bytes for future values and padding */
-  WriteUnusedBytesToFile(file, 4);
+  WriteUnusedBytesToFile(file, 3);
 
   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
     putFile16BitBE(file, group->element[i]);
@@ -2627,29 +2957,40 @@ void SaveLevelTemplate()
 
 void DumpLevel(struct LevelInfo *level)
 {
+  if (level->no_valid_file)
+  {
+    Error(ERR_WARN, "cannot dump -- no valid level file found");
+
+    return;
+  }
+
   printf_line("-", 79);
   printf("Level xxx (file version %08d, game version %08d)\n",
         level->file_version, level->game_version);
   printf_line("-", 79);
 
-  printf("Level Author: '%s'\n", level->author);
-  printf("Level Title:  '%s'\n", level->name);
+  printf("Level author: '%s'\n", level->author);
+  printf("Level title:  '%s'\n", level->name);
   printf("\n");
-  printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
+  printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
   printf("\n");
-  printf("Level Time:  %d seconds\n", level->time);
+  printf("Level time:  %d seconds\n", level->time);
   printf("Gems needed: %d\n", level->gems_needed);
   printf("\n");
-  printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
-  printf("Time for Wheel:      %d seconds\n", level->time_wheel);
-  printf("Time for Light:      %d seconds\n", level->time_light);
-  printf("Time for Timegate:   %d seconds\n", level->time_timegate);
+  printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
+  printf("Time for wheel:      %d seconds\n", level->time_wheel);
+  printf("Time for light:      %d seconds\n", level->time_light);
+  printf("Time for timegate:   %d seconds\n", level->time_timegate);
   printf("\n");
-  printf("Amoeba Speed: %d\n", level->amoeba_speed);
+  printf("Amoeba speed: %d\n", level->amoeba_speed);
   printf("\n");
-  printf("Gravity:                %s\n", (level->initial_gravity ? "yes" : "no"));
-  printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
-  printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
+  printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
+  printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
+  printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
+  printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
+  printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
+  printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
+  printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
 
   printf_line("-", 79);
 }
@@ -2681,6 +3022,8 @@ static void setTapeInfoToDefaults()
   tape.recording = FALSE;
   tape.playing = FALSE;
   tape.pausing = FALSE;
+
+  tape.no_valid_file = FALSE;
 }
 
 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
@@ -2845,7 +3188,15 @@ void LoadTapeFromFilename(char *filename)
   setTapeInfoToDefaults();
 
   if (!(file = fopen(filename, MODE_READ)))
+  {
+    tape.no_valid_file = TRUE;
+
+#if 0
+    Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
+#endif
+
     return;
+  }
 
   getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
@@ -2855,6 +3206,8 @@ void LoadTapeFromFilename(char *filename)
     getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "TAPE") != 0)
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
       fclose(file);
       return;
@@ -2869,6 +3222,8 @@ void LoadTapeFromFilename(char *filename)
 
     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
       fclose(file);
       return;
@@ -2876,6 +3231,8 @@ void LoadTapeFromFilename(char *filename)
 
     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
       fclose(file);
       return;
@@ -3092,11 +3449,21 @@ void DumpTape(struct TapeInfo *tape)
 {
   int i, j;
 
+#if 1
+  if (tape->no_valid_file)
+  {
+    Error(ERR_WARN, "cannot dump -- no valid tape file found");
+
+    return;
+  }
+#else
   if (TAPE_IS_EMPTY(*tape))
   {
     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
+
     return;
   }
+#endif
 
   printf_line("-", 79);
   printf("Tape of Level %03d (file version %08d, game version %08d)\n",