rnd-20040927-1-src
[rocksndiamonds.git] / src / files.c
index 3989a22a83fc62557b4bf434be9e29b9e287b87b..5a5bf6405538feceb3115a6dd39a4d9deecc1d5d 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    1       /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED    0       /* 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  */
 #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_BD             2
-#define LEVEL_FILE_TYPE_EM             3
-#define LEVEL_FILE_TYPE_SP             4
-#define LEVEL_FILE_TYPE_DX             5
-#define LEVEL_FILE_TYPE_SB             6
-#define LEVEL_FILE_TYPE_DC             7
-
-#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                                                      */
@@ -130,6 +114,10 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   int i, j, x, y;
 
+  level->native_em_level = &native_em_level;
+
+  level->game_engine_type = GAME_ENGINE_TYPE_RND;
+
   level->file_version = FILE_VERSION_ACTUAL;
   level->game_version = GAME_VERSION_ACTUAL;
 
@@ -313,6 +301,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->no_valid_file = FALSE;
 
+  level->changed = FALSE;
+
   if (leveldir_current == NULL)                /* only when dumping level */
     return;
 
@@ -549,6 +539,20 @@ static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
     lfi->type = getFileTypeFromBasename(lfi->basename);
 }
 
+#if 1
+static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
+{
+  /* always start with reliable default values */
+  setFileInfoToDefaults(level_file_info);
+
+  level_file_info->nr = nr;    /* set requested level number */
+
+  determineLevelFileInfo_Filename(level_file_info);
+  determineLevelFileInfo_Filetype(level_file_info);
+}
+
+#else
+
 static struct LevelFileInfo *getLevelFileInfo(int nr)
 {
   static struct LevelFileInfo level_file_info;
@@ -563,7 +567,7 @@ static struct LevelFileInfo *getLevelFileInfo(int nr)
 
   return &level_file_info;
 }
-
+#endif
 
 /* ------------------------------------------------------------------------- */
 /* functions for loading R'n'D level                                         */
@@ -712,6 +716,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   level->can_pass_to_walkable  = (getFile8Bit(file) == 1 ? TRUE : FALSE);
   level->grow_into_diggable    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
+  level->game_engine_type      = getFile8Bit(file);
+
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
   return chunk_size;
@@ -1339,6 +1345,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 /* functions for loading EM level                                            */
 /* ------------------------------------------------------------------------- */
 
+#if 0
+
 static int map_em_element_yam(int element)
 {
   switch (element)
@@ -1651,16 +1659,13 @@ static int map_em_element_field(int element)
 #define EM_LEVEL_XSIZE                 64
 #define EM_LEVEL_YSIZE                 32
 
-static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                        struct LevelFileInfo *level_file_info)
 {
   char *filename = level_file_info->filename;
   FILE *file;
   unsigned char leveldata[EM_LEVEL_SIZE];
   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
-  unsigned char code0 = 0x65;
-  unsigned char code1 = 0x11;
-  boolean level_is_crypted = FALSE;
   int nr = level_file_info->nr;
   int i, x, y;
 
@@ -1673,7 +1678,7 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
     return;
   }
 
-  for(i = 0; i < EM_LEVEL_SIZE; i++)
+  for (i = 0; i < EM_LEVEL_SIZE; i++)
     leveldata[i] = fgetc(file);
 
   fclose(file);
@@ -1684,20 +1689,20 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
   if ((leveldata[0] == 0xf1 ||
        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
   {
-    level_is_crypted = TRUE;
+    unsigned char code0 = 0x65;
+    unsigned char code1 = 0x11;
 
     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 < EM_LEVEL_SIZE; i++)
+    /* decode crypted level data */
+
+    for (i = 0; i < EM_LEVEL_SIZE; i++)
     {
       leveldata[i] ^= code0;
       leveldata[i] -= code1;
 
-      code0  = (code0 + 7) & 0xff;
+      code0 = (code0 + 7) & 0xff;
     }
   }
 
@@ -1730,9 +1735,9 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
 
   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++)
+  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]);
 
@@ -1760,6 +1765,151 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
   level->field[x][y] = EL_PLAYER_2;
 }
 
+#else
+
+void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
+{
+  struct LevelInfo_EM *level_em = level->native_em_level;
+  struct LEVEL *lev = level_em->lev;
+  struct PLAYER *ply1 = level_em->ply1;
+  struct PLAYER *ply2 = level_em->ply2;
+  int i, x, y;
+
+  lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
+  lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
+
+  lev->time_initial = level->time;
+  lev->required_initial = level->gems_needed;
+
+  lev->emerald_score   = level->score[SC_EMERALD];
+  lev->diamond_score   = level->score[SC_DIAMOND];
+  lev->alien_score     = level->score[SC_ROBOT];
+  lev->tank_score      = level->score[SC_SPACESHIP];
+  lev->bug_score       = level->score[SC_BUG];
+  lev->eater_score     = level->score[SC_YAMYAM];
+  lev->nut_score       = level->score[SC_NUT];
+  lev->dynamite_score  = level->score[SC_DYNAMITE];
+  lev->key_score       = level->score[SC_TIME_BONUS];  /* ??? CHECK THIS */
+
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       lev->eater_array[i][y * 3 + x] =
+         map_element_RND_to_EM(level->yamyam_content[i][x][y]);
+
+  lev->ameuba_time             = level->amoeba_speed;
+  lev->wonderwall_time_initial = level->time_magic_wall;
+  lev->wheel_time              = level->time_wheel;
+
+  /* first fill the complete playfield with the default border element */
+  for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
+    for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
+      level_em->cave[x][y] = ZBORDER;
+
+  /* then copy the real level contents from level file into the playfield */
+  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  {
+    int new_element = map_element_RND_to_EM(level->field[x][y]);
+
+    level_em->cave[x + 1][y + 1] = new_element;
+  }
+
+  ply1->x_initial = 0;
+  ply1->y_initial = 0;
+
+  ply2->x_initial = 0;
+  ply2->y_initial = 0;
+
+  /* at last, set the two players at their positions in the playfield */
+  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  {
+    if (level->field[x][y] == EL_PLAYER_1)
+    {
+      ply1->x_initial = x + 1;
+      ply1->y_initial = y + 1;
+    }
+    else if (level->field[x][y] == EL_PLAYER_2)
+    {
+      ply2->x_initial = x + 1;
+      ply2->y_initial = y + 1;
+    }
+  }
+}
+
+void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
+{
+  struct LevelInfo_EM *level_em = level->native_em_level;
+  struct LEVEL *lev = level_em->lev;
+  struct PLAYER *ply1 = level_em->ply1;
+  struct PLAYER *ply2 = level_em->ply2;
+  int i, x, y;
+
+  level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
+  level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
+
+  level->time = lev->time_initial;
+  level->gems_needed = lev->required_initial;
+
+  sprintf(level->name, "Level %d", level->file_info.nr);
+
+  level->score[SC_EMERALD]     = lev->emerald_score;
+  level->score[SC_DIAMOND]     = lev->diamond_score;
+  level->score[SC_ROBOT]       = lev->alien_score;
+  level->score[SC_SPACESHIP]   = lev->tank_score;
+  level->score[SC_BUG]         = lev->bug_score;
+  level->score[SC_YAMYAM]      = lev->eater_score;
+  level->score[SC_NUT]         = lev->nut_score;
+  level->score[SC_DYNAMITE]    = lev->dynamite_score;
+  level->score[SC_TIME_BONUS]  = lev->key_score;       /* ??? CHECK THIS */
+
+  level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
+
+  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_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
+
+  level->amoeba_speed          = lev->ameuba_time;
+  level->time_magic_wall       = lev->wonderwall_time_initial;
+  level->time_wheel            = lev->wheel_time;
+
+  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
+  {
+    int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
+
+    if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
+      new_element = EL_AMOEBA_DEAD;
+
+    level->field[x][y] = new_element;
+  }
+
+  level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
+  level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
+}
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info)
+{
+  if (!LoadNativeLevel_EM(level_file_info->filename))
+    level->no_valid_file = TRUE;
+}
+
+#endif
+
+void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_RND_to_EM(level);
+}
+
+void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_EM_to_RND(level);
+}
+
+
 /* ------------------------------------------------------------------------- */
 /* functions for loading SP level                                            */
 /* ------------------------------------------------------------------------- */
@@ -1911,13 +2061,13 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
   level->time_wheel = 0;
   level->amoeba_content = EL_EMPTY;
 
-  for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = 0;               /* !!! CORRECT THIS !!! */
 
   /* there are no yamyams in supaplex levels */
-  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++)
        level->yamyam_content[i][x][y] = EL_EMPTY;
 }
 
@@ -2118,6 +2268,7 @@ void LoadLevelFromFileInfo(struct LevelInfo *level,
 
     case LEVEL_FILE_TYPE_EM:
       LoadLevelFromFileInfo_EM(level, level_file_info);
+      level->game_engine_type = GAME_ENGINE_TYPE_EM;
       break;
 
     case LEVEL_FILE_TYPE_SP:
@@ -2128,6 +2279,11 @@ void LoadLevelFromFileInfo(struct LevelInfo *level,
       LoadLevelFromFileInfo_RND(level, level_file_info);
       break;
   }
+
+  if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
+    level->game_engine_type = GAME_ENGINE_TYPE_RND;
+
+  CopyNativeLevel_Native_to_RND(level);
 }
 
 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
@@ -2235,10 +2391,14 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
       }
     }
 
-#if 1
+#if 1  /* USE_NEW_BLOCK_STYLE */
     /* blocking the last field when moving was corrected in version 3.1.1 */
     if (level->game_version < VERSION_IDENT(3,1,1,0))
     {
+#if 0
+      printf("::: %d\n", level->block_last_field);
+#endif
+
       /* even "not blocking" was blocking the last field for one frame */
       level->block_delay    = (level->block_last_field    ? 7 : 1);
       level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
@@ -2370,6 +2530,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 
   /* initialize "can_explode" field for old levels which did not store this */
+  /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
   if (level->game_version <= VERSION_IDENT(3,1,0,0))
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2385,6 +2546,22 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
+  /* correct previously hard-coded move delay values for maze runner style */
+  if (level->game_version < VERSION_IDENT(3,1,1,0))
+  {
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
+      {
+       /* previously hard-coded and therefore ignored */
+       element_info[element].move_delay_fixed = 9;
+       element_info[element].move_delay_random = 0;
+      }
+    }
+  }
+
 #if 0
   /* set default push delay values (corrected since version 3.0.7-1) */
   if (level->game_version < VERSION_IDENT(3,0,7,1))
@@ -2477,6 +2654,16 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
 
 void LoadLevelTemplate(int nr)
 {
+#if 1
+  char *filename;
+
+  setLevelFileInfo(&level_template.file_info, nr);
+  filename = level_template.file_info.filename;
+
+  LoadLevelFromFileInfo(&level_template, &level_template.file_info);
+
+#else
+
 #if 1
   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
   char *filename = level_file_info->filename;
@@ -2487,15 +2674,31 @@ void LoadLevelTemplate(int nr)
 
   LoadLevelFromFilename_RND(&level_template, filename);
 #endif
+#endif
 
+#if 1
+  LoadLevel_InitVersion(&level_template, filename);
+  LoadLevel_InitElements(&level_template, filename);
+#else
   LoadLevel_InitVersion(&level, filename);
   LoadLevel_InitElements(&level, filename);
+#endif
 
   ActivateLevelTemplate();
 }
 
 void LoadLevel(int nr)
 {
+#if 1
+  char *filename;
+
+  setLevelFileInfo(&level.file_info, nr);
+  filename = level.file_info.filename;
+
+  LoadLevelFromFileInfo(&level, &level.file_info);
+
+#else
+
 #if 1
   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
   char *filename = level_file_info->filename;
@@ -2505,6 +2708,7 @@ void LoadLevel(int nr)
   char *filename = getLevelFilename(nr);
 
   LoadLevelFromFilename_RND(&level, filename);
+#endif
 #endif
 
   if (level.use_custom_template)
@@ -2570,6 +2774,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
 
+  putFile8Bit(file, level->game_engine_type);
+
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
 
@@ -3597,6 +3803,8 @@ void DumpTape(struct TapeInfo *tape)
   printf_line("-", 79);
   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
         tape->level_nr, tape->file_version, tape->game_version);
+  printf("                  (effective engine version %08d)\n",
+        tape->engine_version);
   printf("Level series identifier: '%s'\n", tape->level_identifier);
   printf_line("-", 79);