rnd-20030113-1-src
[rocksndiamonds.git] / src / files.c
index 2a72397139931fb4d9c735428353d0925c65e1cb..6d858c475be133ebbe2e54027677bbdbb63c9db7 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -30,7 +30,7 @@
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
 #define TAPE_HEADER_SIZE       20      /* size of tape file header   */
-#define TAPE_HEADER_UNUSED     7       /* unused tape header bytes   */
+#define TAPE_HEADER_UNUSED     3       /* unused tape header bytes   */
 
 /* file identifier strings */
 #define LEVEL_COOKIE_TMPL      "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
@@ -58,7 +58,7 @@ static void setLevelInfoToDefaults()
 
   for(x=0; x<MAX_LEV_FIELDX; x++)
     for(y=0; y<MAX_LEV_FIELDY; y++)
-      Feld[x][y] = Ur[x][y] = EL_ERDREICH;
+      Feld[x][y] = Ur[x][y] = EL_SAND;
 
   level.time = 100;
   level.gems_needed = 0;
@@ -67,7 +67,7 @@ static void setLevelInfoToDefaults()
   level.time_wheel = 10;
   level.time_light = 10;
   level.time_timegate = 10;
-  level.amoeba_content = EL_DIAMANT;
+  level.amoeba_content = EL_DIAMOND;
   level.double_speed = FALSE;
   level.gravity = FALSE;
   level.em_slippery_gems = FALSE;
@@ -88,13 +88,18 @@ static void setLevelInfoToDefaults()
     for(x=0; x<3; x++)
       for(y=0; y<3; y++)
        level.yam_content[i][x][y] =
-         (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
+         (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
 
-  Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
+  Feld[0][0] = Ur[0][0] = EL_PLAYER1;
   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
-    Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
+    Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
 
-  BorderElement = EL_BETON;
+  BorderElement = EL_STEELWALL;
+
+  level.no_level_file = FALSE;
+
+  if (leveldir_current == NULL)                /* only when dumping level */
+    return;
 
   /* try to determine better author name than 'anonymous' */
   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
@@ -129,18 +134,23 @@ static void setLevelInfoToDefaults()
 
 static int checkLevelElement(int element)
 {
-  if (element >= EL_FIRST_RUNTIME_EL)
+  if (element >= NUM_FILE_ELEMENTS)
   {
     Error(ERR_WARN, "invalid level element %d", element);
-    element = EL_CHAR_FRAGE;
+    element = EL_CHAR_QUESTION;
   }
+  else if (element == EL_PLAYER_OBSOLETE)
+    element = EL_PLAYER1;
+  else if (element == EL_KEY_OBSOLETE)
+    element = EL_KEY1;
 
   return element;
 }
 
 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
 {
-  ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
+  level->file_version = getFileVersion(file);
+  level->game_version = getFileVersion(file);
 
   return chunk_size;
 }
@@ -152,8 +162,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   lev_fieldx = level->fieldx = fgetc(file);
   lev_fieldy = level->fieldy = fgetc(file);
 
-  level->time          = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  level->gems_needed   = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  level->time          = getFile16BitBE(file);
+  level->gems_needed   = getFile16BitBE(file);
 
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     level->name[i] = fgetc(file);
@@ -229,8 +239,7 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
       for(x=0; x<3; x++)
        level->yam_content[i][x][y] =
          checkLevelElement(level->encoding_16bit_field ?
-                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
-                           fgetc(file));
+                           getFile16BitBE(file) : fgetc(file));
   return chunk_size;
 }
 
@@ -257,8 +266,7 @@ static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
     for(x=0; x<level->fieldx; x++)
       Feld[x][y] = Ur[x][y] =
        checkLevelElement(level->encoding_16bit_field ?
-                         getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
-                         fgetc(file));
+                         getFile16BitBE(file) : fgetc(file));
   return chunk_size;
 }
 
@@ -269,7 +277,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   int num_contents, content_xsize, content_ysize;
   int content_array[MAX_ELEMENT_CONTENTS][3][3];
 
-  element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
+  element = checkLevelElement(getFile16BitBE(file));
   num_contents = fgetc(file);
   content_xsize = fgetc(file);
   content_ysize = fgetc(file);
@@ -278,14 +286,13 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   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(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
+       content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
 
   /* correct invalid number of content fields -- should never happen */
   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
     num_contents = STD_ELEMENT_CONTENTS;
 
-  if (element == EL_MAMPFER)
+  if (element == EL_YAMYAM)
   {
     level->num_yam_contents = num_contents;
 
@@ -294,7 +301,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
        for(x=0; x<3; x++)
          level->yam_content[i][x][y] = content_array[i][x][y];
   }
-  else if (element == EL_AMOEBE_BD)
+  else if (element == EL_BD_AMOEBA)
   {
     level->amoeba_content = content_array[0][0][0];
   }
@@ -306,9 +313,8 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
-void LoadLevel(int level_nr)
+void LoadLevelFromFilename(char *filename)
 {
-  char *filename = getLevelFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char chunk_name[CHUNK_ID_LEN + 1];
   int chunk_size;
@@ -319,16 +325,18 @@ void LoadLevel(int level_nr)
 
   if (!(file = fopen(filename, MODE_READ)))
   {
+    level.no_level_file = TRUE;
+
     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
     return;
   }
 
-  getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+  getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
   {
-    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+    getFile32BitBE(file);              /* not used */
 
-    getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+    getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "CAVE") != 0)
     {
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
@@ -386,7 +394,7 @@ void LoadLevel(int level_nr)
       {  NULL,  0,                     NULL }
     };
 
-    while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
+    while (getFileChunkBE(file, chunk_name, &chunk_size))
     {
       int i = 0;
 
@@ -427,6 +435,9 @@ void LoadLevel(int level_nr)
 
   fclose(file);
 
+  if (leveldir_current == NULL)                /* only when dumping level */
+    return;
+
   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
       IS_LEVELCLASS_USER(leveldir_current))
   {
@@ -447,6 +458,10 @@ void LoadLevel(int level_nr)
       /* player was faster than monsters in (pre-)1.0 levels */
       level.double_speed = TRUE;
     }
+
+    /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
+    if (level.game_version == VERSION_IDENT(2,0,1))
+      level.em_slippery_gems = TRUE;
   }
   else
   {
@@ -474,6 +489,19 @@ void LoadLevel(int level_nr)
   SetBorderElement();
 }
 
+void LoadLevel(int level_nr)
+{
+  char *filename = getLevelFilename(level_nr);
+
+  LoadLevelFromFilename(filename);
+}
+
+static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
+{
+  putFileVersion(file, level->file_version);
+  putFileVersion(file, level->game_version);
+}
+
 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
 {
   int i, x, y;
@@ -481,8 +509,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   fputc(level->fieldx, file);
   fputc(level->fieldy, file);
 
-  putFile16BitInteger(file, level->time,        BYTE_ORDER_BIG_ENDIAN);
-  putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
+  putFile16BitBE(file, level->time);
+  putFile16BitBE(file, level->gems_needed);
 
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     fputc(level->name[i], file);
@@ -493,13 +521,13 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
+       fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
               level->yam_content[i][x][y]),
              file);
   fputc(level->amoeba_speed, file);
   fputc(level->time_magic_wall, file);
   fputc(level->time_wheel, file);
-  fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
+  fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
        file);
   fputc((level->double_speed ? 1 : 0), file);
   fputc((level->gravity ? 1 : 0), file);
@@ -522,7 +550,7 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
 {
   int i, x, y;
 
-  fputc(EL_MAMPFER, file);
+  fputc(EL_YAMYAM, file);
   fputc(level->num_yam_contents, file);
   fputc(0, file);
   fputc(0, file);
@@ -531,8 +559,7 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
        if (level->encoding_16bit_field)
-         putFile16BitInteger(file, level->yam_content[i][x][y],
-                             BYTE_ORDER_BIG_ENDIAN);
+         putFile16BitBE(file, level->yam_content[i][x][y]);
        else
          fputc(level->yam_content[i][x][y], file);
 }
@@ -545,7 +572,7 @@ static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   for(y=0; y<level->fieldy; y++) 
     for(x=0; x<level->fieldx; x++) 
       if (level->encoding_16bit_field)
-       putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
+       putFile16BitBE(file, Ur[x][y]);
       else
        fputc(Ur[x][y], file);
 }
@@ -556,7 +583,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
   int num_contents, content_xsize, content_ysize;
   int content_array[MAX_ELEMENT_CONTENTS][3][3];
 
-  if (element == EL_MAMPFER)
+  if (element == EL_YAMYAM)
   {
     num_contents = level->num_yam_contents;
     content_xsize = 3;
@@ -567,7 +594,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
        for(x=0; x<3; x++)
          content_array[i][x][y] = level->yam_content[i][x][y];
   }
-  else if (element == EL_AMOEBE_BD)
+  else if (element == EL_BD_AMOEBA)
   {
     num_contents = 1;
     content_xsize = 1;
@@ -576,7 +603,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     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_LEERRAUM;
+         content_array[i][x][y] = EL_EMPTY;
     content_array[0][0][0] = level->amoeba_content;
   }
   else
@@ -588,7 +615,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     return;
   }
 
-  putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
+  putFile16BitBE(file, element);
   fputc(num_contents, file);
   fputc(content_xsize, file);
   fputc(content_ysize, file);
@@ -598,8 +625,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       putFile16BitInteger(file, content_array[i][x][y],
-                           BYTE_ORDER_BIG_ENDIAN);
+       putFile16BitBE(file, content_array[i][x][y]);
 }
 
 void SaveLevel(int level_nr)
@@ -615,6 +641,8 @@ void SaveLevel(int level_nr)
     return;
   }
 
+  level.file_version = FILE_VERSION_ACTUAL;
+  level.game_version = GAME_VERSION_ACTUAL;
 
   /* check level field for 16-bit elements */
   level.encoding_16bit_field = FALSE;
@@ -639,32 +667,32 @@ void SaveLevel(int level_nr)
   body_chunk_size =
     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
 
-  putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
-  putFileChunk(file, "CAVE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+  putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
 
-  putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
-  WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
+  putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
+  SaveLevel_VERS(file, &level);
 
-  putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
   SaveLevel_HEAD(file, &level);
 
-  putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
   SaveLevel_AUTH(file, &level);
 
-  putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "BODY", body_chunk_size);
   SaveLevel_BODY(file, &level);
 
   if (level.encoding_16bit_yamyam ||
       level.num_yam_contents != STD_ELEMENT_CONTENTS)
   {
-    putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
-    SaveLevel_CNT2(file, &level, EL_MAMPFER);
+    putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
+    SaveLevel_CNT2(file, &level, EL_YAMYAM);
   }
 
   if (level.encoding_16bit_amoeba)
   {
-    putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
-    SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
+    putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
+    SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
   }
 
   fclose(file);
@@ -672,6 +700,35 @@ void SaveLevel(int level_nr)
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
 
+void DumpLevel(struct LevelInfo *level)
+{
+  printf_line('-', 79);
+  printf("Level xxx (file version %06d, game version %06d)\n",
+        level->file_version, level->game_version);
+  printf_line('-', 79);
+
+  printf("Level Author: '%s'\n", level->author);
+  printf("Level Title:  '%s'\n", level->name);
+  printf("\n");
+  printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
+  printf("\n");
+  printf("Level Time:  %d seconds\n", level->time);
+  printf("Gems needed: %d\n", level->gems_needed);
+  printf("\n");
+  printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
+  printf("Time for Wheel:      %d seconds\n", level->time_wheel);
+  printf("Time for Light:      %d seconds\n", level->time_light);
+  printf("Time for Timegate:   %d seconds\n", level->time_timegate);
+  printf("\n");
+  printf("Amoeba Speed: %d\n", level->amoeba_speed);
+  printf("\n");
+  printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
+  printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
+  printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
+
+  printf_line('-', 79);
+}
+
 
 /* ========================================================================= */
 /* tape file functions                                                       */
@@ -682,8 +739,6 @@ static void setTapeInfoToDefaults()
   int i;
 
   /* always start with reliable default values (empty tape) */
-  tape.file_version = FILE_VERSION_ACTUAL;
-  tape.game_version = GAME_VERSION_ACTUAL;
   TapeErase();
 
   /* default values (also for pre-1.2 tapes) with only the first player */
@@ -705,7 +760,8 @@ static void setTapeInfoToDefaults()
 
 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
 {
-  ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
+  tape->file_version = getFileVersion(file);
+  tape->game_version = getFileVersion(file);
 
   return chunk_size;
 }
@@ -714,16 +770,15 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
 {
   int i;
 
-  tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  tape->date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  tape->length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  tape->random_seed = getFile32BitBE(file);
+  tape->date        = getFile32BitBE(file);
+  tape->length      = getFile32BitBE(file);
 
   /* read header fields that are new since version 1.2 */
   if (tape->file_version >= FILE_VERSION_1_2)
   {
     byte store_participating_players = fgetc(file);
-
-    ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
+    int engine_version;
 
     /* since version 1.2, tapes store which players participate in the tape */
     tape->num_participating_players = 0;
@@ -737,6 +792,12 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
        tape->num_participating_players++;
       }
     }
+
+    ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
+
+    engine_version = getFileVersion(file);
+    if (engine_version > 0)
+      tape->engine_version = engine_version;
   }
 
   return chunk_size;
@@ -826,9 +887,8 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
-void LoadTape(int level_nr)
+void LoadTapeFromFilename(char *filename)
 {
-  char *filename = getTapeFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char chunk_name[CHUNK_ID_LEN + 1];
   FILE *file;
@@ -840,12 +900,12 @@ void LoadTape(int level_nr)
   if (!(file = fopen(filename, MODE_READ)))
     return;
 
-  getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+  getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
   {
-    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+    getFile32BitBE(file);              /* not used */
 
-    getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+    getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "TAPE") != 0)
     {
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
@@ -900,7 +960,7 @@ void LoadTape(int level_nr)
       {  NULL,  0,                     NULL }
     };
 
-    while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
+    while (getFileChunkBE(file, chunk_name, &chunk_size))
     {
       int i = 0;
 
@@ -944,6 +1004,19 @@ void LoadTape(int level_nr)
   tape.length_seconds = GetTapeLength();
 }
 
+void LoadTape(int level_nr)
+{
+  char *filename = getTapeFilename(level_nr);
+
+  LoadTapeFromFilename(filename);
+}
+
+static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
+{
+  putFileVersion(file, tape->file_version);
+  putFileVersion(file, tape->game_version);
+}
+
 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 {
   int i;
@@ -954,13 +1027,16 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
     if (tape->player_participates[i])
       store_participating_players |= (1 << i);
 
-  putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
-  putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
-  putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
+  putFile32BitBE(file, tape->random_seed);
+  putFile32BitBE(file, tape->date);
+  putFile32BitBE(file, tape->length);
 
   fputc(store_participating_players, file);
 
+  /* unused bytes not at the end here for 4-byte alignment of engine_version */
   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
+
+  putFileVersion(file, tape->engine_version);
 }
 
 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
@@ -1002,6 +1078,9 @@ void SaveTape(int level_nr)
     return;
   }
 
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
+
   /* count number of participating players  */
   for(i=0; i<MAX_PLAYERS; i++)
     if (tape.player_participates[i])
@@ -1009,16 +1088,16 @@ void SaveTape(int level_nr)
 
   body_chunk_size = (num_participating_players + 1) * tape.length;
 
-  putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
-  putFileChunk(file, "TAPE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+  putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
 
-  putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
-  WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
+  putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
+  SaveTape_VERS(file, &tape);
 
-  putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
   SaveTape_HEAD(file, &tape);
 
-  putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "BODY", body_chunk_size);
   SaveTape_BODY(file, &tape);
 
   fclose(file);
@@ -1041,17 +1120,18 @@ void DumpTape(struct TapeInfo *tape)
     return;
   }
 
-  printf("\n");
-  printf("-------------------------------------------------------------------------------\n");
-  printf("Tape of Level %d (file version %06d, game version %06d\n",
+  printf_line('-', 79);
+  printf("Tape of Level %03d (file version %06d, game version %06d)\n",
         tape->level_nr, tape->file_version, tape->game_version);
-  printf("-------------------------------------------------------------------------------\n");
+  printf_line('-', 79);
 
   for(i=0; i<tape->length; i++)
   {
     if (i >= MAX_TAPELEN)
       break;
 
+    printf("%03d: ", i);
+
     for(j=0; j<MAX_PLAYERS; j++)
     {
       if (tape->player_participates[j])
@@ -1072,7 +1152,7 @@ void DumpTape(struct TapeInfo *tape)
     printf("(%03d)\n", tape->pos[i].delay);
   }
 
-  printf("-------------------------------------------------------------------------------\n");
+  printf_line('-', 79);
 }
 
 
@@ -1162,55 +1242,76 @@ void SaveScore(int level_nr)
 /* setup file functions                                                      */
 /* ========================================================================= */
 
-#define TOKEN_STR_PLAYER_PREFIX                "player_"
+#define TOKEN_STR_PLAYER_PREFIX                        "player_"
 
 /* global setup */
-#define SETUP_TOKEN_PLAYER_NAME                0
-#define SETUP_TOKEN_SOUND              1
-#define SETUP_TOKEN_SOUND_LOOPS                2
-#define SETUP_TOKEN_SOUND_MUSIC                3
-#define SETUP_TOKEN_SOUND_SIMPLE       4
-#define SETUP_TOKEN_TOONS              5
-#define SETUP_TOKEN_SCROLL_DELAY       6
-#define SETUP_TOKEN_SOFT_SCROLLING     7
-#define SETUP_TOKEN_FADING             8
-#define SETUP_TOKEN_AUTORECORD         9
-#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 NUM_GLOBAL_SETUP_TOKENS                16
+#define SETUP_TOKEN_PLAYER_NAME                        0
+#define SETUP_TOKEN_SOUND                      1
+#define SETUP_TOKEN_SOUND_LOOPS                        2
+#define SETUP_TOKEN_SOUND_MUSIC                        3
+#define SETUP_TOKEN_SOUND_SIMPLE               4
+#define SETUP_TOKEN_TOONS                      5
+#define SETUP_TOKEN_SCROLL_DELAY               6
+#define SETUP_TOKEN_SOFT_SCROLLING             7
+#define SETUP_TOKEN_FADING                     8
+#define SETUP_TOKEN_AUTORECORD                 9
+#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
+
+/* 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 NUM_EDITOR_SETUP_TOKENS                        9
 
 /* shortcut setup */
-#define SETUP_TOKEN_SAVE_GAME          0
-#define SETUP_TOKEN_LOAD_GAME          1
+#define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
+#define SETUP_TOKEN_SHORTCUT_LOAD_GAME         1
+#define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE      2
 
-#define NUM_SHORTCUT_SETUP_TOKENS      2
+#define NUM_SHORTCUT_SETUP_TOKENS              3
 
 /* player setup */
-#define SETUP_TOKEN_USE_JOYSTICK       0
-#define SETUP_TOKEN_JOY_DEVICE_NAME    1
-#define SETUP_TOKEN_JOY_XLEFT          2
-#define SETUP_TOKEN_JOY_XMIDDLE                3
-#define SETUP_TOKEN_JOY_XRIGHT         4
-#define SETUP_TOKEN_JOY_YUPPER         5
-#define SETUP_TOKEN_JOY_YMIDDLE                6
-#define SETUP_TOKEN_JOY_YLOWER         7
-#define SETUP_TOKEN_JOY_SNAP           8
-#define SETUP_TOKEN_JOY_BOMB           9
-#define SETUP_TOKEN_KEY_LEFT           10
-#define SETUP_TOKEN_KEY_RIGHT          11
-#define SETUP_TOKEN_KEY_UP             12
-#define SETUP_TOKEN_KEY_DOWN           13
-#define SETUP_TOKEN_KEY_SNAP           14
-#define SETUP_TOKEN_KEY_BOMB           15
-
-#define NUM_PLAYER_SETUP_TOKENS                16
+#define SETUP_TOKEN_PLAYER_USE_JOYSTICK                0
+#define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME     1
+#define SETUP_TOKEN_PLAYER_JOY_XLEFT           2
+#define SETUP_TOKEN_PLAYER_JOY_XMIDDLE         3
+#define SETUP_TOKEN_PLAYER_JOY_XRIGHT          4
+#define SETUP_TOKEN_PLAYER_JOY_YUPPER          5
+#define SETUP_TOKEN_PLAYER_JOY_YMIDDLE         6
+#define SETUP_TOKEN_PLAYER_JOY_YLOWER          7
+#define SETUP_TOKEN_PLAYER_JOY_SNAP            8
+#define SETUP_TOKEN_PLAYER_JOY_BOMB            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 NUM_PLAYER_SETUP_TOKENS                        16
 
 static struct SetupInfo si;
+static struct SetupEditorInfo sei;
 static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
 
@@ -1231,15 +1332,36 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.team_mode,                "team_mode"                     },
   { TYPE_SWITCH, &si.handicap,         "handicap"                      },
   { TYPE_SWITCH, &si.time_limit,       "time_limit"                    },
+  { TYPE_SWITCH, &si.fullscreen,       "fullscreen"                    },
   { TYPE_SWITCH, &si.ask_on_escape,    "ask_on_escape"                 },
-  { TYPE_SWITCH, &si.fullscreen,       "fullscreen"                    }
+  { TYPE_STRING, &si.graphics_set,     "graphics_set"                  },
+  { TYPE_STRING, &si.sounds_set,       "sounds_set"                    },
+  { TYPE_STRING, &si.music_set,                "music_set"                     },
+  { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics"        },
+  { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"  },
+  { TYPE_SWITCH, &si.override_level_music,    "override_level_music"   },
+};
+
+static struct TokenInfo editor_setup_tokens[] =
+{
+  /* shortcut setup */
+  { TYPE_SWITCH, &sei.el_boulderdash,  "editor.el_boulderdash"         },
+  { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine"        },
+  { TYPE_SWITCH, &sei.el_more,         "editor.el_more"                },
+  { TYPE_SWITCH, &sei.el_sokoban,      "editor.el_sokoban"             },
+  { TYPE_SWITCH, &sei.el_supaplex,     "editor.el_supaplex"            },
+  { TYPE_SWITCH, &sei.el_diamond_caves,        "editor.el_diamond_caves"       },
+  { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"     },
+  { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
+  { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
 };
 
 static struct TokenInfo shortcut_setup_tokens[] =
 {
   /* shortcut setup */
-  { TYPE_KEY_X11, &ssi.save_game,              "shortcut.save_game"    },
-  { TYPE_KEY_X11, &ssi.load_game,              "shortcut.load_game"    }
+  { TYPE_KEY_X11, &ssi.save_game,      "shortcut.save_game"            },
+  { TYPE_KEY_X11, &ssi.load_game,      "shortcut.load_game"            },
+  { TYPE_KEY_X11, &ssi.toggle_pause,   "shortcut.toggle_pause"         }
 };
 
 static struct TokenInfo player_setup_tokens[] =
@@ -1287,8 +1409,26 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->fullscreen = FALSE;
   si->ask_on_escape = TRUE;
 
+  si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
+  si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
+  si->music_set = getStringCopy(MUSIC_SUBDIR);
+  si->override_level_graphics = FALSE;
+  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 = FALSE;
+
   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
+  si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
 
   for (i=0; i<MAX_PLAYERS; i++)
   {
@@ -1325,6 +1465,13 @@ static void decodeSetupFileList(struct SetupFileList *setup_file_list)
                 getTokenValue(setup_file_list, global_setup_tokens[i].text));
   setup = si;
 
+  /* editor setup */
+  sei = setup.editor;
+  for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
+    setSetupInfo(editor_setup_tokens, i,
+                getTokenValue(setup_file_list,editor_setup_tokens[i].text));
+  setup.editor = sei;
+
   /* shortcut setup */
   ssi = setup.shortcut;
   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
@@ -1409,13 +1556,20 @@ void SaveSetup()
   si = setup;
   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
   {
-    fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
-
     /* just to make things nicer :) */
-    if (i == SETUP_TOKEN_PLAYER_NAME)
+    if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
+       i == SETUP_TOKEN_GRAPHICS_SET)
       fprintf(file, "\n");
+
+    fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
   }
 
+  /* editor setup */
+  sei = setup.editor;
+  fprintf(file, "\n");
+  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");