rnd-20051125-1-src
[rocksndiamonds.git] / src / files.c
index 20eadecd7b52d3d85d445af79ab7d82994a9234a..f5275728156c3e921462a8cf5d030149262672b1 100644 (file)
 #define TAPE_COOKIE_TMPL       "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
 #define SCORE_COOKIE           "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
 
+static struct
+{
+  int filetype;
+  char *id;
+}
+filetype_id_list[] =
+{
+  { LEVEL_FILE_TYPE_RND,       "RND"   },
+  { LEVEL_FILE_TYPE_BD,                "BD"    },
+  { LEVEL_FILE_TYPE_EM,                "EM"    },
+  { LEVEL_FILE_TYPE_SP,                "SP"    },
+  { LEVEL_FILE_TYPE_DX,                "DX"    },
+  { LEVEL_FILE_TYPE_SB,                "SB"    },
+  { LEVEL_FILE_TYPE_DC,                "DC"    },
+  { -1,                                NULL    },
+};
+
 
 /* ========================================================================= */
 /* level file functions                                                      */
@@ -71,11 +88,12 @@ void setElementChangePages(struct ElementInfo *ei, int change_pages)
 
 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 {
-  int x, y;
+  int i, x, y;
 
   change->can_change = FALSE;
 
-  change->events = CE_BITMASK_DEFAULT;
+  for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+    change->has_event[i] = FALSE;
 
   change->trigger_player = CH_PLAYER_ANY;
   change->trigger_side = CH_SIDE_ANY;
@@ -96,6 +114,11 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->random_percentage = 100;
   change->replace_when = CP_WHEN_EMPTY;
 
+  change->has_action = FALSE;
+  change->action_type = CA_NO_ACTION;
+  change->action_mode = CA_MODE_UNDEFINED;
+  change->action_arg = CA_ARG_UNDEFINED;
+
   for (x = 0; x < 3; x++)
     for (y = 0; y < 3; y++)
       change->target_content[x][y] = EL_EMPTY_SPACE;
@@ -111,9 +134,10 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 static void setLevelInfoToDefaults(struct LevelInfo *level)
 {
   static boolean clipboard_elements_initialized = FALSE;
-
   int i, j, x, y;
 
+  setLevelInfoToDefaults_EM();
+
   level->native_em_level = &native_em_level;
 
   level->game_engine_type = GAME_ENGINE_TYPE_RND;
@@ -153,8 +177,11 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->block_last_field = FALSE;     /* EM does not block by default */
   level->sp_block_last_field = TRUE;   /* SP blocks the last field */
+
+#if 0  /* !!! THIS IS NOT A LEVEL SETTING => LOGIC MOVED TO "game.c" !!! */
   level->block_delay = 8;              /* when blocking, block 8 frames */
   level->sp_block_delay = 9;           /* SP indeed blocks 9 frames, not 8 */
+#endif
 
   level->can_move_into_acid_bits = ~0; /* everything can move into acid */
   level->dont_collide_with_bits = ~0;  /* always deadly when colliding */
@@ -162,6 +189,25 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   level->use_spring_bug = FALSE;
   level->use_step_counter = FALSE;
 
+  /* values for the new EMC elements */
+  level->android_move_time = 10;
+  level->android_clone_time = 10;
+  level->ball_random = FALSE;
+  level->ball_state_initial = FALSE;
+  level->ball_time = 10;
+  level->lenses_score = 10;
+  level->magnify_score = 10;
+  level->slurp_score = 10;
+  level->lenses_time = 10;
+  level->magnify_time = 10;
+  level->wind_direction_initial = MV_NO_MOVING;
+  for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++)
+    for (x = 0; x < 3; x++)
+      for (y = 0; y < 3; y++)
+       level->ball_content[i][x][y] = EL_EMPTY;
+  for (i = 0; i < 16; i++)
+    level->android_array[i] = FALSE;
+
   level->use_custom_template = FALSE;
 
   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
@@ -252,6 +298,9 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
       element_info[element].explosion_delay = 16;
       element_info[element].ignition_delay = 8;
 
+      element_info[element].counter_initial = 0;
+      element_info[element].counter = 0;
+
       for (x = 0; x < 3; x++)
        for (y = 0; y < 3; y++)
          element_info[element].content[x][y] = EL_EMPTY_SPACE;
@@ -391,34 +440,14 @@ static int getFileTypeFromBasename(char *basename)
   return LEVEL_FILE_TYPE_UNKNOWN;
 }
 
-static char *getSingleLevelBasename(int nr, int type)
+static char *getSingleLevelBasename(int nr)
 {
   static char basename[MAX_FILENAME_LEN];
-  char *level_filename = getStringCopy(leveldir_current->level_filename);
-
-  if (level_filename == NULL)
-    level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
-
-  switch (type)
-  {
-    case LEVEL_FILE_TYPE_RND:
-      if (nr < 0)
-       sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
-      else
-       sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
-      break;
-
-    case LEVEL_FILE_TYPE_EM:
-      sprintf(basename, "%d", nr);
-      break;
-
-    case LEVEL_FILE_TYPE_UNKNOWN:
-    default:
-      sprintf(basename, level_filename, nr);
-      break;
-  }
 
-  free(level_filename);
+  if (nr < 0)
+    sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
+  else
+    sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
 
   return basename;
 }
@@ -461,9 +490,9 @@ static char *getPackedLevelBasename(int type)
   return basename;
 }
 
-static char *getSingleLevelFilename(int nr, int type)
+static char *getSingleLevelFilename(int nr)
 {
-  return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
+  return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
 }
 
 #if 0
@@ -475,9 +504,10 @@ static char *getPackedLevelFilename(int type)
 
 char *getDefaultLevelFilename(int nr)
 {
-  return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
+  return getSingleLevelFilename(nr);
 }
 
+#if 0
 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
                                                 int type)
 {
@@ -486,6 +516,23 @@ static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
 }
+#endif
+
+static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
+                                                int type, char *format, ...)
+{
+  static char basename[MAX_FILENAME_LEN];
+  va_list ap;
+
+  va_start(ap, format);
+  vsprintf(basename, format, ap);
+  va_end(ap);
+
+  lfi->type = type;
+  lfi->packed = FALSE;
+  lfi->basename = basename;
+  lfi->filename = getLevelFilenameFromBasename(lfi->basename);
+}
 
 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
                                                 int type)
@@ -496,7 +543,37 @@ static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
 }
 
-static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+static int getFiletypeFromID(char *filetype_id)
+{
+  char *filetype_id_lower;
+  int filetype = LEVEL_FILE_TYPE_UNKNOWN;
+  int i;
+
+  if (filetype_id == NULL)
+    return LEVEL_FILE_TYPE_UNKNOWN;
+
+  filetype_id_lower = getStringToLower(filetype_id);
+
+  for (i = 0; filetype_id_list[i].id != NULL; i++)
+  {
+    char *id_lower = getStringToLower(filetype_id_list[i].id);
+    
+    if (strcmp(filetype_id_lower, id_lower) == 0)
+      filetype = filetype_id_list[i].filetype;
+
+    free(id_lower);
+
+    if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
+      break;
+  }
+
+  free(filetype_id_lower);
+
+  return filetype;
+}
+
+#if 0
+static void OLD_determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
 {
   /* special case: level number is negative => check for level template file */
   if (lfi->nr < 0)
@@ -508,8 +585,11 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
 
   if (leveldir_current->level_filename != NULL)
   {
+    int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
     /* check for file name/pattern specified in "levelinfo.conf" */
-    setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
+    setLevelFileInfo_SingleLevelFilename(lfi, filetype);
+
     if (fileExists(lfi->filename))
       return;
   }
@@ -532,6 +612,79 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
   /* no known level file found -- try to use default values */
   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
 }
+#endif
+
+static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+{
+  int nr = lfi->nr;
+
+  /* special case: level number is negative => check for level template file */
+  if (nr < 0)
+  {
+    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                        "template.%s", LEVELFILE_EXTENSION);
+
+    /* no fallback if template file not existing */
+    return;
+  }
+
+  /* special case: check for file name/pattern specified in "levelinfo.conf" */
+  if (leveldir_current->level_filename != NULL)
+  {
+    int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
+    setLevelFileInfo_FormatLevelFilename(lfi, filetype,
+                                        leveldir_current->level_filename, nr);
+    if (fileExists(lfi->filename))
+      return;
+  }
+
+  /* check for native Rocks'n'Diamonds level file */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                      "%03d.%s", nr, LEVELFILE_EXTENSION);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V1) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
+                                      'a' + (nr / 10) % 26, '0' + nr % 10);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
+                                      'A' + (nr / 10) % 26, '0' + nr % 10);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V2 to V5) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V6 / single mode) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V6 / teamwork mode) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for various packed level file formats */
+  setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* no known level file found -- use default values (and fail later) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                      "%03d.%s", nr, LEVELFILE_EXTENSION);
+}
 
 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
 {
@@ -946,6 +1099,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i = 0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
+    unsigned long event_bits;
 
     if (!IS_CUSTOM_ELEMENT(element))
     {
@@ -984,7 +1138,10 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
        element_info[element].content[x][y] =
          getMappedElement(getFile16BitBE(file));
 
-    element_info[element].change->events = getFile32BitBE(file);
+    event_bits = getFile32BitBE(file);
+    for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+      if (event_bits & (1 << j))
+       element_info[element].change->has_event[j] = TRUE;
 
     element_info[element].change->target_element =
       getMappedElement(getFile16BitBE(file));
@@ -1026,7 +1183,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   struct ElementInfo *ei;
   int chunk_size_expected;
   int element;
-  int i, x, y;
+  int i, j, x, y;
 
   element = getFile16BitBE(file);
 
@@ -1108,11 +1265,15 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i = 0; i < ei->num_change_pages; i++)
   {
     struct ElementChangeInfo *change = &ei->change_page[i];
+    unsigned long event_bits;
 
     /* always start with reliable default values */
     setElementChangeInfoToDefaults(change);
 
-    change->events = getFile32BitBE(file);
+    event_bits = getFile32BitBE(file);
+    for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+      if (event_bits & (1 << j))
+       change->has_event[j] = TRUE;
 
     change->target_element = getMappedElement(getFile16BitBE(file));
 
@@ -1145,8 +1306,18 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
                            CH_PAGE_ANY : (1 << change->trigger_page));
 
+#if 1
+    change->has_action = getFile8Bit(file);
+    change->action_type = getFile8Bit(file);
+    change->action_mode = getFile8Bit(file);
+    change->action_arg = getFile16BitBE(file);
+
+    /* some free bytes for future change property values and padding */
+    ReadUnusedBytesFromFile(file, 1);
+#else
     /* some free bytes for future change property values and padding */
     ReadUnusedBytesFromFile(file, 6);
+#endif
 
 #else
 
@@ -1769,16 +1940,27 @@ static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
 
 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
 {
+  static int ball_xy[8][2] =
+  {
+    { 0, 0 },
+    { 1, 0 },
+    { 2, 0 },
+    { 0, 1 },
+    { 2, 1 },
+    { 0, 2 },
+    { 1, 2 },
+    { 2, 2 },
+  };
   struct LevelInfo_EM *level_em = level->native_em_level;
   struct LEVEL *lev = level_em->lev;
   struct PLAYER *ply1 = level_em->ply1;
   struct PLAYER *ply2 = level_em->ply2;
-  int i, x, y;
+  int i, j, x, y;
 
   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
 
-  lev->time_initial = level->time;
+  lev->time_seconds     = level->time;
   lev->required_initial = level->gems_needed;
 
   lev->emerald_score   = level->score[SC_EMERALD];
@@ -1789,7 +1971,8 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   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_TIME_BONUS];  /* ??? CHECK THIS */
+  lev->key_score       = level->score[SC_KEY];
+  lev->exit_score      = level->score[SC_TIME_BONUS];
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
@@ -1801,6 +1984,29 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   lev->wonderwall_time_initial = level->time_magic_wall;
   lev->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->lenses_score            = level->lenses_score;
+  lev->magnify_score           = level->magnify_score;
+  lev->slurp_score             = level->slurp_score;
+
+  lev->lenses_time             = level->lenses_time;
+  lev->magnify_time            = level->magnify_time;
+  lev->wind_direction_initial  = level->wind_direction_initial;
+
+  for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      lev->ball_array[i][j] =
+       map_element_RND_to_EM(level->
+                             ball_content[i][ball_xy[j][0]][ball_xy[j][1]]);
+
+  for (i = 0; i < 16; i++)
+    lev->android_array[i] = FALSE;     /* !!! YET TO COME !!! */
+
   /* first fill the complete playfield with the default border element */
   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
@@ -1811,6 +2017,9 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   {
     int new_element = map_element_RND_to_EM(level->field[x][y]);
 
+    if (level->field[x][y] == EL_AMOEBA_DEAD)
+      new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
+
     level_em->cave[x + 1][y + 1] = new_element;
   }
 
@@ -1820,34 +2029,47 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   ply2->x_initial = 0;
   ply2->y_initial = 0;
 
-  /* at last, set the two players to their positions in the playfield */
+  /* initialize player positions and delete players from the playfield */
   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
   {
     if (level->field[x][y] == EL_PLAYER_1)
     {
       ply1->x_initial = x + 1;
       ply1->y_initial = y + 1;
+      level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
     }
     else if (level->field[x][y] == EL_PLAYER_2)
     {
       ply2->x_initial = x + 1;
       ply2->y_initial = y + 1;
+      level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
     }
   }
 }
 
 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
 {
+  static int ball_xy[8][2] =
+  {
+    { 0, 0 },
+    { 1, 0 },
+    { 2, 0 },
+    { 0, 1 },
+    { 2, 1 },
+    { 0, 2 },
+    { 1, 2 },
+    { 2, 2 },
+  };
   struct LevelInfo_EM *level_em = level->native_em_level;
   struct LEVEL *lev = level_em->lev;
   struct PLAYER *ply1 = level_em->ply1;
   struct PLAYER *ply2 = level_em->ply2;
-  int i, x, y;
+  int i, j, x, y;
 
   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
 
-  level->time = lev->time_initial;
+  level->time        = lev->time_seconds;
   level->gems_needed = lev->required_initial;
 
   sprintf(level->name, "Level %d", level->file_info.nr);
@@ -1860,7 +2082,8 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   level->score[SC_YAMYAM]      = lev->eater_score;
   level->score[SC_NUT]         = lev->nut_score;
   level->score[SC_DYNAMITE]    = lev->dynamite_score;
-  level->score[SC_TIME_BONUS]  = lev->key_score;       /* ??? CHECK THIS */
+  level->score[SC_KEY]         = lev->key_score;
+  level->score[SC_TIME_BONUS]  = lev->exit_score;
 
   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
 
@@ -1874,6 +2097,29 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   level->time_magic_wall       = lev->wonderwall_time_initial;
   level->time_wheel            = lev->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->lenses_score          = lev->lenses_score;
+  level->magnify_score         = lev->magnify_score;
+  level->slurp_score           = lev->slurp_score;
+
+  level->lenses_time           = lev->lenses_time;
+  level->magnify_time          = lev->magnify_time;
+  level->wind_direction_initial        = lev->wind_direction_initial;
+
+  for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      level->ball_content[i][ball_xy[j][0]][ball_xy[j][1]] =
+       map_element_EM_to_RND(lev->ball_array[i][j]);
+
+  for (i = 0; i < 16; i++)
+    level->android_array[i] = FALSE;   /* !!! YET TO COME !!! */
+
+  /* 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]);
@@ -1884,8 +2130,13 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
     level->field[x][y] = new_element;
   }
 
-  level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
+  /* in case of both players set to the same field, use the first player */
   level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
+  level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
+
+#if 0
+  printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
+#endif
 }
 
 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
@@ -2280,10 +2531,17 @@ void LoadLevelFromFileInfo(struct LevelInfo *level,
       break;
   }
 
+  /* if level file is invalid, restore level structure to default values */
+  if (level->no_valid_file)
+    setLevelInfoToDefaults(level);
+
   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
     level->game_engine_type = GAME_ENGINE_TYPE_RND;
 
-  CopyNativeLevel_Native_to_RND(level);
+  if (level_file_info->type == LEVEL_FILE_TYPE_RND)
+    CopyNativeLevel_RND_to_Native(level);
+  else
+    CopyNativeLevel_Native_to_RND(level);
 }
 
 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
@@ -2361,10 +2619,13 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       level->use_spring_bug = TRUE;
 
     /* only few elements were able to actively move into acid before 3.1.0 */
+    /* trigger settings did not exist before 3.1.0; set to default "any" */
     if (level->game_version < VERSION_IDENT(3,1,0,0))
     {
       int i, j;
 
+      /* correct "can move into acid" settings (all zero in old levels) */
+
       level->can_move_into_acid_bits = 0; /* nothing can move into acid */
       level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
 
@@ -2376,6 +2637,8 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
        SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
 
+      /* correct trigger settings (stored as zero == "none" in old levels) */
+
       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
       {
        int element = EL_CUSTOM_START + i;
@@ -2391,6 +2654,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       }
     }
 
+#if 0  /* !!! MOVED TO "game.c", BECAUSE CAN CHANGE INSIDE LEVEL EDITOR !!! */
 #if 1  /* USE_NEW_BLOCK_STYLE */
     /* blocking the last field when moving was corrected in version 3.1.1 */
     if (level->game_version < VERSION_IDENT(3,1,1,0))
@@ -2407,6 +2671,8 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       level->sp_block_last_field = TRUE;
     }
 #endif
+#endif
+
   }
   else         /* always use the latest game engine version */
   {
@@ -2455,7 +2721,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   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) */
+     (these following values were accidentally changed in version 3.0.1)
+     (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
   if (level->game_version <= VERSION_IDENT(3,0,0,0))
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2463,7 +2730,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
       int element = EL_CUSTOM_START + i;
 
       /* order of checking and copying events to be mapped is important */
-      for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
+      for (j = CE_BY_OTHER_ACTION; j >= CE_COUNT_AT_ZERO; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 2))
        {
@@ -2473,7 +2740,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
       }
 
       /* order of checking and copying events to be mapped is important */
-      for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
+      for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 1))
        {
@@ -2484,20 +2751,30 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
+#if 0
+  /* !!! TESTS SHOWED THAT THIS CODE SEGMENT IS NOT NEEDED FOR ANY LEVEL !!! */
+
   /* some custom element change events get mapped since version 3.0.3 */
-  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+#if 1
+  if (level->game_version >= VERSION_IDENT(3,0,3,0) &&
+      level->game_version <= VERSION_IDENT(3,2,0,3))
+#endif
   {
-    int element = EL_CUSTOM_START + i;
-
-    if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
-       HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
-      SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
-      SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
+      int element = EL_CUSTOM_START + i;
 
-      SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
+      if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
+         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
+      {
+       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
+       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
+
+       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
+      }
     }
   }
+#endif
 
   /* initialize "can_change" field for old levels with only one change page */
   if (level->game_version <= VERSION_IDENT(3,0,2,0))
@@ -2512,20 +2789,23 @@ 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++)
+  if (level->game_version < VERSION_IDENT(3,1,1,0))
   {
-    int element = EL_CUSTOM_START + i;
-    struct ElementInfo *ei = &element_info[element];
+    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;
+      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];
+      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 (change->trigger_side == CH_SIDE_NONE)
+         change->trigger_side = CH_SIDE_ANY;
+      }
     }
   }
 
@@ -3030,7 +3310,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 {
   struct ElementInfo *ei = &element_info[element];
-  int i, x, y;
+  int i, j, x, y;
 
   putFile16BitBE(file, element);
 
@@ -3092,8 +3372,13 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
   for (i = 0; i < ei->num_change_pages; i++)
   {
     struct ElementChangeInfo *change = &ei->change_page[i];
+    unsigned long event_bits = 0;
+
+    for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+      if (change->has_event[j])
+       event_bits |= (1 << j);
 
-    putFile32BitBE(file, change->events);
+    putFile32BitBE(file, event_bits);
 
     putFile16BitBE(file, change->target_element);
 
@@ -3124,8 +3409,19 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
                       log_2(change->trigger_page)));
 
+#if 1
+
+    putFile8Bit(file, change->has_action);
+    putFile8Bit(file, change->action_type);
+    putFile8Bit(file, change->action_mode);
+    putFile16BitBE(file, change->action_arg);
+
+    /* some free bytes for future change property values and padding */
+    WriteUnusedBytesToFile(file, 1);
+#else
     /* some free bytes for future change property values and padding */
     WriteUnusedBytesToFile(file, 6);
+#endif
 
 #else
 
@@ -3442,7 +3738,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    if (i >= MAX_TAPELEN)
+    if (i >= MAX_TAPE_LEN)
       break;
 
     for (j = 0; j < MAX_PLAYERS; j++)
@@ -3731,7 +4027,7 @@ void SaveTape(int nr)
   InitTapeDirectory(leveldir_current->subdir);
 
   /* if a tape still exists, ask to overwrite it */
-  if (access(filename, F_OK) == 0)
+  if (fileExists(filename))
   {
     new_tape = FALSE;
     if (!Request("Replace old tape ?", REQ_ASK))
@@ -3777,7 +4073,7 @@ void SaveTape(int nr)
   tape.changed = FALSE;
 
   if (new_tape)
-    Request("tape saved !", REQ_CONFIRM);
+    Request("Tape saved !", REQ_CONFIRM);
 }
 
 void DumpTape(struct TapeInfo *tape)
@@ -3810,7 +4106,7 @@ void DumpTape(struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    if (i >= MAX_TAPELEN)
+    if (i >= MAX_TAPE_LEN)
       break;
 
     printf("%03d: ", i);
@@ -3941,33 +4237,35 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_QUICK_DOORS                        10
 #define SETUP_TOKEN_TEAM_MODE                  11
 #define SETUP_TOKEN_HANDICAP                   12
-#define SETUP_TOKEN_TIME_LIMIT                 13
-#define SETUP_TOKEN_FULLSCREEN                 14
-#define SETUP_TOKEN_ASK_ON_ESCAPE              15
-#define SETUP_TOKEN_GRAPHICS_SET               16
-#define SETUP_TOKEN_SOUNDS_SET                 17
-#define SETUP_TOKEN_MUSIC_SET                  18
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    19
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      20
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       21
-
-#define NUM_GLOBAL_SETUP_TOKENS                        22
+#define SETUP_TOKEN_SKIP_LEVELS                        13
+#define SETUP_TOKEN_TIME_LIMIT                 14
+#define SETUP_TOKEN_FULLSCREEN                 15
+#define SETUP_TOKEN_ASK_ON_ESCAPE              16
+#define SETUP_TOKEN_GRAPHICS_SET               17
+#define SETUP_TOKEN_SOUNDS_SET                 18
+#define SETUP_TOKEN_MUSIC_SET                  19
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    20
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      21
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       22
+
+#define NUM_GLOBAL_SETUP_TOKENS                        23
 
 /* editor setup */
 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE     1
-#define SETUP_TOKEN_EDITOR_EL_MORE             2
-#define SETUP_TOKEN_EDITOR_EL_SOKOBAN          3
-#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX         4
-#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES    5
-#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   6
-#define SETUP_TOKEN_EDITOR_EL_CHARS            7
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM           8
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE      9
-#define SETUP_TOKEN_EDITOR_EL_HEADLINES                10
-#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     11
-
-#define NUM_EDITOR_SETUP_TOKENS                        12
+#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB        2
+#define SETUP_TOKEN_EDITOR_EL_MORE             3
+#define SETUP_TOKEN_EDITOR_EL_SOKOBAN          4
+#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX         5
+#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES    6
+#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   7
+#define SETUP_TOKEN_EDITOR_EL_CHARS            8
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM           9
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE      10
+#define SETUP_TOKEN_EDITOR_EL_HEADLINES                11
+#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     12
+
+#define NUM_EDITOR_SETUP_TOKENS                        13
 
 /* shortcut setup */
 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
@@ -4030,6 +4328,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.quick_doors,      "quick_doors"                   },
   { TYPE_SWITCH, &si.team_mode,                "team_mode"                     },
   { TYPE_SWITCH, &si.handicap,         "handicap"                      },
+  { TYPE_SWITCH, &si.skip_levels,      "skip_levels"                   },
   { TYPE_SWITCH, &si.time_limit,       "time_limit"                    },
   { TYPE_SWITCH, &si.fullscreen,       "fullscreen"                    },
   { TYPE_SWITCH, &si.ask_on_escape,    "ask_on_escape"                 },
@@ -4045,6 +4344,7 @@ static struct TokenInfo editor_setup_tokens[] =
 {
   { TYPE_SWITCH, &sei.el_boulderdash,  "editor.el_boulderdash"         },
   { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine"        },
+  { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
   { TYPE_SWITCH, &sei.el_more,         "editor.el_more"                },
   { TYPE_SWITCH, &sei.el_sokoban,      "editor.el_sokoban"             },
   { TYPE_SWITCH, &sei.el_supaplex,     "editor.el_supaplex"            },
@@ -4130,6 +4430,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->quick_doors = FALSE;
   si->team_mode = FALSE;
   si->handicap = TRUE;
+  si->skip_levels = TRUE;
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
   si->ask_on_escape = TRUE;
@@ -4141,16 +4442,17 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->override_level_sounds = FALSE;
   si->override_level_music = FALSE;
 
-  si->editor.el_boulderdash = TRUE;
-  si->editor.el_emerald_mine = TRUE;
-  si->editor.el_more = TRUE;
-  si->editor.el_sokoban = TRUE;
-  si->editor.el_supaplex = TRUE;
-  si->editor.el_diamond_caves = TRUE;
-  si->editor.el_dx_boulderdash = TRUE;
-  si->editor.el_chars = TRUE;
-  si->editor.el_custom = TRUE;
-  si->editor.el_custom_more = FALSE;
+  si->editor.el_boulderdash       = TRUE;
+  si->editor.el_emerald_mine      = TRUE;
+  si->editor.el_emerald_mine_club = TRUE;
+  si->editor.el_more              = TRUE;
+  si->editor.el_sokoban           = TRUE;
+  si->editor.el_supaplex          = TRUE;
+  si->editor.el_diamond_caves     = TRUE;
+  si->editor.el_dx_boulderdash    = TRUE;
+  si->editor.el_chars             = TRUE;
+  si->editor.el_custom            = TRUE;
+  si->editor.el_custom_more       = FALSE;
 
   si->editor.el_headlines = TRUE;
   si->editor.el_user_defined = FALSE;
@@ -4457,7 +4759,19 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
   {
     char *value = getHashEntry(element_hash, list->token);
 
-    if (value)
+    if (value == NULL)         /* try to find obsolete token mapping */
+    {
+      char *mapped_token = get_mapped_token(list->token);
+
+      if (mapped_token != NULL)
+      {
+       value = getHashEntry(element_hash, mapped_token);
+
+       free(mapped_token);
+      }
+    }
+
+    if (value != NULL)
     {
       (*elements)[(*num_elements)++] = atoi(value);
     }