added space-efficient loading of Supaplex demos to internal tape structure
[rocksndiamonds.git] / src / files.c
index 4fd17038204a1dce81521c71a3f83df4aa0d4735..83749a573d4f62489989211475bd53dfdf9103b0 100644 (file)
@@ -3775,10 +3775,20 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
   level_sp->header.DemoRandomSeed = tape.random_seed;
 
   demo->length = 0;
+
   for (i = 0; i < tape.length; i++)
   {
     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
     int demo_repeat = tape.pos[i].delay;
+    int demo_entries = (demo_repeat + 15) / 16;
+
+    if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
+           SP_MAX_TAPE_LEN);
+
+      break;
+    }
 
     for (j = 0; j < demo_repeat / 16; j++)
       demo->data[demo->length++] = 0xf0 | demo_action;
@@ -3787,8 +3797,6 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
   }
 
-  demo->data[demo->length++] = 0xff;
-
   demo->is_available = TRUE;
 }
 
@@ -3808,20 +3816,52 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     return;
 
   tape.level_nr = demo->level_nr;      /* (currently not used) */
-  tape.length = demo->length - 1;      /* without "end of demo" byte */
   tape.random_seed = level_sp->header.DemoRandomSeed;
 
   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
 
-  for (i = 0; i < demo->length - 1; i++)
+  tape.length = 0;
+  tape.pos[tape.length].delay = 0;
+
+  for (i = 0; i < demo->length; i++)
   {
     int demo_action = demo->data[i] & 0x0f;
     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+    int tape_action = map_key_SP_to_RND(demo_action);
+    int tape_repeat = demo_repeat + 1;
+    int tape_entries = 1;      // one new tape entry may be added
+
+    if (tape.length + tape_entries >= MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+           MAX_TAPE_LEN);
+
+      break;
+    }
 
-    tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
-    tape.pos[i].delay = demo_repeat + 1;
+    if (tape.pos[tape.length].delay > 0)       /* already stored action */
+    {
+      if (tape.pos[tape.length].action[0] != tape_action ||
+         tape.pos[tape.length].delay + tape_repeat >= 256)
+      {
+       tape.length++;
+       tape.pos[tape.length].delay = 0;
+      }
+      else
+      {
+       tape.pos[tape.length].delay += tape_repeat;
+      }
+    }
+
+    if (tape.pos[tape.length].delay == 0)      /* store new action */
+    {
+      tape.pos[tape.length].action[0] = tape_action;
+      tape.pos[tape.length].delay = tape_repeat;
+    }
   }
 
+  tape.length++;
+
   tape.length_frames  = GetTapeLengthFrames();
   tape.length_seconds = GetTapeLengthSeconds();
 }