rnd-20100218-1-src
[rocksndiamonds.git] / src / game_sp / file.c
index 2aa0ddf5e34e39adff14a286f8874db1dadf28ad..bc828c0ee47fdd08fea0e353294379c6aa079c43 100644 (file)
@@ -43,13 +43,67 @@ void setLevelInfoToDefaults_SP()
     port->FreezeEnemies = 0;
   }
 
-  native_sp_level.demo_available = FALSE;
-  native_sp_level.demo_length = 0;
+  /* set raw header bytes (used for subsequent buffer zone) to "hardware" */
+  for (i = 0; i < SP_HEADER_SIZE; i++)
+    native_sp_level.header_raw_bytes[i] = 0x20;
+
+  native_sp_level.demo.is_available = FALSE;
+  native_sp_level.demo.length = 0;
 }
 
 void copyInternalEngineVars_SP()
 {
+  char *preceding_playfield_memory[] =
+  {
+    "95 89 95 89 95 89 95 89  95 89 3b 8a 3b 8a 3b 8a",        // |..........;.;.;.|
+    "3b 8a 3b 8a 3b 8a 3b 8a  3b 8a e8 8a e8 8a e8 8a",        // |;.;.;.;.;.è.è.è.|
+    "e8 8a e8 8a e8 8a e8 8a  e8 8a b1 8b b1 8b b1 8b",        // |è.è.è.è.è.±.±.±.|
+    "b1 8b b1 8b b1 8b b1 8b  b1 8b 85 8c 85 8c 85 8c",        // |±.±.±.±.±.......|
+    "85 8c 85 8c 85 8c 85 8c  85 8c 5b 8d 5b 8d 5b 8d",        // |..........[.[.[.|
+    "5b 8d 5b 8d 5b 8d 5b 8d  5b 8d 06 8e 06 8e 06 8e",        // |[.[.[.[.[.......|
+    "06 8e 06 8e 06 8e 06 8e  06 8e ac 8e ac 8e ac 8e",        // |..........¬.¬.¬.|
+    "ac 8e ac 8e ac 8e ac 8e  ac 8e 59 8f 59 8f 59 8f",        // |¬.¬.¬.¬.¬.Y.Y.Y.|
+    "59 8f 59 8f 59 8f 59 8f  59 8f 00 00 70 13 00 00",        // |Y.Y.Y.Y.Y...p...|
+    "00 00 e8 17 00 00 00 00  00 00 69 38 00 00 00 00",        // |..è.......i8....|
+    "00 00 00 00 00 00 00 00  00 00 00 00 d0 86 00 00",        // |............Ð...|
+    "b2 34 00 00 00 00 00 00  00 00 8f 8b 1d 34 00 00",        // |²4...........4..|
+    "00 00 00 00 00 00 00 00  23 39 09 09 00 0c 00 08",        // |........#9......|
+    "00 58 00 00 00 00 00 25  77 06 7f 00 00 00 01 00",        // |.X.....%w.......|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 ec 06 26",        // |.............ì.&|
+    "05 00 00 00 00 00 00 01  00 00 00 00 31 32 33 34",        // |............1234|
+    "35 36 37 38 39 30 2d 00  08 00 51 57 45 52 54 59",        // |567890-...QWERTY|
+    "55 49 4f 50 00 00 0a 00  41 53 44 46 47 48 4a 4b",        // |UIOP....ASDFGHJK|
+    "4c 00 00 00 00 00 5a 58  43 56 42 4e 4d 00 00 00",        // |L.....ZXCVBNM...|
+    "00 00 00 20 00 00 00 00  00 00 00 00 00 00 00 00",        // |... ............|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 2e 00 1e 00 31 00",        // |..............1.|
+    "14 00 39 00 1f 00 14 00  18 00 ff ff 01 00 01 4c",        // |..9.......ÿÿ...L|
+    "45 56 45 4c 53 2e 44 41  54 00 00 00 00 00 00 00",        // |EVELS.DAT.......|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00",        // |................|
+    "00 00 00 00",                                     // |....            |
+
+    NULL
+  };
   int i, x, y;
+  int count;
 
   LInfo = native_sp_level.header;
 
@@ -60,13 +114,49 @@ void copyInternalEngineVars_SP()
   FieldMax = (FieldWidth * FieldHeight) + HeaderSize - 1;
   LevelMax = (FieldWidth * FieldHeight) - 1;
 
-  FileMax = FieldMax + native_sp_level.demo_length;
+  FileMax = FieldMax + native_sp_level.demo.length;
 
   PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax + 1 - 1);
   DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
+#if 0
   PlayField16 = REDIM_1D(sizeof(int), -FieldWidth, FieldMax);
+#else
+  PlayField16 = REDIM_1D(sizeof(int), -FieldWidth * 2, FieldMax);
+#endif
+
+#if 1
+
+#if 1
+  /* fill preceding playfield buffer zone with (indestructible) "hardware" */
+  for (i = -FieldWidth * 2; i < -FieldWidth; i++)
+    PlayField16[i] = 0x20;
+#endif
+
+#if 0
+  /* fill preceding playfield buffer zone with (indestructible) "hardware" */
+  for (i = -FieldWidth; i < 0; i++)
+    PlayField16[i] = 0x20;
+#endif
+
+  count = 0;
+  for (y = 0; y < native_sp_level.height; y++)
+    for (x = 0; x < native_sp_level.width; x++)
+      PlayField8[count++] = native_sp_level.playfield[x][y];
+
+  /* add raw header bytes to subsequent playfield buffer zone */
+  for (i = 0; i < SP_HEADER_SIZE; i++)
+    PlayField8[count++] = native_sp_level.header_raw_bytes[i];
+
+  for (i = 0; i < count; i++)
+  {
+    PlayField16[i] = PlayField8[i];
+    DisPlayField[i] = PlayField8[i];
+    PlayField8[i] = 0;
+  }
 
-  for (i = 0, y = 0; y < native_sp_level.height; y++)
+#else
+
+  for (i = 0; y = 0; y < native_sp_level.height; y++)
   {
     for (x = 0; x < native_sp_level.width; x++)
     {
@@ -80,12 +170,16 @@ void copyInternalEngineVars_SP()
     }
   }
 
-  if (native_sp_level.demo_available)
+#endif
+
+  if (native_sp_level.demo.is_available)
   {
     DemoAvailable = True;
 
-    for (i = 0; i < native_sp_level.demo_length; i++)
-      PlayField8[FieldMax + i + 1] = native_sp_level.demo[i];
+    PlayField8[FieldMax + 1] = native_sp_level.demo.level_nr;
+
+    for (i = 0; i < native_sp_level.demo.length; i++)
+      PlayField8[FieldMax + i + 2] = native_sp_level.demo.data[i];
   }
 
   AnimationPosTable = REDIM_1D(sizeof(int), 0, LevelMax - 2 * FieldWidth);
@@ -99,12 +193,23 @@ void copyInternalEngineVars_SP()
   GravityFlag = LInfo.InitialGravity;
   FreezeZonks = LInfo.InitialFreezeZonks;
 
+#if 1
+  /* this is set by main game tape code to native random generator directly */
+#else
+
+#if 1
+  printf("::: file.c: copyInternalEngineVars_SP(): RandomSeed = LInfo.DemoRandomSeed\n");
+#endif
+
   RandomSeed = LInfo.DemoRandomSeed;
 
+#endif
+
   LevelLoaded = True;
 }
 
-static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
+static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height,
+                                            boolean demo_available)
 {
   LevelInfoType *header = &native_sp_level.header;
   int i, x, y;
@@ -112,8 +217,8 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
   /* for details of the Supaplex level format, see Herman Perk's Supaplex
      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
 
-  native_sp_level.width  = SP_PLAYFIELD_WIDTH;
-  native_sp_level.height = SP_PLAYFIELD_HEIGHT;
+  native_sp_level.width  = width;
+  native_sp_level.height = height;
 
   /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
   for (y = 0; y < native_sp_level.height; y++)
@@ -191,6 +296,7 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
 
   /* random seed used for recorded demos */
   header->DemoRandomSeed = getFile16BitLE(file);       /* yes, little endian */
+  // header->DemoRandomSeed = getFile16BitBE(file);    /* !!! TEST ONLY !!! */
 
 #if 0
   printf("::: file.c: DemoRandomSeed == %d\n", header->DemoRandomSeed);
@@ -207,15 +313,29 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
     header->InfotronsNeeded &= 0xff;   /* only use low byte -- see above */
   }
 
-  /* also load demo tape, if available */
+  /* read raw level header bytes (96 bytes) */
+
+  fseek(file, -(SP_HEADER_SIZE), SEEK_CUR);    /* rewind file */
+  for (i = 0; i < SP_HEADER_SIZE; i++)
+    native_sp_level.header_raw_bytes[i] = fgetc(file);
+
+  /* also load demo tape, if available (only in single level files) */
 
   if (demo_available)
   {
+    int level_nr = getFile8Bit(file);
+
+    level_nr &= 0x7f;                  /* clear highest bit */
+    level_nr = (level_nr < 1   ? 1   :
+               level_nr > 111 ? 111 : level_nr);
+
+    native_sp_level.demo.level_nr = level_nr;
+
     for (i = 0; i < SP_MAX_TAPE_LEN && !feof(file); i++)
     {
-      native_sp_level.demo[i] = getFile8Bit(file);
+      native_sp_level.demo.data[i] = getFile8Bit(file);
 
-      if (native_sp_level.demo[i] == 0xff)     /* "end of demo" byte */
+      if (native_sp_level.demo.data[i] == 0xff)        /* "end of demo" byte */
       {
        i++;
 
@@ -223,12 +343,12 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
       }
     }
 
-    native_sp_level.demo_length = i;
-    native_sp_level.demo_available = (native_sp_level.demo_length > 0);
+    native_sp_level.demo.length = i;
+    native_sp_level.demo.is_available = (native_sp_level.demo.length > 0);
   }
 }
 
-boolean LoadNativeLevel_SP(char *filename, int pos)
+boolean LoadNativeLevel_SP(char *filename, int level_pos)
 {
   FILE *file;
   int i, l, x, y;
@@ -240,8 +360,13 @@ boolean LoadNativeLevel_SP(char *filename, int pos)
   boolean reading_multipart_level = FALSE;
   boolean use_empty_level = FALSE;
   LevelInfoType *header = &native_sp_level.header;
-  boolean demo_available = (strSuffix(filename, ".sp") ||
-                           strSuffix(filename, ".SP"));
+  boolean is_single_level_file = (strSuffixLower(filename, ".sp") ||
+                                 strSuffixLower(filename, ".mpx"));
+  boolean demo_available = is_single_level_file;
+  boolean is_mpx_file = strSuffixLower(filename, ".mpx");
+  int file_seek_pos = level_pos * SP_LEVEL_SIZE;
+  int level_width  = SP_PLAYFIELD_WIDTH;
+  int level_height = SP_PLAYFIELD_HEIGHT;
 
   /* always start with reliable default values */
   setLevelInfoToDefaults_SP();
@@ -249,13 +374,76 @@ boolean LoadNativeLevel_SP(char *filename, int pos)
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    Error(ERR_WARN, "cannot open level '%s' -- using empty level", filename);
+    Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename);
 
     return FALSE;
   }
 
+  if (is_mpx_file)
+  {
+    char mpx_chunk_name[4 + 1];
+    int mpx_version;
+    int mpx_level_count;
+    LevelDescriptor *mpx_level_desc;
+
+    getFileChunkBE(file, mpx_chunk_name, NULL);
+
+    if (!strEqual(mpx_chunk_name, "MPX "))
+    {
+      Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level",
+           filename);
+
+      return FALSE;
+    }
+
+    mpx_version = getFile16BitLE(file);
+
+    if (mpx_version != 1)
+    {
+      Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level",
+           filename);
+
+      return FALSE;
+    }
+
+    mpx_level_count = getFile16BitLE(file);
+
+    if (mpx_level_count < 1)
+    {
+      Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level",
+           filename);
+
+      return FALSE;
+    }
+
+    if (level_pos >= mpx_level_count)
+    {
+      Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level",
+           filename);
+
+      return FALSE;
+    }
+
+    mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor));
+
+    for (i = 0; i < mpx_level_count; i++)
+    {
+      LevelDescriptor *ldesc = &mpx_level_desc[i];
+
+      ldesc->Width  = getFile16BitLE(file);
+      ldesc->Height = getFile16BitLE(file);
+      ldesc->OffSet = getFile32BitLE(file);    /* starts with 1, not with 0 */
+      ldesc->Size   = getFile32BitLE(file);
+    }
+
+    level_width  = mpx_level_desc[level_pos].Width;
+    level_height = mpx_level_desc[level_pos].Height;
+
+    file_seek_pos = mpx_level_desc[level_pos].OffSet - 1;
+  }
+
   /* position file stream to the requested level (in case of level package) */
-  if (fseek(file, pos * SP_LEVEL_SIZE, SEEK_SET) != 0)
+  if (fseek(file, file_seek_pos, SEEK_SET) != 0)
   {
     Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
 
@@ -270,12 +458,16 @@ boolean LoadNativeLevel_SP(char *filename, int pos)
      of the level name, the multi-part level consists of only horizontal or
      vertical parts */
 
-  for (l = pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
+  for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
   {
-    LoadNativeLevelFromFileStream_SP(file, demo_available);
+    LoadNativeLevelFromFileStream_SP(file, level_width, level_height,
+                                    demo_available);
 
     /* check if this level is a part of a bigger multi-part level */
 
+    if (is_single_level_file)
+      break;
+
     name_first = header->LevelTitle[0];
     name_last  = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
 
@@ -287,13 +479,20 @@ boolean LoadNativeLevel_SP(char *filename, int pos)
       ((name_first == '?' || name_first == '1') &&
        (name_last  == '?' || name_last  == '1'));
 
-    /* correct leading multipart level meta information in level name */
-    for (i = 0; i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first; i++)
-      header->LevelTitle[i] = '-';
-
-    /* correct trailing multipart level meta information in level name */
-    for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && header->LevelTitle[i] == name_last; i--)
-      header->LevelTitle[i] = '-';
+    if (is_multipart_level)
+    {
+      /* correct leading multipart level meta information in level name */
+      for (i = 0;
+          i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first;
+          i++)
+       header->LevelTitle[i] = '-';
+
+      /* correct trailing multipart level meta information in level name */
+      for (i = SP_LEVEL_NAME_LEN - 1;
+          i >= 0 && header->LevelTitle[i] == name_last;
+          i--)
+       header->LevelTitle[i] = '-';
+    }
 
     /* ---------- check for normal single level ---------- */