rnd-20040103-1-src
[rocksndiamonds.git] / src / files.c
index a2f7cf69faf45be632ac96a07d0e6b4c20832ca9..ae43afc879aae27806d27a55d4a4b34bc530df21 100644 (file)
 #define TAPE_COOKIE_TMPL       "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
 #define SCORE_COOKIE           "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
 
+/* values for level file type identifier */
+#define LEVEL_FILE_TYPE_UNKNOWN                0
+#define LEVEL_FILE_TYPE_RND            1
+#define LEVEL_FILE_TYPE_EM             2
+
+#define LEVEL_FILE_TYPE_RND_PACKED     (10 + LEVEL_FILE_TYPE_RND)
+#define LEVEL_FILE_TYPE_EM_PACKED      (10 + LEVEL_FILE_TYPE_EM)
+
+#define IS_SINGLE_LEVEL_FILE(x)                (x < 10)
+#define IS_PACKED_LEVEL_FILE(x)                (x > 10)
+
 
 /* ========================================================================= */
 /* level file functions                                                      */
@@ -91,8 +102,8 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->random = 100;
   change->power = CP_NON_DESTRUCTIVE;
 
-  for(x=0; x<3; x++)
-    for(y=0; y<3; y++)
+  for (x = 0; x < 3; x++)
+    for (y = 0; y < 3; y++)
       change->content[x][y] = EL_EMPTY_SPACE;
 
   change->direct_action = 0;
@@ -117,8 +128,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   level->fieldx = STD_LEV_FIELDX;
   level->fieldy = STD_LEV_FIELDY;
 
-  for(x=0; x<MAX_LEV_FIELDX; x++)
-    for(y=0; y<MAX_LEV_FIELDY; y++)
+  for (x = 0; x < MAX_LEV_FIELDX; x++)
+    for (y = 0; y < MAX_LEV_FIELDY; y++)
       level->field[x][y] = EL_SAND;
 
   level->time = 100;
@@ -135,45 +146,45 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->use_custom_template = FALSE;
 
-  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
     level->name[i] = '\0';
-  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
     level->author[i] = '\0';
 
   strcpy(level->name, NAMELESS_LEVEL_NAME);
   strcpy(level->author, ANONYMOUS_NAME);
 
-  for (i=0; i<4; i++)
+  for (i = 0; i < 4; i++)
   {
     level->envelope_text[i][0] = '\0';
     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
   }
 
-  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = 10;
 
   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
-  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-    for(x=0; x<3; x++)
-      for(y=0; y<3; y++)
+  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] =
          (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
 
   level->field[0][0] = EL_PLAYER_1;
   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
 
-  for (i=0; i < MAX_NUM_ELEMENTS; i++)
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     setElementChangePages(&element_info[i], 1);
     setElementChangeInfoToDefaults(element_info[i].change);
   }
 
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
-    for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
+    for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
       element_info[element].description[j] = '\0';
     if (element_info[element].custom_description != NULL)
       strncpy(element_info[element].description,
@@ -199,8 +210,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
     element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
 
-    for(x=0; x<3; x++)
-      for(y=0; y<3; y++)
+    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;
@@ -217,7 +228,7 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
     element_info[element].current_change_page = 0;
 
     /* start with no properties at all */
-    for (j=0; j < NUM_EP_BITFIELDS; j++)
+    for (j = 0; j < NUM_EP_BITFIELDS; j++)
       Properties[element][j] = EP_BITMASK_DEFAULT;
 
     element_info[element].modified_settings = FALSE;
@@ -268,13 +279,109 @@ static void ActivateLevelTemplate()
      level data, while all other variables do not change. */
 }
 
-boolean LevelFileExists(int level_nr)
+static char *getLevelFilenameFromBasename(char *basename)
+{
+  static char *filename = NULL;
+
+  checked_free(filename);
+
+  filename = getPath2(getCurrentLevelDir(), basename);
+
+  return filename;
+}
+
+static char *getSingleLevelBasename(int nr, int type)
+{
+  static char basename[MAX_FILENAME_LEN];
+
+  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;
+
+    default:
+      strcpy(basename, UNDEFINED_FILENAME);
+      break;
+  }
+
+  return basename;
+}
+
+static char *getPackedLevelBasename(int type)
+{
+  static char basename[MAX_FILENAME_LEN];
+
+  switch (type)
+  {
+    default:
+      strcpy(basename, UNDEFINED_FILENAME);
+      break;
+  }
+
+  return basename;
+}
+
+static char *getSingleLevelFilename(int nr, int type)
 {
-  char *filename = getLevelFilename(level_nr);
+  return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
+}
+
+static char *getPackedLevelFilename(int type)
+{
+  return getLevelFilenameFromBasename(getPackedLevelBasename(type));
+}
+
+char *getDefaultLevelFilename(int nr)
+{
+  return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
+}
+
+static struct LevelFileInfo *getLevelFileInfo(int nr)
+{
+  static struct LevelFileInfo level_file_info;
+
+  level_file_info.nr = nr;
+
+  /* special case: level template */
+  if (nr < 0)
+  {
+    level_file_info.type = LEVEL_FILE_TYPE_RND;
+    level_file_info.filename = getDefaultLevelFilename(nr);
+
+    return &level_file_info;
+  }
+
+  /* 1st try: check for native Rocks'n'Diamonds level file */
+  level_file_info.type = LEVEL_FILE_TYPE_RND;
+  level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
+  if (fileExists(level_file_info.filename))
+    return &level_file_info;
+
+  /* 2nd try: check for classic Emerald Mine level file */
+  level_file_info.type = LEVEL_FILE_TYPE_EM;
+  level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
+  if (fileExists(level_file_info.filename))
+    return &level_file_info;
 
-  return (access(filename, F_OK) == 0);
+  /* no known level file found -- use default values */
+  level_file_info.type = LEVEL_FILE_TYPE_RND;
+  level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
+
+  return &level_file_info;
 }
 
+/* ------------------------------------------------------------------------- */
+/* functions for loading R'n'D level                                         */
+/* ------------------------------------------------------------------------- */
+
 static int checkLevelElement(int element)
 {
   /* map some (historic, now obsolete) elements */
@@ -356,17 +463,17 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   level->time          = getFile16BitBE(file);
   level->gems_needed   = getFile16BitBE(file);
 
-  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
     level->name[i] = getFile8Bit(file);
   level->name[MAX_LEVEL_NAME_LEN] = 0;
 
-  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = getFile8Bit(file);
 
   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
-  for(i=0; i<STD_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
 
   level->amoeba_speed          = getFile8Bit(file);
@@ -389,7 +496,7 @@ static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
 {
   int i;
 
-  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
     level->author[i] = getFile8Bit(file);
   level->author[MAX_LEVEL_NAME_LEN] = 0;
 
@@ -415,8 +522,8 @@ static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
     return chunk_size_expected;
   }
 
-  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++)
       level->field[x][y] =
        checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
                          getFile8Bit(file));
@@ -454,9 +561,9 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
 
-  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        level->yamyam_content[i][x][y] =
          checkLevelElement(level->encoding_16bit_field ?
                            getFile16BitBE(file) : getFile8Bit(file));
@@ -477,9 +584,9 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
 
   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
 
-  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
 
   /* correct invalid number of content fields -- should never happen */
@@ -490,9 +597,9 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   {
     level->num_yamyam_contents = num_contents;
 
-    for(i=0; i<num_contents; i++)
-      for(y=0; y<3; y++)
-       for(x=0; x<3; x++)
+    for (i = 0; i < num_contents; i++)
+      for (y = 0; y < 3; y++)
+       for (x = 0; x < 3; x++)
          level->yamyam_content[i][x][y] = content_array[i][x][y];
   }
   else if (element == EL_BD_AMOEBA)
@@ -536,7 +643,7 @@ static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
     return chunk_size_expected;
   }
 
-  for(i=0; i < envelope_len; i++)
+  for (i = 0; i < envelope_len; i++)
     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
 
   return chunk_size;
@@ -554,7 +661,7 @@ static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
     return chunk_size_expected;
   }
 
-  for (i=0; i < num_changed_custom_elements; i++)
+  for (i = 0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
     int properties = getFile32BitBE(file);
@@ -580,7 +687,7 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
     return chunk_size_expected;
   }
 
-  for (i=0; i < num_changed_custom_elements; i++)
+  for (i = 0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
     int custom_target_element = getFile16BitBE(file);
@@ -606,7 +713,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
     return chunk_size_expected;
   }
 
-  for (i=0; i < num_changed_custom_elements; i++)
+  for (i = 0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
 
@@ -617,7 +724,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
       element = EL_DUMMY;
     }
 
-    for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
+    for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
       element_info[element].description[j] = getFile8Bit(file);
     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
 
@@ -642,8 +749,8 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
     element_info[element].move_direction_initial = getFile8Bit(file);
     element_info[element].move_stepsize = getFile8Bit(file);
 
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        element_info[element].content[x][y] =
          checkLevelElement(getFile16BitBE(file));
 
@@ -667,8 +774,8 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
     element_info[element].change->random = getFile8Bit(file);
     element_info[element].change->power = getFile8Bit(file);
 
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        element_info[element].change->content[x][y] =
          checkLevelElement(getFile16BitBE(file));
 
@@ -702,7 +809,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
   ei = &element_info[element];
 
-  for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
     ei->description[i] = getFile8Bit(file);
   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
 
@@ -740,8 +847,8 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
   ei->slippery_type = getFile8Bit(file);
 
-  for(y=0; y<3; y++)
-    for(x=0; x<3; x++)
+  for (y = 0; y < 3; y++)
+    for (x = 0; x < 3; x++)
       ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
 
   /* some free bytes for future custom property values and padding */
@@ -751,7 +858,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
   setElementChangePages(ei, ei->num_change_pages);
 
-  for (i=0; i < ei->num_change_pages; i++)
+  for (i = 0; i < ei->num_change_pages; i++)
   {
     struct ElementChangeInfo *change = &ei->change_page[i];
 
@@ -776,8 +883,8 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
     change->random = getFile8Bit(file);
     change->power = getFile8Bit(file);
 
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        change->content[x][y] = checkLevelElement(getFile16BitBE(file));
 
     change->can_change = getFile8Bit(file);
@@ -797,8 +904,10 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
+static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
+                                     struct LevelFileInfo *level_file_info)
 {
+  char *filename = level_file_info->filename;
   char cookie[MAX_LINE_LEN];
   char chunk_name[CHUNK_ID_LEN + 1];
   int chunk_size;
@@ -812,7 +921,7 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
     level->no_level_file = TRUE;
 
     if (level != &level_template)
-      Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
+      Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
 
     return;
   }
@@ -927,6 +1036,338 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
   fclose(file);
 }
 
+/* ------------------------------------------------------------------------- */
+/* functions for loading EM level                                            */
+/* ------------------------------------------------------------------------- */
+
+static int map_em_element_yam(int element)
+{
+  switch (element)
+  {
+    case 0x00: return EL_EMPTY;
+    case 0x01: return EL_EMERALD;
+    case 0x02: return EL_DIAMOND;
+    case 0x03: return EL_ROCK;
+    case 0x04: return EL_ROBOT;
+    case 0x05: return EL_SPACESHIP_UP;
+    case 0x06: return EL_BOMB;
+    case 0x07: return EL_BUG_UP;
+    case 0x08: return EL_AMOEBA_DROP;
+    case 0x09: return EL_NUT;
+    case 0x0a: return EL_YAMYAM;
+    case 0x0b: return EL_QUICKSAND_FULL;
+    case 0x0c: return EL_SAND;
+    case 0x0d: return EL_WALL_SLIPPERY;
+    case 0x0e: return EL_STEELWALL;
+    case 0x0f: return EL_WALL;
+    case 0x10: return EL_EM_KEY_1;
+    case 0x11: return EL_EM_KEY_2;
+    case 0x12: return EL_EM_KEY_4;
+    case 0x13: return EL_EM_KEY_3;
+    case 0x14: return EL_MAGIC_WALL;
+    case 0x15: return EL_ROBOT_WHEEL;
+    case 0x16: return EL_DYNAMITE;
+
+    case 0x17: return EL_EM_KEY_1;                     /* EMC */
+    case 0x18: return EL_BUG_UP;                       /* EMC */
+    case 0x1a: return EL_DIAMOND;                      /* EMC */
+    case 0x1b: return EL_EMERALD;                      /* EMC */
+    case 0x25: return EL_NUT;                          /* EMC */
+    case 0x80: return EL_EMPTY;                        /* EMC */
+    case 0x85: return EL_EM_KEY_1;                     /* EMC */
+    case 0x86: return EL_EM_KEY_2;                     /* EMC */
+    case 0x87: return EL_EM_KEY_4;                     /* EMC */
+    case 0x88: return EL_EM_KEY_3;                     /* EMC */
+    case 0x94: return EL_QUICKSAND_EMPTY;              /* EMC */
+    case 0x9a: return EL_AMOEBA_WET;                   /* EMC */
+    case 0xaf: return EL_DYNAMITE;                     /* EMC */
+    case 0xbd: return EL_SAND;                         /* EMC */
+
+    default:
+      Error(ERR_WARN, "invalid level element %d", element);
+      return EL_CHAR_QUESTION;
+  }
+}
+
+static int map_em_element_field(int element)
+{
+  if (element >= 0xc8 && element <= 0xe1)
+    return EL_CHAR_A + (element - 0xc8);
+  else if (element >= 0xe2 && element <= 0xeb)
+    return EL_CHAR_0 + (element - 0xe2);
+
+  switch (element)
+  {
+    case 0x00: return EL_ROCK;
+    case 0x02: return EL_DIAMOND;
+    case 0x03: return EL_DIAMOND;
+    case 0x04: return EL_ROBOT;
+    case 0x05: return EL_ROBOT;                        /* EMC */
+    case 0x08: return EL_SPACESHIP_UP;
+    case 0x09: return EL_SPACESHIP_RIGHT;
+    case 0x0a: return EL_SPACESHIP_DOWN;
+    case 0x0b: return EL_SPACESHIP_LEFT;
+    case 0x0c: return EL_SPACESHIP_UP;
+    case 0x0d: return EL_SPACESHIP_RIGHT;
+    case 0x0e: return EL_SPACESHIP_DOWN;
+    case 0x0f: return EL_SPACESHIP_LEFT;
+    case 0x10: return EL_BOMB;
+    case 0x12: return EL_EMERALD;
+    case 0x13: return EL_EMERALD;
+    case 0x14: return EL_BUG_UP;
+    case 0x15: return EL_BUG_RIGHT;
+    case 0x16: return EL_BUG_DOWN;
+    case 0x17: return EL_BUG_LEFT;
+    case 0x18: return EL_BUG_UP;
+    case 0x19: return EL_BUG_RIGHT;
+    case 0x1a: return EL_BUG_DOWN;
+    case 0x1b: return EL_BUG_LEFT;
+    case 0x1c: return EL_AMOEBA_DROP;
+    case 0x20: return EL_ROCK;
+    case 0x24: return EL_MAGIC_WALL;
+    case 0x25: return EL_NUT;
+
+      /* 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 0x2d: return EL_QUICKSAND_FULL;
+    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 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 0x74: return EL_STEELWALL;
+    case 0x7b: return EL_ACID;
+    case 0x80: return EL_EMPTY;
+    case 0x81: return EL_WALL_SLIPPERY;
+    case 0x82: return EL_SAND;
+    case 0x83: return EL_STEELWALL;
+    case 0x84: return EL_WALL;
+    case 0x85: return EL_EM_KEY_1;
+    case 0x86: return EL_EM_KEY_2;
+    case 0x87: return EL_EM_KEY_4;
+    case 0x88: return EL_EM_KEY_3;
+    case 0x89: return EL_EM_GATE_1;
+    case 0x8a: return EL_EM_GATE_2;
+    case 0x8b: return EL_EM_GATE_4;
+    case 0x8c: return EL_EM_GATE_3;
+    case 0x8d: return EL_INVISIBLE_WALL;               /* EMC */
+    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 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 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 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 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 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;
+
+    default:
+      Error(ERR_WARN, "invalid level element %d", element);
+      return EL_CHAR_QUESTION;
+  }
+}
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info)
+{
+  char *filename = level_file_info->filename;
+  FILE *file;
+  unsigned char body[40][64];
+  unsigned char *leveldata = &body[0][0];
+  unsigned char *header = &leveldata[2048];
+  unsigned char code0 = 0x65;
+  unsigned char code1 = 0x11;
+  boolean level_is_crypted = FALSE;
+  int nr = level_file_info->nr;
+  int jx, jy;
+  int i, x, y;
+
+  /* always start with reliable default values */
+  setLevelInfoToDefaults(level);
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    level->no_level_file = TRUE;
+
+    Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
+
+    return;
+  }
+
+  for(i = 0; i < 2106; i++)
+    leveldata[i] = fgetc(file);
+
+  fclose(file);
+
+  /* check if level data is crypted by testing against known starting bytes
+     of the few existing crypted level files (from Emerald Mine 1 + 2) */
+
+  if ((leveldata[0] == 0xf1 ||
+       leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
+  {
+    level_is_crypted = TRUE;
+
+    if (leveldata[0] == 0xf5)  /* error in crypted Emerald Mine 2 levels */
+      leveldata[0] = 0xf1;
+  }
+
+  if (level_is_crypted)                /* decode crypted level data */
+  {
+    for(i = 0; i < 2106; i++)
+    {
+      leveldata[i] ^= code0;
+      leveldata[i] -= code1;
+
+      code0  = (code0 + 7) & 0xff;
+    }
+  }
+
+  level->fieldx        = 64;
+  level->fieldy        = 32;
+
+  level->time          = header[46] * 10;
+  level->gems_needed   = header[47];
+
+  /* The original Emerald Mine levels have their level number stored
+     at the second byte of the level file...
+     Do not trust this information at other level files, e.g. EMC,
+     but correct it anyway (normally the first row is completely
+     steel wall, so the correction does not hurt anyway). */
+
+  if (leveldata[1] == nr)
+    leveldata[1] = leveldata[2];       /* correct level number field */
+
+  sprintf(level->name, "Level %d", nr);
+
+  level->score[SC_EMERALD]     = header[36];
+  level->score[SC_DIAMOND]     = header[37];
+  level->score[SC_ROBOT]       = header[38];
+  level->score[SC_SPACESHIP]   = header[39];
+  level->score[SC_BUG]         = header[40];
+  level->score[SC_YAMYAM]      = header[41];
+  level->score[SC_NUT]         = header[42];
+  level->score[SC_DYNAMITE]    = header[43];
+  level->score[SC_TIME_BONUS]  = header[44];
+
+  level->num_yamyam_contents = 4;
+
+  for(i = 0; i < level->num_yamyam_contents; i++)
+    for(y = 0; y < 3; y++)
+      for(x = 0; x < 3; x++)
+       level->yamyam_content[i][x][y] =
+         map_em_element_yam(header[i * 9 + y * 3 + x]);
+
+  level->amoeba_speed          = (header[52] * 256 + header[53]) % 256;
+  level->time_magic_wall       = (header[54] * 256 + header[55]) * 16 / 100;
+  level->time_wheel            = (header[56] * 256 + header[57]) * 16 / 100;
+  level->amoeba_content                = EL_DIAMOND;
+
+  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
+  {
+    int new_element = map_em_element_field(body[y][x]);
+
+    if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
+      new_element = EL_AMOEBA_WET;
+
+    level->field[x][y] = new_element;
+  }
+
+  jx = (header[48] * 256 + header[49]) % 64;
+  jy = (header[48] * 256 + header[49]) / 64;
+  level->field[jx][jy] = EL_PLAYER_1;
+
+  jx = (header[50] * 256 + header[51]) % 64;
+  jy = (header[50] * 256 + header[51]) / 64;
+  level->field[jx][jy] = EL_PLAYER_2;
+}
+
+void LoadLevelFromFileInfo(struct LevelInfo *level,
+                          struct LevelFileInfo *level_file_info)
+{
+  switch (level_file_info->type)
+  {
+    case LEVEL_FILE_TYPE_RND:
+      LoadLevelFromFileInfo_RND(level, level_file_info);
+      break;
+
+    case LEVEL_FILE_TYPE_EM:
+      LoadLevelFromFileInfo_EM(level, level_file_info);
+      break;
+
+    default:
+      LoadLevelFromFileInfo_RND(level, level_file_info);
+      break;
+  }
+}
+
+void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
+{
+  static struct LevelFileInfo level_file_info;
+
+  level_file_info.nr = 0;                      /* unknown */
+  level_file_info.type = LEVEL_FILE_TYPE_RND;  /* no others supported yet */
+  level_file_info.filename = filename;
+
+  LoadLevelFromFileInfo(level, &level_file_info);
+}
+
 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 {
   if (leveldir_current == NULL)                /* only when dumping level */
@@ -1027,12 +1468,12 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
      (these following values were accidentally changed in version 3.0.1) */
   if (level->game_version <= VERSION_IDENT(3,0,0,0))
   {
-    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
       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; j--)
+      for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 2))
        {
@@ -1042,7 +1483,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_COLLISION; j--)
+      for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 1))
        {
@@ -1054,15 +1495,15 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 
   /* some custom element change events get mapped since version 3.0.3 */
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
-    if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
-       HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
+    if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
+       HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
     {
-      SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
-      SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
+      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);
     }
@@ -1071,7 +1512,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   /* initialize "can_change" field for old levels with only one change page */
   if (level->game_version <= VERSION_IDENT(3,0,2,0))
   {
-    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
       int element = EL_CUSTOM_START + i;
 
@@ -1094,7 +1535,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 
   /* set uninitialized push delay values of custom elements in older levels */
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
@@ -1114,9 +1555,9 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
   int x, y;
 
   /* map elements that have changed in newer versions */
-  for(y=0; y<level->fieldy; y++)
+  for (y = 0; y < level->fieldy; y++)
   {
-    for(x=0; x<level->fieldx; x++)
+    for (x = 0; x < level->fieldx; x++)
     {
       int element = level->field[x][y];
 
@@ -1144,8 +1585,8 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
   }
 
   /* copy elements to runtime playfield array */
-  for(x=0; x<MAX_LEV_FIELDX; x++)
-    for(y=0; y<MAX_LEV_FIELDY; y++)
+  for (x = 0; x < MAX_LEV_FIELDX; x++)
+    for (y = 0; y < MAX_LEV_FIELDY; y++)
       Feld[x][y] = level->field[x][y];
 
   /* initialize level size variables for faster access */
@@ -1156,11 +1597,18 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
   SetBorderElement();
 }
 
-void LoadLevelTemplate(int level_nr)
+void LoadLevelTemplate(int nr)
 {
-  char *filename = getLevelFilename(level_nr);
+#if 1
+  struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
+  char *filename = level_file_info->filename;
+
+  LoadLevelFromFileInfo(&level, level_file_info);
+#else
+  char *filename = getDefaultLevelFilename(nr);
 
-  LoadLevelFromFilename(&level_template, filename);
+  LoadLevelFromFilename_RND(&level_template, filename);
+#endif
 
   LoadLevel_InitVersion(&level, filename);
   LoadLevel_InitElements(&level, filename);
@@ -1168,11 +1616,18 @@ void LoadLevelTemplate(int level_nr)
   ActivateLevelTemplate();
 }
 
-void LoadLevel(int level_nr)
+void LoadLevel(int nr)
 {
-  char *filename = getLevelFilename(level_nr);
+#if 1
+  struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
+  char *filename = level_file_info->filename;
+
+  LoadLevelFromFileInfo(&level, level_file_info);
+#else
+  char *filename = getLevelFilename(nr);
 
-  LoadLevelFromFilename(&level, filename);
+  LoadLevelFromFilename_RND(&level, filename);
+#endif
 
   if (level.use_custom_template)
     LoadLevelTemplate(-1);
@@ -1202,15 +1657,15 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   putFile16BitBE(file, level->time);
   putFile16BitBE(file, level->gems_needed);
 
-  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
     putFile8Bit(file, level->name[i]);
 
-  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     putFile8Bit(file, level->score[i]);
 
-  for(i=0; i<STD_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
                           level->yamyam_content[i][x][y]));
   putFile8Bit(file, level->amoeba_speed);
@@ -1232,7 +1687,7 @@ static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
 {
   int i;
 
-  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+  for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
     putFile8Bit(file, level->author[i]);
 }
 
@@ -1240,8 +1695,8 @@ static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
 {
   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)
        putFile16BitBE(file, level->field[x][y]);
       else
@@ -1258,9 +1713,9 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
   putFile8Bit(file, 0);
   putFile8Bit(file, 0);
 
-  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        if (level->encoding_16bit_field)
          putFile16BitBE(file, level->yamyam_content[i][x][y]);
        else
@@ -1280,9 +1735,9 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     content_xsize = 3;
     content_ysize = 3;
 
-    for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-      for(y=0; y<3; y++)
-       for(x=0; x<3; x++)
+    for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+      for (y = 0; y < 3; y++)
+       for (x = 0; x < 3; x++)
          content_array[i][x][y] = level->yamyam_content[i][x][y];
   }
   else if (element == EL_BD_AMOEBA)
@@ -1291,9 +1746,9 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     content_xsize = 1;
     content_ysize = 1;
 
-    for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-      for(y=0; y<3; y++)
-       for(x=0; x<3; x++)
+    for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+      for (y = 0; y < 3; y++)
+       for (x = 0; x < 3; x++)
          content_array[i][x][y] = EL_EMPTY;
     content_array[0][0][0] = level->amoeba_content;
   }
@@ -1313,9 +1768,9 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
 
   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
 
-  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        putFile16BitBE(file, content_array[i][x][y]);
 }
 
@@ -1332,7 +1787,7 @@ static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
 
   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
 
-  for(i=0; i < envelope_len; i++)
+  for (i = 0; i < envelope_len; i++)
     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
 }
 
@@ -1344,7 +1799,7 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
 
   putFile16BitBE(file, num_changed_custom_elements);
 
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
@@ -1373,7 +1828,7 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
 
   putFile16BitBE(file, num_changed_custom_elements);
 
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
@@ -1402,7 +1857,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
 
   putFile16BitBE(file, num_changed_custom_elements);
 
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
@@ -1412,7 +1867,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
       {
        putFile16BitBE(file, element);
 
-       for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
+       for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
          putFile8Bit(file, element_info[element].description[j]);
 
        putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
@@ -1435,8 +1890,8 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
        putFile8Bit(file, element_info[element].move_direction_initial);
        putFile8Bit(file, element_info[element].move_stepsize);
 
-       for(y=0; y<3; y++)
-         for(x=0; x<3; x++)
+       for (y = 0; y < 3; y++)
+         for (x = 0; x < 3; x++)
            putFile16BitBE(file, element_info[element].content[x][y]);
 
        putFile32BitBE(file, element_info[element].change->events);
@@ -1457,8 +1912,8 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
        putFile8Bit(file, element_info[element].change->random);
        putFile8Bit(file, element_info[element].change->power);
 
-       for(y=0; y<3; y++)
-         for(x=0; x<3; x++)
+       for (y = 0; y < 3; y++)
+         for (x = 0; x < 3; x++)
            putFile16BitBE(file, element_info[element].change->content[x][y]);
 
        putFile8Bit(file, element_info[element].slippery_type);
@@ -1483,7 +1938,7 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
   putFile16BitBE(file, element);
 
-  for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
     putFile8Bit(file, ei->description[i]);
 
   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
@@ -1513,8 +1968,8 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
   putFile8Bit(file, ei->slippery_type);
 
-  for(y=0; y<3; y++)
-    for(x=0; x<3; x++)
+  for (y = 0; y < 3; y++)
+    for (x = 0; x < 3; x++)
       putFile16BitBE(file, ei->content[x][y]);
 
   /* some free bytes for future custom property values and padding */
@@ -1522,7 +1977,7 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
   /* write change property values */
 
-  for (i=0; i < ei->num_change_pages; i++)
+  for (i = 0; i < ei->num_change_pages; i++)
   {
     struct ElementChangeInfo *change = &ei->change_page[i];
 
@@ -1544,8 +1999,8 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
     putFile8Bit(file, change->random);
     putFile8Bit(file, change->power);
 
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        putFile16BitBE(file, change->content[x][y]);
 
     putFile8Bit(file, change->can_change);
@@ -1574,16 +2029,16 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
 
   /* check level field for 16-bit elements */
   level->encoding_16bit_field = FALSE;
-  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->field[x][y] > 255)
        level->encoding_16bit_field = TRUE;
 
   /* check yamyam content for 16-bit elements */
   level->encoding_16bit_yamyam = FALSE;
-  for(i=0; i<level->num_yamyam_contents; i++)
-    for(y=0; y<3; y++)
-      for(x=0; x<3; x++)
+  for (i = 0; i < level->num_yamyam_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
        if (level->yamyam_content[i][x][y] > 255)
          level->encoding_16bit_yamyam = TRUE;
 
@@ -1625,7 +2080,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   }
 
   /* check for envelope content */
-  for (i=0; i<4; i++)
+  for (i = 0; i < 4; i++)
   {
     if (strlen(level->envelope_text[i]) > 0)
     {
@@ -1639,7 +2094,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   /* check for non-default custom elements (unless using template level) */
   if (!level->use_custom_template)
   {
-    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
       int element = EL_CUSTOM_START + i;
 
@@ -1658,16 +2113,16 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
 
-void SaveLevel(int level_nr)
+void SaveLevel(int nr)
 {
-  char *filename = getLevelFilename(level_nr);
+  char *filename = getDefaultLevelFilename(nr);
 
   SaveLevelFromFilename(&level, filename);
 }
 
 void SaveLevelTemplate()
 {
-  char *filename = getLevelFilename(-1);
+  char *filename = getDefaultLevelFilename(-1);
 
   SaveLevelFromFilename(&level, filename);
 }
@@ -1715,7 +2170,7 @@ static void setTapeInfoToDefaults()
 
   /* default values (also for pre-1.2 tapes) with only the first player */
   tape.player_participates[0] = TRUE;
-  for(i=1; i<MAX_PLAYERS; i++)
+  for (i = 1; i < MAX_PLAYERS; i++)
     tape.player_participates[i] = FALSE;
 
   /* at least one (default: the first) player participates in every tape */
@@ -1754,7 +2209,7 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
 
     /* since version 1.2, tapes store which players participate in the tape */
     tape->num_participating_players = 0;
-    for(i=0; i<MAX_PLAYERS; i++)
+    for (i = 0; i < MAX_PLAYERS; i++)
     {
       tape->player_participates[i] = FALSE;
 
@@ -1787,7 +2242,7 @@ static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
   tape->level_identifier =
     checked_realloc(tape->level_identifier, level_identifier_size);
 
-  for(i=0; i < level_identifier_size; i++)
+  for (i = 0; i < level_identifier_size; i++)
     tape->level_identifier[i] = getFile8Bit(file);
 
   tape->level_nr = getFile16BitBE(file);
@@ -1809,12 +2264,12 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
     return chunk_size_expected;
   }
 
-  for(i=0; i<tape->length; i++)
+  for (i = 0; i < tape->length; i++)
   {
     if (i >= MAX_TAPELEN)
       break;
 
-    for(j=0; j<MAX_PLAYERS; j++)
+    for (j = 0; j < MAX_PLAYERS; j++)
     {
       tape->pos[i].action[j] = MV_NO_MOVING;
 
@@ -1833,7 +2288,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
       byte action = tape->pos[i].action[0];
       int k, num_moves = 0;
 
-      for (k=0; k<4; k++)
+      for (k = 0; k<4; k++)
       {
        if (action & joy_dir[k])
        {
@@ -1862,7 +2317,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
        tape->pos[i + 1].delay = 1;
 
        /* delay part */
-       for(j=0; j<MAX_PLAYERS; j++)
+       for (j = 0; j < MAX_PLAYERS; j++)
          tape->pos[i].action[j] = MV_NO_MOVING;
        tape->pos[i].delay--;
 
@@ -2004,9 +2459,16 @@ void LoadTapeFromFilename(char *filename)
 #endif
 }
 
-void LoadTape(int level_nr)
+void LoadTape(int nr)
+{
+  char *filename = getTapeFilename(nr);
+
+  LoadTapeFromFilename(filename);
+}
+
+void LoadSolutionTape(int nr)
 {
-  char *filename = getTapeFilename(level_nr);
+  char *filename = getSolutionTapeFilename(nr);
 
   LoadTapeFromFilename(filename);
 }
@@ -2023,7 +2485,7 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
   byte store_participating_players = 0;
 
   /* set bits for participating players for compact storage */
-  for(i=0; i<MAX_PLAYERS; i++)
+  for (i = 0; i < MAX_PLAYERS; i++)
     if (tape->player_participates[i])
       store_participating_players |= (1 << i);
 
@@ -2046,7 +2508,7 @@ static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
 
   putFile16BitBE(file, level_identifier_size);
 
-  for(i=0; i < level_identifier_size; i++)
+  for (i = 0; i < level_identifier_size; i++)
     putFile8Bit(file, tape->level_identifier[i]);
 
   putFile16BitBE(file, tape->level_nr);
@@ -2056,9 +2518,9 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
 {
   int i, j;
 
-  for(i=0; i<tape->length; i++)
+  for (i = 0; i < tape->length; i++)
   {
-    for(j=0; j<MAX_PLAYERS; j++)
+    for (j = 0; j < MAX_PLAYERS; j++)
       if (tape->player_participates[j])
        putFile8Bit(file, tape->pos[i].action[j]);
 
@@ -2066,9 +2528,9 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
   }
 }
 
-void SaveTape(int level_nr)
+void SaveTape(int nr)
 {
-  char *filename = getTapeFilename(level_nr);
+  char *filename = getTapeFilename(nr);
   FILE *file;
   boolean new_tape = TRUE;
   int num_participating_players = 0;
@@ -2096,7 +2558,7 @@ void SaveTape(int level_nr)
   tape.game_version = GAME_VERSION_ACTUAL;
 
   /* count number of participating players  */
-  for(i=0; i<MAX_PLAYERS; i++)
+  for (i = 0; i < MAX_PLAYERS; i++)
     if (tape.player_participates[i])
       num_participating_players++;
 
@@ -2144,14 +2606,14 @@ void DumpTape(struct TapeInfo *tape)
   printf("Level series identifier: '%s'\n", tape->level_identifier);
   printf_line("-", 79);
 
-  for(i=0; i<tape->length; i++)
+  for (i = 0; i < tape->length; i++)
   {
     if (i >= MAX_TAPELEN)
       break;
 
     printf("%03d: ", i);
 
-    for(j=0; j<MAX_PLAYERS; j++)
+    for (j = 0; j < MAX_PLAYERS; j++)
     {
       if (tape->player_participates[j])
       {
@@ -2179,17 +2641,17 @@ void DumpTape(struct TapeInfo *tape)
 /* score file functions                                                      */
 /* ========================================================================= */
 
-void LoadScore(int level_nr)
+void LoadScore(int nr)
 {
   int i;
-  char *filename = getScoreFilename(level_nr);
+  char *filename = getScoreFilename(nr);
   char cookie[MAX_LINE_LEN];
   char line[MAX_LINE_LEN];
   char *line_ptr;
   FILE *file;
 
   /* always start with reliable default values */
-  for(i=0; i<MAX_SCORE_ENTRIES; i++)
+  for (i = 0; i < MAX_SCORE_ENTRIES; i++)
   {
     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
     highscore[i].Score = 0;
@@ -2210,7 +2672,7 @@ void LoadScore(int level_nr)
     return;
   }
 
-  for(i=0; i<MAX_SCORE_ENTRIES; i++)
+  for (i = 0; i < MAX_SCORE_ENTRIES; i++)
   {
     fscanf(file, "%d", &highscore[i].Score);
     fgets(line, MAX_LINE_LEN, file);
@@ -2232,23 +2694,23 @@ void LoadScore(int level_nr)
   fclose(file);
 }
 
-void SaveScore(int level_nr)
+void SaveScore(int nr)
 {
   int i;
-  char *filename = getScoreFilename(level_nr);
+  char *filename = getScoreFilename(nr);
   FILE *file;
 
   InitScoreDirectory(leveldir_current->filename);
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save score for level %d", level_nr);
+    Error(ERR_WARN, "cannot save score for level %d", nr);
     return;
   }
 
   fprintf(file, "%s\n\n", SCORE_COOKIE);
 
-  for(i=0; i<MAX_SCORE_ENTRIES; i++)
+  for (i = 0; i < MAX_SCORE_ENTRIES; i++)
     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
 
   fclose(file);
@@ -2322,13 +2784,13 @@ void SaveScore(int level_nr)
 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE         6
 #define SETUP_TOKEN_PLAYER_JOY_YLOWER          7
 #define SETUP_TOKEN_PLAYER_JOY_SNAP            8
-#define SETUP_TOKEN_PLAYER_JOY_BOMB            9
+#define SETUP_TOKEN_PLAYER_JOY_DROP            9
 #define SETUP_TOKEN_PLAYER_KEY_LEFT            10
 #define SETUP_TOKEN_PLAYER_KEY_RIGHT           11
 #define SETUP_TOKEN_PLAYER_KEY_UP              12
 #define SETUP_TOKEN_PLAYER_KEY_DOWN            13
 #define SETUP_TOKEN_PLAYER_KEY_SNAP            14
-#define SETUP_TOKEN_PLAYER_KEY_BOMB            15
+#define SETUP_TOKEN_PLAYER_KEY_DROP            15
 
 #define NUM_PLAYER_SETUP_TOKENS                        16
 
@@ -2411,13 +2873,13 @@ static struct TokenInfo player_setup_tokens[] =
   { TYPE_INTEGER, &sii.joy.ymiddle,    ".joy.ymiddle"                  },
   { TYPE_INTEGER, &sii.joy.ylower,     ".joy.ylower"                   },
   { TYPE_INTEGER, &sii.joy.snap,       ".joy.snap_field"               },
-  { TYPE_INTEGER, &sii.joy.bomb,       ".joy.place_bomb"               },
+  { TYPE_INTEGER, &sii.joy.drop,       ".joy.place_bomb"               },
   { TYPE_KEY_X11, &sii.key.left,       ".key.move_left"                },
   { TYPE_KEY_X11, &sii.key.right,      ".key.move_right"               },
   { TYPE_KEY_X11, &sii.key.up,         ".key.move_up"                  },
   { TYPE_KEY_X11, &sii.key.down,       ".key.move_down"                },
   { TYPE_KEY_X11, &sii.key.snap,       ".key.snap_field"               },
-  { TYPE_KEY_X11, &sii.key.bomb,       ".key.place_bomb"               }
+  { TYPE_KEY_X11, &sii.key.drop,       ".key.place_bomb"               }
 };
 
 static struct TokenInfo system_setup_tokens[] =
@@ -2495,7 +2957,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
 
-  for (i=0; i<MAX_PLAYERS; i++)
+  for (i = 0; i < MAX_PLAYERS; i++)
   {
     si->input[i].use_joystick = FALSE;
     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
@@ -2506,13 +2968,13 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
-    si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
+    si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
-    si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
+    si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
   }
 
   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
@@ -2530,34 +2992,34 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 
   /* global setup */
   si = setup;
-  for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
     setSetupInfo(global_setup_tokens, i,
                 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
   setup = si;
 
   /* editor setup */
   sei = setup.editor;
-  for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
     setSetupInfo(editor_setup_tokens, i,
                 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
   setup.editor = sei;
 
   /* shortcut setup */
   ssi = setup.shortcut;
-  for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
     setSetupInfo(shortcut_setup_tokens, i,
                 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
   setup.shortcut = ssi;
 
   /* player setup */
-  for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+  for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
   {
     char prefix[30];
 
     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
 
     sii = setup.input[pnr];
-    for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
+    for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
     {
       char full_token[100];
 
@@ -2570,14 +3032,14 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 
   /* system setup */
   syi = setup.system;
-  for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
     setSetupInfo(system_setup_tokens, i,
                 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
   setup.system = syi;
 
   /* options setup */
   soi = setup.options;
-  for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
     setSetupInfo(options_setup_tokens, i,
                 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
   setup.options = soi;
@@ -2633,7 +3095,7 @@ void SaveSetup()
 
   /* global setup */
   si = setup;
-  for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
   {
     /* just to make things nicer :) */
     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
@@ -2646,17 +3108,17 @@ void SaveSetup()
   /* editor setup */
   sei = setup.editor;
   fprintf(file, "\n");
-  for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
 
   /* shortcut setup */
   ssi = setup.shortcut;
   fprintf(file, "\n");
-  for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
 
   /* player setup */
-  for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+  for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
   {
     char prefix[30];
 
@@ -2664,20 +3126,20 @@ void SaveSetup()
     fprintf(file, "\n");
 
     sii = setup.input[pnr];
-    for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
+    for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
   }
 
   /* system setup */
   syi = setup.system;
   fprintf(file, "\n");
-  for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
 
   /* options setup */
   soi = setup.options;
   fprintf(file, "\n");
-  for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
+  for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
 
   fclose(file);
@@ -2691,7 +3153,7 @@ void LoadCustomElementDescriptions()
   SetupFileHash *setup_file_hash;
   int i;
 
-  for (i=0; i<NUM_FILE_ELEMENTS; i++)
+  for (i = 0; i < NUM_FILE_ELEMENTS; i++)
   {
     if (element_info[i].custom_description != NULL)
     {
@@ -2703,7 +3165,7 @@ void LoadCustomElementDescriptions()
   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
     return;
 
-  for (i=0; i<NUM_FILE_ELEMENTS; i++)
+  for (i = 0; i < NUM_FILE_ELEMENTS; i++)
   {
     char *token = getStringCat2(element_info[i].token_name, ".name");
     char *value = getHashEntry(setup_file_hash, token);
@@ -2724,8 +3186,8 @@ void LoadSpecialMenuDesignSettings()
   int i, j;
 
   /* always start with reliable default values from default config */
-  for (i=0; image_config_vars[i].token != NULL; i++)
-    for (j=0; image_config[j].token != NULL; j++)
+  for (i = 0; image_config_vars[i].token != NULL; i++)
+    for (j = 0; image_config[j].token != NULL; j++)
       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
        *image_config_vars[i].value =
          get_auto_parameter_value(image_config_vars[i].token,
@@ -2735,7 +3197,7 @@ void LoadSpecialMenuDesignSettings()
     return;
 
   /* special case: initialize with default values that may be overwritten */
-  for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
   {
     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
@@ -2750,7 +3212,7 @@ void LoadSpecialMenuDesignSettings()
   }
 
   /* read (and overwrite with) values that may be specified in config file */
-  for (i=0; image_config_vars[i].token != NULL; i++)
+  for (i = 0; image_config_vars[i].token != NULL; i++)
   {
     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
 
@@ -2762,23 +3224,6 @@ void LoadSpecialMenuDesignSettings()
   freeSetupFileHash(setup_file_hash);
 }
 
-static char *itoa(unsigned int i)
-{
-  static char *a = NULL;
-
-  if (a != NULL)
-    free(a);
-
-  if (i > 2147483647)  /* yes, this is a kludge */
-    i = 2147483647;
-
-  a = checked_malloc(10 + 1);
-
-  sprintf(a, "%d", i);
-
-  return a;
-}
-
 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 {
   char *filename = getEditorSetupFilename();
@@ -2792,8 +3237,8 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 
   element_hash = newSetupFileHash();
 
-  for (i=0; i < NUM_FILE_ELEMENTS; i++)
-    setHashEntry(element_hash, element_info[i].token_name, itoa(i));
+  for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+    setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
 
   /* determined size may be larger than needed (due to unknown elements) */
   *num_elements = 0;
@@ -2840,18 +3285,18 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 
 #if 0
   /* TEST-ONLY */
-  for (i=0; i < *num_elements; i++)
+  for (i = 0; i < *num_elements; i++)
     printf("editor: element '%s' [%d]\n",
           element_info[(*elements)[i]].token_name, (*elements)[i]);
 #endif
 }
 
-static struct MusicFileInfo *get_music_file_info(char *basename)
+static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
+                                                    boolean is_sound)
 {
   SetupFileHash *setup_file_hash = NULL;
   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
-  char *filename_music = getCustomMusicFilename(basename);
-  char *filename_prefix, *filename_info;
+  char *filename_music, *filename_prefix, *filename_info;
   struct
   {
     char *token;
@@ -2859,15 +3304,23 @@ static struct MusicFileInfo *get_music_file_info(char *basename)
   }
   token_to_value_ptr[] =
   {
-    { "context",       &tmp_music_file_info.context    },
-    { "title",         &tmp_music_file_info.title      },
-    { "artist",                &tmp_music_file_info.artist     },
-    { "album",         &tmp_music_file_info.album      },
-    { "year",          &tmp_music_file_info.year       },
-    { NULL,            NULL                            },
+    { "title_header",  &tmp_music_file_info.title_header       },
+    { "artist_header", &tmp_music_file_info.artist_header      },
+    { "album_header",  &tmp_music_file_info.album_header       },
+    { "year_header",   &tmp_music_file_info.year_header        },
+
+    { "title",         &tmp_music_file_info.title              },
+    { "artist",                &tmp_music_file_info.artist             },
+    { "album",         &tmp_music_file_info.album              },
+    { "year",          &tmp_music_file_info.year               },
+
+    { NULL,            NULL                                    },
   };
   int i;
 
+  filename_music = (is_sound ? getCustomSoundFilename(basename) :
+                   getCustomMusicFilename(basename));
+
   if (filename_music == NULL)
     return NULL;
 
@@ -2911,26 +3364,65 @@ static struct MusicFileInfo *get_music_file_info(char *basename)
 
   /* ---------- music file info found ---------- */
 
+  memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
+
   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
   {
     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
 
-    *token_to_value_ptr[i].value_ptr = getStringCopy(value);  /* may be NULL */
+    *token_to_value_ptr[i].value_ptr =
+      getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
   }
 
-  new_music_file_info = checked_calloc(sizeof(struct MusicFileInfo));
+  tmp_music_file_info.basename = getStringCopy(basename);
+  tmp_music_file_info.music = music;
+  tmp_music_file_info.is_sound = is_sound;
+
+  new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
   *new_music_file_info = tmp_music_file_info;
 
   return new_music_file_info;
 }
 
+static struct MusicFileInfo *get_music_file_info(char *basename, int music)
+{
+  return get_music_file_info_ext(basename, music, FALSE);
+}
+
+static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
+{
+  return get_music_file_info_ext(basename, sound, TRUE);
+}
+
+static boolean music_info_listed_ext(struct MusicFileInfo *list,
+                                    char *basename, boolean is_sound)
+{
+  for (; list != NULL; list = list->next)
+    if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
+      return TRUE;
+
+  return FALSE;
+}
+
+static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
+{
+  return music_info_listed_ext(list, basename, FALSE);
+}
+
+static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
+{
+  return music_info_listed_ext(list, basename, TRUE);
+}
+
 void LoadMusicInfo()
 {
   char *music_directory = getCustomMusicDirectory();
   int num_music = getMusicListSize();
+  int num_music_noconf = 0;
+  int num_sounds = getSoundListSize();
   DIR *dir;
   struct dirent *dir_entry;
-  struct FileInfo *music;
+  struct FileInfo *music, *sound;
   struct MusicFileInfo *next, **new;
   int i;
 
@@ -2938,16 +3430,17 @@ void LoadMusicInfo()
   {
     next = music_file_info->next;
 
-    if (music_file_info->context)
-      free(music_file_info->context);
-    if (music_file_info->title)
-      free(music_file_info->title);
-    if (music_file_info->artist)
-      free(music_file_info->artist);
-    if (music_file_info->album)
-      free(music_file_info->album);
-    if (music_file_info->year)
-      free(music_file_info->year);
+    checked_free(music_file_info->basename);
+
+    checked_free(music_file_info->title_header);
+    checked_free(music_file_info->artist_header);
+    checked_free(music_file_info->album_header);
+    checked_free(music_file_info->year_header);
+
+    checked_free(music_file_info->title);
+    checked_free(music_file_info->artist);
+    checked_free(music_file_info->album);
+    checked_free(music_file_info->year);
 
     free(music_file_info);
 
@@ -2956,20 +3449,38 @@ void LoadMusicInfo()
 
   new = &music_file_info;
 
-  for (i=0; i < num_music; i++)
+#if 0
+  printf("::: num_music == %d\n", num_music);
+#endif
+
+  for (i = 0; i < num_music; i++)
   {
     music = getMusicListEntry(i);
 
+#if 0
+    printf("::: %d [%08x]\n", i, music->filename);
+#endif
+
+    if (music->filename == NULL)
+      continue;
+
     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
       continue;
 
+    /* a configured file may be not recognized as music */
+    if (!FileIsMusic(music->filename))
+      continue;
+
 #if 0
-    printf("::: -> '%s'\n", music->filename);
+    printf("::: -> '%s' (configured)\n", music->filename);
 #endif
 
-    *new = get_music_file_info(music->filename);
-    if (*new != NULL)
-      new = &(*new)->next;
+    if (!music_info_listed(music_file_info, music->filename))
+    {
+      *new = get_music_file_info(music->filename, i);
+      if (*new != NULL)
+       new = &(*new)->next;
+    }
   }
 
   if ((dir = opendir(music_directory)) == NULL)
@@ -2984,10 +3495,14 @@ void LoadMusicInfo()
     boolean music_already_used = FALSE;
     int i;
 
-    for (i=0; i < num_music; i++)
+    /* skip all music files that are configured in music config file */
+    for (i = 0; i < num_music; i++)
     {
       music = getMusicListEntry(i);
 
+      if (music->filename == NULL)
+       continue;
+
       if (strcmp(basename, music->filename) == 0)
       {
        music_already_used = TRUE;
@@ -2998,20 +3513,51 @@ void LoadMusicInfo()
     if (music_already_used)
       continue;
 
-    if (!FileIsSound(basename) && !FileIsMusic(basename))
+    if (!FileIsMusic(basename))
       continue;
 
 #if 0
-    printf("::: -> '%s'\n", basename);
+    printf("::: -> '%s' (found in directory)\n", basename);
 #endif
 
-    *new = get_music_file_info(basename);
-    if (*new != NULL)
-      new = &(*new)->next;
+    if (!music_info_listed(music_file_info, basename))
+    {
+      *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
+      if (*new != NULL)
+       new = &(*new)->next;
+    }
+
+    num_music_noconf++;
   }
 
   closedir(dir);
 
+  for (i = 0; i < num_sounds; i++)
+  {
+    sound = getSoundListEntry(i);
+
+    if (sound->filename == NULL)
+      continue;
+
+    if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
+      continue;
+
+    /* a configured file may be not recognized as sound */
+    if (!FileIsSound(sound->filename))
+      continue;
+
+#if 0
+    printf("::: -> '%s' (configured)\n", sound->filename);
+#endif
+
+    if (!sound_info_listed(music_file_info, sound->filename))
+    {
+      *new = get_sound_file_info(sound->filename, i);
+      if (*new != NULL)
+       new = &(*new)->next;
+    }
+  }
+
 #if 0
   /* TEST-ONLY */
   for (next = music_file_info; next != NULL; next = next->next)
@@ -3019,16 +3565,16 @@ void LoadMusicInfo()
 #endif
 }
 
-void add_info_animation(int element, int action, int direction, int delay,
+void add_helpanim_entry(int element, int action, int direction, int delay,
                        int *num_list_entries)
 {
-  struct InfoAnimationInfo *new_list_entry;
+  struct HelpAnimInfo *new_list_entry;
   (*num_list_entries)++;
 
-  info_animation_info =
-    checked_realloc(info_animation_info,
-                   *num_list_entries * sizeof(struct InfoAnimationInfo));
-  new_list_entry = &info_animation_info[*num_list_entries - 1];
+  helpanim_info =
+    checked_realloc(helpanim_info,
+                   *num_list_entries * sizeof(struct HelpAnimInfo));
+  new_list_entry = &helpanim_info[*num_list_entries - 1];
 
   new_list_entry->element = element;
   new_list_entry->action = action;
@@ -3054,45 +3600,48 @@ void print_unknown_token_end(int token_nr)
     Error(ERR_RETURN_LINE, "-");
 }
 
-void LoadInfoAnimations()
+void LoadHelpAnimInfo()
 {
-  char *filename = getElementInfoFilename();
-  SetupFileList *setup_file_list, *list;
+  char *filename = getHelpAnimFilename();
+  SetupFileList *setup_file_list = NULL, *list;
   SetupFileHash *element_hash, *action_hash, *direction_hash;
   int num_list_entries = 0;
   int num_unknown_tokens = 0;
   int i;
 
-  if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+  if (fileExists(filename))
+    setup_file_list = loadSetupFileList(filename);
+
+  if (setup_file_list == NULL)
   {
     /* use reliable default values from static configuration */
     SetupFileList *insert_ptr;
 
     insert_ptr = setup_file_list =
-      newSetupFileList(info_animation_config[0].token,
-                      info_animation_config[0].value);
+      newSetupFileList(helpanim_config[0].token,
+                      helpanim_config[0].value);
 
-    for (i=1; info_animation_config[i].token; i++)
-       insert_ptr = addListEntry(insert_ptr,
-                                 info_animation_config[i].token,
-                                 info_animation_config[i].value);
+    for (i = 1; helpanim_config[i].token; i++)
+      insert_ptr = addListEntry(insert_ptr,
+                               helpanim_config[i].token,
+                               helpanim_config[i].value);
   }
 
   element_hash   = newSetupFileHash();
   action_hash    = newSetupFileHash();
   direction_hash = newSetupFileHash();
 
-  for (i=0; i < MAX_NUM_ELEMENTS; i++)
-    setHashEntry(element_hash, element_info[i].token_name, itoa(i));
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
 
-  for (i=0; i < NUM_ACTIONS; i++)
+  for (i = 0; i < NUM_ACTIONS; i++)
     setHashEntry(action_hash, element_action_info[i].suffix,
-                itoa(element_action_info[i].value));
+                i_to_a(element_action_info[i].value));
 
   /* do not store direction index (bit) here, but direction value! */
-  for (i=0; i < NUM_DIRECTIONS; i++)
+  for (i = 0; i < NUM_DIRECTIONS; i++)
     setHashEntry(direction_hash, element_direction_info[i].suffix,
-                itoa(1 << element_direction_info[i].value));
+                i_to_a(1 << element_direction_info[i].value));
 
   for (list = setup_file_list; list != NULL; list = list->next)
   {
@@ -3102,41 +3651,49 @@ void LoadInfoAnimations()
 
     if (strcmp(list->token, "end") == 0)
     {
-      add_info_animation(-1, -1, -1, -1, &num_list_entries);
+      add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
 
       continue;
     }
 
-    element_token = list->token;
-    element_value = getHashEntry(element_hash, element_token);
+    /* first try to break element into element/action/direction parts;
+       if this does not work, also accept combined "element[.act][.dir]"
+       elements (like "dynamite.active"), which are unique elements */
 
-    if (element_value != NULL)
+    if (strchr(list->token, '.') == NULL)      /* token contains no '.' */
     {
-      /* element found */
-      add_info_animation(atoi(element_value), -1, -1,
-                        delay, &num_list_entries);
+      element_value = getHashEntry(element_hash, list->token);
+      if (element_value != NULL)       /* element found */
+       add_helpanim_entry(atoi(element_value), -1, -1, delay,
+                          &num_list_entries);
+      else
+      {
+       /* no further suffixes found -- this is not an element */
+       print_unknown_token(filename, list->token, num_unknown_tokens++);
+      }
 
       continue;
     }
 
-    if (strchr(element_token, '.') == NULL)
-    {
-      /* no further suffixes found -- this is not an element */
-      print_unknown_token(filename, list->token, num_unknown_tokens++);
+    /* token has format "<prefix>.<something>" */
 
-      continue;
-    }
+    action_token = strchr(list->token, '.');   /* suffix may be action ... */
+    direction_token = action_token;            /* ... or direction */
 
-    action_token = strchr(element_token, '.');
-    element_token = getStringCopy(element_token);
+    element_token = getStringCopy(list->token);
     *strchr(element_token, '.') = '\0';
 
     element_value = getHashEntry(element_hash, element_token);
 
-    if (element_value == NULL)
+    if (element_value == NULL)         /* this is no element */
     {
-      /* this is not an element */
-      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      element_value = getHashEntry(element_hash, list->token);
+      if (element_value != NULL)       /* combined element found */
+       add_helpanim_entry(atoi(element_value), -1, -1, delay,
+                          &num_list_entries);
+      else
+       print_unknown_token(filename, list->token, num_unknown_tokens++);
+
       free(element_token);
 
       continue;
@@ -3144,24 +3701,23 @@ void LoadInfoAnimations()
 
     action_value = getHashEntry(action_hash, action_token);
 
-    if (action_value != NULL)
+    if (action_value != NULL)          /* action found */
     {
-      /* action found */
-      add_info_animation(atoi(element_value), atoi(action_value), -1,
-                        delay, &num_list_entries);
+      add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
+                   &num_list_entries);
+
       free(element_token);
 
       continue;
     }
 
-    direction_token = action_token;
     direction_value = getHashEntry(direction_hash, direction_token);
 
-    if (direction_value != NULL)
+    if (direction_value != NULL)       /* direction found */
     {
-      /* direction found */
-      add_info_animation(atoi(element_value), -1, atoi(direction_value),
-                        delay, &num_list_entries);
+      add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
+                        &num_list_entries);
+
       free(element_token);
 
       continue;
@@ -3169,23 +3725,38 @@ void LoadInfoAnimations()
 
     if (strchr(action_token + 1, '.') == NULL)
     {
-      /* no further suffixes found -- this is not an action or direction */
-      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      /* no further suffixes found -- this is not an action nor direction */
+
+      element_value = getHashEntry(element_hash, list->token);
+      if (element_value != NULL)       /* combined element found */
+       add_helpanim_entry(atoi(element_value), -1, -1, delay,
+                          &num_list_entries);
+      else
+       print_unknown_token(filename, list->token, num_unknown_tokens++);
+
       free(element_token);
 
       continue;
     }
 
+    /* token has format "<prefix>.<suffix>.<something>" */
+
     direction_token = strchr(action_token + 1, '.');
+
     action_token = getStringCopy(action_token);
     *strchr(action_token + 1, '.') = '\0';
 
     action_value = getHashEntry(action_hash, action_token);
 
-    if (action_value == NULL)
+    if (action_value == NULL)          /* this is no action */
     {
-      /* this is not an action */
-      print_unknown_token(filename, list->token, num_unknown_tokens++);
+      element_value = getHashEntry(element_hash, list->token);
+      if (element_value != NULL)       /* combined element found */
+       add_helpanim_entry(atoi(element_value), -1, -1, delay,
+                          &num_list_entries);
+      else
+       print_unknown_token(filename, list->token, num_unknown_tokens++);
+
       free(element_token);
       free(action_token);
 
@@ -3194,19 +3765,25 @@ void LoadInfoAnimations()
 
     direction_value = getHashEntry(direction_hash, direction_token);
 
-    if (direction_value != NULL)
+    if (direction_value != NULL)       /* direction found */
     {
-      /* direction found */
-      add_info_animation(atoi(element_value), atoi(action_value),
-                        atoi(direction_value),
-                        delay, &num_list_entries);
+      add_helpanim_entry(atoi(element_value), atoi(action_value),
+                        atoi(direction_value), delay, &num_list_entries);
+
       free(element_token);
       free(action_token);
 
       continue;
     }
 
-    print_unknown_token(filename, list->token, num_unknown_tokens++);
+    /* this is no direction */
+
+    element_value = getHashEntry(element_hash, list->token);
+    if (element_value != NULL)         /* combined element found */
+      add_helpanim_entry(atoi(element_value), -1, -1, delay,
+                        &num_list_entries);
+    else
+      print_unknown_token(filename, list->token, num_unknown_tokens++);
 
     free(element_token);
     free(action_token);
@@ -3214,7 +3791,8 @@ void LoadInfoAnimations()
 
   print_unknown_token_end(num_unknown_tokens);
 
-  add_info_animation(-999, -999, -999, -999, &num_list_entries);
+  add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
+  add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
 
   freeSetupFileList(setup_file_list);
   freeSetupFileHash(element_hash);
@@ -3223,11 +3801,47 @@ void LoadInfoAnimations()
 
 #if 0
   /* TEST ONLY */
-  for (i=0; i < num_list_entries; i++)
+  for (i = 0; i < num_list_entries; i++)
     printf("::: %d, %d, %d => %d\n",
-          info_animation_info[i].element,
-          info_animation_info[i].action,
-          info_animation_info[i].direction,
-          info_animation_info[i].delay);
+          helpanim_info[i].element,
+          helpanim_info[i].action,
+          helpanim_info[i].direction,
+          helpanim_info[i].delay);
+#endif
+}
+
+void LoadHelpTextInfo()
+{
+  char *filename = getHelpTextFilename();
+  int i;
+
+  if (helptext_info != NULL)
+  {
+    freeSetupFileHash(helptext_info);
+    helptext_info = NULL;
+  }
+
+  if (fileExists(filename))
+    helptext_info = loadSetupFileHash(filename);
+
+  if (helptext_info == NULL)
+  {
+    /* use reliable default values from static configuration */
+    helptext_info = newSetupFileHash();
+
+    for (i = 0; helptext_config[i].token; i++)
+      setHashEntry(helptext_info,
+                  helptext_config[i].token,
+                  helptext_config[i].value);
+  }
+
+#if 0
+  /* TEST ONLY */
+  BEGIN_HASH_ITERATION(helptext_info, itr)
+  {
+    printf("::: '%s' => '%s'\n",
+          HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+  }
+  END_HASH_ITERATION(hash, itr)
 #endif
 }