rnd-20040303-B-src
[rocksndiamonds.git] / src / files.c
index 34e3d3acebb9f2f3af0d013718574c7eae867e39..bf3b77e7d063e9662243e6afdd0dbeeeb2a19d69 100644 (file)
@@ -29,7 +29,7 @@
 #define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
 #define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
 #define LEVEL_HEADER_SIZE      80      /* size of level file header  */
-#define LEVEL_HEADER_UNUSED    8       /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED    7       /* unused level header bytes  */
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
 #define LEVEL_CHUNK_CNT3_HEADER        16      /* size of level CNT3 header  */
@@ -92,7 +92,10 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->can_change = FALSE;
 
   change->events = CE_BITMASK_DEFAULT;
-  change->sides = CH_SIDE_ANY;
+
+  change->trigger_player = CH_PLAYER_ANY;
+  change->trigger_side = CH_SIDE_ANY;
+  change->trigger_page = CH_PAGE_ANY;
 
   change->target_element = EL_EMPTY_SPACE;
 
@@ -159,7 +162,9 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->use_spring_bug = FALSE;
 
-  level->can_move_into_acid = ~0;      /* everything can move into acid */
+  level->can_move_into_acid_bits = ~0; /* everything can move into acid */
+
+  level->use_step_counter = FALSE;
 
   level->use_custom_template = FALSE;
 
@@ -284,7 +289,7 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   BorderElement = EL_STEELWALL;
 
-  level->no_level_file = FALSE;
+  level->no_valid_file = FALSE;
 
   if (leveldir_current == NULL)                /* only when dumping level */
     return;
@@ -544,7 +549,7 @@ static struct LevelFileInfo *getLevelFileInfo(int nr)
 
 int getMappedElement(int element)
 {
-  /* map some (historic, now obsolete) elements */
+  /* remap some (historic, now obsolete) elements */
 
 #if 1
   switch (element)
@@ -605,6 +610,32 @@ int getMappedElement(int element)
   return element;
 }
 
+int getMappedElementByVersion(int element, int game_version)
+{
+  /* remap some elements due to certain game version */
+
+  if (game_version <= VERSION_IDENT(2,2,0,0))
+  {
+    /* map game font elements */
+    element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
+              element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
+              element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
+              element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
+  }
+
+  if (game_version < VERSION_IDENT(3,0,0,0))
+  {
+    /* map Supaplex gravity tube elements */
+    element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
+              element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
+              element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
+              element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
+              element);
+  }
+
+  return element;
+}
+
 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
 {
   level->file_version = getFileVersion(file);
@@ -652,7 +683,9 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
 
   level->use_spring_bug                = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
-  level->can_move_into_acid    = getFile16BitBE(file);
+  level->can_move_into_acid_bits = getFile16BitBE(file);
+
+  level->use_step_counter      = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
@@ -1069,13 +1102,23 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
     change->can_change = getFile8Bit(file);
 
-    change->sides = getFile8Bit(file);
+    change->trigger_side = getFile8Bit(file);
+
+#if 1
+    change->trigger_player = getFile8Bit(file);
+    change->trigger_page = getFile8Bit(file);
+
+    change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
+                           CH_PAGE_ANY : (1 << change->trigger_page));
 
-    if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
-      change->sides = CH_SIDE_ANY;
+    /* some free bytes for future change property values and padding */
+    ReadUnusedBytesFromFile(file, 6);
+
+#else
 
     /* some free bytes for future change property values and padding */
     ReadUnusedBytesFromFile(file, 8);
+#endif
   }
 
   /* mark this custom element as modified */
@@ -1139,7 +1182,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     if (level != &level_template)
       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
@@ -1155,6 +1198,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "CAVE") != 0)
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
       fclose(file);
       return;
@@ -1169,6 +1214,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
       fclose(file);
       return;
@@ -1176,6 +1223,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
     {
+      level->no_valid_file = TRUE;
+
       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
       fclose(file);
       return;
@@ -1589,7 +1638,7 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
@@ -1781,7 +1830,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
 
@@ -1791,7 +1840,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
   /* position file stream to the requested level inside the level package */
   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
   {
-    level->no_level_file = TRUE;
+    level->no_valid_file = TRUE;
 
     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
 
@@ -2042,9 +2091,9 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
     if (level->game_version < VERSION_IDENT(3,0,9,0))
     {
-      int i;
+      int i, j;
 
-      level->can_move_into_acid = 0;   /* nothing can move into acid */
+      level->can_move_into_acid_bits = 0; /* nothing can move into acid */
 
       setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
       setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
@@ -2053,6 +2102,20 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
        SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
+
+      for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+      {
+       int element = EL_CUSTOM_START + i;
+       struct ElementInfo *ei = &element_info[element];
+
+       for (j = 0; j < ei->num_change_pages; j++)
+       {
+         struct ElementChangeInfo *change = &ei->change_page[j];
+
+         change->trigger_player = CH_PLAYER_ANY;
+         change->trigger_page = CH_PAGE_ANY;
+       }
+      }
     }
   }
   else
@@ -2099,7 +2162,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
 {
-  int i, j;
+  int i, j, x, y;
 
   /* map custom element change events that have changed in newer versions
      (these following values were accidentally changed in version 3.0.1) */
@@ -2158,6 +2221,24 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
+  /* correct custom element values (for old levels without these options) */
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    if (ei->access_direction == MV_NO_MOVING)
+      ei->access_direction = MV_ALL_DIRECTIONS;
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      if (change->trigger_side == CH_SIDE_NONE)
+       change->trigger_side = CH_SIDE_ANY;
+    }
+  }
+
 #if 0
   /* set default push delay values (corrected since version 3.0.7-1) */
   if (level->game_version < VERSION_IDENT(3,0,7,1))
@@ -2183,6 +2264,16 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 #endif
 
+  /* map elements that have changed in newer versions */
+  level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
+                                                   level->game_version);
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (x = 0; x < 3; x++)
+      for (y = 0; y < 3; y++)
+       level->yamyam_content[i][x][y] =
+         getMappedElementByVersion(level->yamyam_content[i][x][y],
+                                   level->game_version);
+
   /* initialize element properties for level editor etc. */
   InitElementPropertiesEngine(level->game_version);
 }
@@ -2198,6 +2289,9 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
     {
       int element = level->field[x][y];
 
+#if 1
+      element = getMappedElementByVersion(element, level->game_version);
+#else
       if (level->game_version <= VERSION_IDENT(2,2,0,0))
       {
        /* map game font elements */
@@ -2216,6 +2310,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
                   element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
                   element);
       }
+#endif
 
       level->field[x][y] = element;
     }
@@ -2322,7 +2417,9 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
 
   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
 
-  putFile16BitBE(file, level->can_move_into_acid);
+  putFile16BitBE(file, level->can_move_into_acid_bits);
+
+  putFile8Bit(file, (level->use_step_counter ? 1 : 0));
 
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
@@ -2662,10 +2759,21 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
     putFile8Bit(file, change->can_change);
 
-    putFile8Bit(file, change->sides);
+    putFile8Bit(file, change->trigger_side);
+
+#if 1
+    putFile8Bit(file, change->trigger_player);
+    putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
+                      log_2(change->trigger_page)));
+
+    /* some free bytes for future change property values and padding */
+    WriteUnusedBytesToFile(file, 6);
+
+#else
 
     /* some free bytes for future change property values and padding */
     WriteUnusedBytesToFile(file, 8);
+#endif
   }
 }
 
@@ -2826,6 +2934,13 @@ void SaveLevelTemplate()
 
 void DumpLevel(struct LevelInfo *level)
 {
+  if (level->no_valid_file)
+  {
+    Error(ERR_WARN, "cannot dump -- no valid level file found");
+
+    return;
+  }
+
   printf_line("-", 79);
   printf("Level xxx (file version %08d, game version %08d)\n",
         level->file_version, level->game_version);
@@ -2852,6 +2967,7 @@ void DumpLevel(struct LevelInfo *level)
   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
+  printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
 
   printf_line("-", 79);
 }
@@ -2883,6 +2999,8 @@ static void setTapeInfoToDefaults()
   tape.recording = FALSE;
   tape.playing = FALSE;
   tape.pausing = FALSE;
+
+  tape.no_valid_file = FALSE;
 }
 
 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
@@ -3047,7 +3165,13 @@ void LoadTapeFromFilename(char *filename)
   setTapeInfoToDefaults();
 
   if (!(file = fopen(filename, MODE_READ)))
+  {
+    tape.no_valid_file = TRUE;
+
+    Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
+
     return;
+  }
 
   getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
@@ -3057,6 +3181,8 @@ void LoadTapeFromFilename(char *filename)
     getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "TAPE") != 0)
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
       fclose(file);
       return;
@@ -3071,6 +3197,8 @@ void LoadTapeFromFilename(char *filename)
 
     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
       fclose(file);
       return;
@@ -3078,6 +3206,8 @@ void LoadTapeFromFilename(char *filename)
 
     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
     {
+      tape.no_valid_file = TRUE;
+
       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
       fclose(file);
       return;
@@ -3294,11 +3424,21 @@ void DumpTape(struct TapeInfo *tape)
 {
   int i, j;
 
+#if 1
+  if (tape->no_valid_file)
+  {
+    Error(ERR_WARN, "cannot dump -- no valid tape file found");
+
+    return;
+  }
+#else
   if (TAPE_IS_EMPTY(*tape))
   {
     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
+
     return;
   }
+#endif
 
   printf_line("-", 79);
   printf("Tape of Level %03d (file version %08d, game version %08d)\n",