added Android project files
[rocksndiamonds.git] / src / tape.c
index 0c389a6b85333b2216ed8d968d5e0d3bbe157f0c..43c3c30699807efed1332e53488ab6adbf02ae5a 100644 (file)
 
 #define DEBUG_TAPE_WHEN_PLAYING                        FALSE
 
-/* tape button identifiers */
+// tape button identifiers
 #define TAPE_CTRL_ID_EJECT                     0
 #define TAPE_CTRL_ID_EXTRA                     1
 #define TAPE_CTRL_ID_STOP                      2
 #define TAPE_CTRL_ID_PAUSE                     3
 #define TAPE_CTRL_ID_RECORD                    4
 #define TAPE_CTRL_ID_PLAY                      5
+#define TAPE_CTRL_ID_INSERT_SOLUTION           6
+#define TAPE_CTRL_ID_PLAY_SOLUTION             7
 
-#define NUM_TAPE_BUTTONS                       6
+#define NUM_TAPE_BUTTONS                       8
 
-/* values for tape handling */
+// values for tape handling
 #define TAPE_PAUSE_SECONDS_BEFORE_DEATH                5
 
-/* forward declaration for internal use */
+// forward declaration for internal use
 static void HandleTapeButtons(struct GadgetInfo *);
-static void TapeStopWarpForward();
+static void TapeStopWarpForward(void);
+static float GetTapeLengthSecondsFloat(void);
 
 static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
 
 
-/* ========================================================================= */
-/* video tape definitions                                                    */
-/* ========================================================================= */
-
-/* some positions in the video tape control window */
-#define VIDEO_DATE_LABEL_XPOS          (VIDEO_DISPLAY1_XPOS)
-#define VIDEO_DATE_LABEL_YPOS          (VIDEO_DISPLAY1_YPOS)
-#define VIDEO_DATE_LABEL_XSIZE         (VIDEO_DISPLAY_XSIZE)
-#define VIDEO_DATE_LABEL_YSIZE         (VIDEO_DISPLAY_YSIZE)
-#define VIDEO_DATE_XPOS                        (VIDEO_DISPLAY1_XPOS + 2)
-#define VIDEO_DATE_YPOS                        (VIDEO_DISPLAY1_YPOS + 14)
-#define VIDEO_DATE_XSIZE               (VIDEO_DISPLAY_XSIZE)
-#define VIDEO_DATE_YSIZE               16
-#define VIDEO_REC_LABEL_XPOS           (VIDEO_DISPLAY2_XPOS)
-#define VIDEO_REC_LABEL_YPOS           (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_REC_LABEL_XSIZE          20
-#define VIDEO_REC_LABEL_YSIZE          12
-#define VIDEO_REC_SYMBOL_XPOS          (VIDEO_DISPLAY2_XPOS + 20)
-#define VIDEO_REC_SYMBOL_YPOS          (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_REC_SYMBOL_XSIZE         16
-#define VIDEO_REC_SYMBOL_YSIZE         16
-#define VIDEO_PLAY_LABEL_XPOS          (VIDEO_DISPLAY2_XPOS + 65)
-#define VIDEO_PLAY_LABEL_YPOS          (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PLAY_LABEL_XSIZE         22
-#define VIDEO_PLAY_LABEL_YSIZE         12
-#define VIDEO_PLAY_SYMBOL_XPOS         (VIDEO_DISPLAY2_XPOS + 52)
-#define VIDEO_PLAY_SYMBOL_YPOS         (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PLAY_SYMBOL_XSIZE                11
-#define VIDEO_PLAY_SYMBOL_YSIZE                13
-#define VIDEO_PAUSE_LABEL_XPOS         (VIDEO_DISPLAY2_XPOS)
-#define VIDEO_PAUSE_LABEL_YPOS         (VIDEO_DISPLAY2_YPOS + 20)
-#define VIDEO_PAUSE_LABEL_XSIZE                35
-#define VIDEO_PAUSE_LABEL_YSIZE                8
-#define VIDEO_PAUSE_SYMBOL_XPOS                (VIDEO_DISPLAY2_XPOS + 35)
-#define VIDEO_PAUSE_SYMBOL_YPOS                (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PAUSE_SYMBOL_XSIZE       17
-#define VIDEO_PAUSE_SYMBOL_YSIZE       13
-#define VIDEO_TIME_XPOS                        (VIDEO_DISPLAY2_XPOS + 39)
-#define VIDEO_TIME_YPOS                        (VIDEO_DISPLAY2_YPOS + 14)
-#define VIDEO_TIME_XSIZE               50
-#define VIDEO_TIME_YSIZE               16
-
-/* some default values for tape labels and symbols */
-#define VIDEO_LABEL_XPOS               5
-#define VIDEO_LABEL_YPOS               42
-#define VIDEO_LABEL_XSIZE              40
-#define VIDEO_LABEL_YSIZE              28
-#define VIDEO_SYMBOL_XPOS              39
-#define VIDEO_SYMBOL_YPOS              42
-#define VIDEO_SYMBOL_XSIZE             56
-#define VIDEO_SYMBOL_YSIZE             13
-
-/* values for certain tape labels and symbols */
-#define VIDEO_FFWD_LABEL_XPOS          VIDEO_LABEL_XPOS
-#define VIDEO_FFWD_LABEL_YPOS          193
-#define VIDEO_FFWD_LABEL_XSIZE         VIDEO_LABEL_XSIZE
-#define VIDEO_FFWD_LABEL_YSIZE         VIDEO_LABEL_YSIZE
-#define VIDEO_FFWD_SYMBOL_XPOS         VIDEO_SYMBOL_XPOS
-#define VIDEO_FFWD_SYMBOL_YPOS         193
-#define VIDEO_FFWD_SYMBOL_XSIZE                27
-#define VIDEO_FFWD_SYMBOL_YSIZE                VIDEO_SYMBOL_YSIZE
-
-#define VIDEO_PBEND_LABEL_XPOS         VIDEO_LABEL_XPOS
-#define VIDEO_PBEND_LABEL_YPOS         221
-#define VIDEO_PBEND_LABEL_XSIZE                VIDEO_LABEL_XSIZE
-#define VIDEO_PBEND_LABEL_YSIZE                VIDEO_LABEL_YSIZE
-#define VIDEO_PBEND_SYMBOL_XPOS                VIDEO_SYMBOL_XPOS
-#define VIDEO_PBEND_SYMBOL_YPOS                221
-#define VIDEO_PBEND_SYMBOL_XSIZE       27
-#define VIDEO_PBEND_SYMBOL_YSIZE       VIDEO_SYMBOL_YSIZE
-
-#define VIDEO_WARP_LABEL_XPOS          VIDEO_LABEL_XPOS
-#define VIDEO_WARP_LABEL_YPOS          165
-#define VIDEO_WARP_LABEL_XSIZE         VIDEO_LABEL_XSIZE
-#define VIDEO_WARP_LABEL_YSIZE         VIDEO_LABEL_YSIZE
-#define VIDEO_WARP_SYMBOL_XPOS         VIDEO_SYMBOL_XPOS
-#define VIDEO_WARP_SYMBOL_YPOS         165
-#define VIDEO_WARP_SYMBOL_XSIZE                27
-#define VIDEO_WARP_SYMBOL_YSIZE                VIDEO_SYMBOL_YSIZE
-#define VIDEO_WARP2_SYMBOL_XPOS                VIDEO_SYMBOL_XPOS
-#define VIDEO_WARP2_SYMBOL_YPOS                152
-#define VIDEO_WARP2_SYMBOL_XSIZE       27
-#define VIDEO_WARP2_SYMBOL_YSIZE       VIDEO_SYMBOL_YSIZE
-
-#define VIDEO_1STEP_SYMBOL_XPOS                (VIDEO_SYMBOL_XPOS + 18)
-#define VIDEO_1STEP_SYMBOL_YPOS                139
-#define VIDEO_1STEP_SYMBOL_XSIZE       (VIDEO_SYMBOL_XSIZE - 18)
-#define VIDEO_1STEP_SYMBOL_YSIZE       VIDEO_SYMBOL_YSIZE
-
-#define PG_X3(x)                       (DOOR_GFX_PAGEX3 + (x))
-#define PG_X4(x)                       (DOOR_GFX_PAGEX4 + (x))
-#define PG_X5(x)                       (DOOR_GFX_PAGEX5 + (x))
-#define PG_X6(x)                       (DOOR_GFX_PAGEX6 + (x))
-
-#define PG_Y1(y)                       (DOOR_GFX_PAGEY1 + (y))
-#define PG_Y2(y)                       (DOOR_GFX_PAGEY2 + (y))
+// ============================================================================
+// video tape definitions
+// ============================================================================
 
 #define VIDEO_INFO_OFF                 (VIDEO_STATE_DATE_OFF   |       \
                                         VIDEO_STATE_TIME_OFF   |       \
@@ -145,6 +55,8 @@ static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
                                         VIDEO_STATE_REC_OFF    |       \
                                         VIDEO_STATE_PAUSE_OFF  |       \
                                         VIDEO_STATE_FFWD_OFF   |       \
+                                        VIDEO_STATE_WARP_OFF   |       \
+                                        VIDEO_STATE_WARP2_OFF  |       \
                                         VIDEO_STATE_PBEND_OFF  |       \
                                         VIDEO_STATE_1STEP_OFF)
 #define VIDEO_PRESS_OFF                        (VIDEO_PRESS_PLAY_OFF   |       \
@@ -163,6 +75,8 @@ static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
                                         VIDEO_STATE_REC_ON     |       \
                                         VIDEO_STATE_PAUSE_ON   |       \
                                         VIDEO_STATE_FFWD_ON    |       \
+                                        VIDEO_STATE_WARP_ON    |       \
+                                        VIDEO_STATE_WARP2_ON   |       \
                                         VIDEO_STATE_PBEND_ON   |       \
                                         VIDEO_STATE_1STEP_ON)
 #define VIDEO_PRESS_ON                 (VIDEO_PRESS_PLAY_ON    |       \
@@ -184,9 +98,9 @@ static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
 #define NUM_TAPE_FUNCTION_STATES       2
 
 
-/* ========================================================================= */
-/* video display functions                                                   */
-/* ========================================================================= */
+// ============================================================================
+// video display functions
+// ============================================================================
 
 static void DrawVideoDisplay_Graphics(unsigned int state, unsigned int value)
 {
@@ -220,7 +134,7 @@ static void DrawVideoDisplay_Graphics(unsigned int state, unsigned int value)
       { -1,                                    NULL                    },
     },
     {
-      /* (no label for displaying optional frame) */
+      // (no label for displaying optional frame)
       { -1,                                    NULL                    },
       { -1,                                    NULL                    },
     },
@@ -229,16 +143,16 @@ static void DrawVideoDisplay_Graphics(unsigned int state, unsigned int value)
       { IMG_GFX_TAPE_SYMBOL_FAST_FORWARD,      &tape.symbol.fast_forward },
     },
     {
-      { IMG_GFX_TAPE_LABEL_PAUSE_BEFORE_END,   &tape.label.pause_before_end  },
-      { IMG_GFX_TAPE_SYMBOL_PAUSE_BEFORE_END,  &tape.symbol.pause_before_end },
+      { IMG_GFX_TAPE_LABEL_WARP_FORWARD,       &tape.label.warp_forward  },
+      { IMG_GFX_TAPE_SYMBOL_WARP_FORWARD,      &tape.symbol.warp_forward },
     },
     {
       { IMG_GFX_TAPE_LABEL_WARP_FORWARD_BLIND, &tape.label.warp_forward_blind},
       { IMG_GFX_TAPE_SYMBOL_WARP_FORWARD_BLIND, &tape.symbol.warp_forward_blind},
     },
     {
-      { IMG_GFX_TAPE_LABEL_WARP_FORWARD,       &tape.label.warp_forward  },
-      { IMG_GFX_TAPE_SYMBOL_WARP_FORWARD,      &tape.symbol.warp_forward },
+      { IMG_GFX_TAPE_LABEL_PAUSE_BEFORE_END,   &tape.label.pause_before_end  },
+      { IMG_GFX_TAPE_SYMBOL_PAUSE_BEFORE_END,  &tape.symbol.pause_before_end },
     },
     {
       { IMG_GFX_TAPE_LABEL_SINGLE_STEP,                &tape.label.single_step  },
@@ -246,11 +160,11 @@ static void DrawVideoDisplay_Graphics(unsigned int state, unsigned int value)
     },
   };
 
-  for (k = 0; k < NUM_TAPE_FUNCTION_STATES; k++)       /* on or off states */
+  for (k = 0; k < NUM_TAPE_FUNCTION_STATES; k++)       // on or off states
   {
-    for (i = 0; i < NUM_TAPE_FUNCTIONS; i++)           /* record, play, ... */
+    for (i = 0; i < NUM_TAPE_FUNCTIONS; i++)           // record, play, ...
     {
-      for (j = 0; j < NUM_TAPE_FUNCTION_PARTS; j++)    /* label or symbol */
+      for (j = 0; j < NUM_TAPE_FUNCTION_PARTS; j++)    // label or symbol
       {
        int graphic = video_pos[i][j].graphic;
        struct XY *pos = video_pos[i][j].pos;
@@ -272,20 +186,20 @@ static void DrawVideoDisplay_Graphics(unsigned int state, unsigned int value)
          if (value == skip_value)
            continue;
 
-         if (k == 1)           /* on */
+         if (k == 1)           // on
          {
            gd_bitmap = gfx->bitmap;
            gd_x = gfx->src_x;
            gd_y = gfx->src_y;
          }
-         else                  /* off */
+         else                  // off
          {
            gd_bitmap = gfx_bg->bitmap;
            gd_x = gfx_bg->src_x + pos->x;
            gd_y = gfx_bg->src_y + pos->y;
          }
 
-         /* some tape graphics may be undefined -- only draw if defined */
+         // some tape graphics may be undefined -- only draw if defined
          if (gd_bitmap != NULL)
            BlitBitmap(gd_bitmap, drawto, gd_x, gd_y, gfx->width, gfx->height,
                       VX + pos->x, VY + pos->y);
@@ -435,27 +349,65 @@ void DrawVideoDisplay(unsigned int state, unsigned int value)
   DrawVideoDisplay_DateTime(state, value);
 }
 
-void DrawVideoDisplayLabel(unsigned int state)
+static void DrawVideoDisplayLabel(unsigned int state)
 {
   DrawVideoDisplay(state, VIDEO_DISPLAY_LABEL_ONLY);
 }
 
-void DrawVideoDisplaySymbol(unsigned int state)
+static void DrawVideoDisplaySymbol(unsigned int state)
 {
   DrawVideoDisplay(state, VIDEO_DISPLAY_SYMBOL_ONLY);
 }
 
-void DrawCompleteVideoDisplay()
+static void DrawVideoDisplayCurrentState(void)
+{
+  int state = 0;
+
+  DrawVideoDisplay(VIDEO_STATE_OFF, 0);
+
+  if (tape.pausing)
+    state |= VIDEO_STATE_PAUSE_ON;
+
+  if (tape.recording)
+  {
+    state |= VIDEO_STATE_REC_ON;
+
+    if (tape.single_step)
+      state |= VIDEO_STATE_1STEP_ON;
+  }
+  else if (tape.playing)
+  {
+    state |= VIDEO_STATE_PLAY_ON;
+
+    if (!tape.pausing)
+    {
+      if (tape.deactivate_display)
+       state |= VIDEO_STATE_WARP2_ON;
+      else if (tape.warp_forward)
+       state |= VIDEO_STATE_WARP_ON;
+      else if (tape.fast_forward)
+       state |= VIDEO_STATE_FFWD_ON;
+
+      if (tape.pause_before_end)
+       state |= VIDEO_STATE_PBEND_ON;
+    }
+  }
+
+  // draw labels and symbols separately to prevent labels overlapping symbols
+  DrawVideoDisplayLabel(state);
+  DrawVideoDisplaySymbol(state);
+}
+
+void DrawCompleteVideoDisplay(void)
 {
   struct GraphicInfo *g_tape = &graphic_info[IMG_BACKGROUND_TAPE];
 
-  /* draw tape background */
+  // draw tape background
   BlitBitmap(g_tape->bitmap, drawto, g_tape->src_x, g_tape->src_y,
             gfx.vxsize, gfx.vysize, gfx.vx, gfx.vy);
 
-  /* draw tape buttons (forced) */
-  UnmapTapeButtons();
-  MapTapeButtons();
+  // draw tape buttons (forced)
+  RedrawOrRemapTapeButtons();
 
   DrawVideoDisplay(VIDEO_ALL_OFF, 0);
 
@@ -490,7 +442,7 @@ void DrawCompleteVideoDisplay()
             0, 0);
 }
 
-void TapeDeactivateDisplayOn()
+void TapeDeactivateDisplayOn(void)
 {
   SetDrawDeactivationMask(REDRAW_FIELD);
   audio.sound_deactivated = TRUE;
@@ -509,9 +461,49 @@ void TapeDeactivateDisplayOff(boolean redraw_display)
 }
 
 
-/* ========================================================================= */
-/* tape control functions                                                    */
-/* ========================================================================= */
+// ============================================================================
+// tape logging functions
+// ============================================================================
+
+static void PrintTapeReplayProgress(boolean replay_finished)
+{
+  static unsigned int counter_last = -1;
+  unsigned int counter = Counter();
+  unsigned int counter_seconds  = counter / 1000;
+
+  if (!replay_finished)
+  {
+    unsigned int counter_delay = 50;
+
+    if (counter > counter_last + counter_delay)
+    {
+      PrintNoLog("\r");
+      PrintNoLog("Level %03d [%02d:%02d]: [%02d:%02d] - playing tape ... ",
+                level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+                TapeTime / 60, TapeTime % 60);
+
+      counter_last = counter;
+    }
+  }
+  else
+  {
+    float tape_length_seconds = GetTapeLengthSecondsFloat();
+
+    PrintNoLog("\r");
+    Print("Level %03d [%02d:%02d]: (%02d:%02d.%03d / %.2f %%) - %s.\n",
+         level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+         counter_seconds / 60, counter_seconds % 60, counter % 1000,
+         (float)counter / tape_length_seconds / 10,
+         tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
+
+    counter_last = -1;
+  }
+}
+
+
+// ============================================================================
+// tape control functions
+// ============================================================================
 
 void TapeSetDateFromEpochSeconds(time_t epoch_seconds)
 {
@@ -520,12 +512,12 @@ void TapeSetDateFromEpochSeconds(time_t epoch_seconds)
   tape.date = 10000 * (lt->tm_year % 100) + 100 * lt->tm_mon + lt->tm_mday;
 }
 
-void TapeSetDateFromNow()
+void TapeSetDateFromNow(void)
 {
   TapeSetDateFromEpochSeconds(time(NULL));
 }
 
-void TapeErase()
+void TapeErase(void)
 {
   int i;
 
@@ -554,13 +546,15 @@ void TapeErase()
 
   tape.centered_player_nr_next = -1;
   tape.set_centered_player = FALSE;
+
+  tape.use_mouse = (level.game_engine_type == GAME_ENGINE_TYPE_MM);
 }
 
-static void TapeRewind()
+static void TapeRewind(void)
 {
   tape.counter = 0;
   tape.delay_played = 0;
-  tape.pause_before_death = FALSE;
+  tape.pause_before_end = FALSE;
   tape.recording = FALSE;
   tape.playing = FALSE;
   tape.fast_forward = FALSE;
@@ -602,25 +596,18 @@ void TapeStartRecording(int random_seed)
 
   SetDrawDeactivationMask(REDRAW_NONE);
   audio.sound_deactivated = FALSE;
+
+  // required here to update video display if tape door is closed
+  if (GetDoorState() & DOOR_CLOSE_2)
+    OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
 }
 
-static void TapeStartGameRecording()
+static void TapeStartGameRecording(void)
 {
-  TapeStartRecording(level.random_seed);
-
-#if defined(NETWORK_AVALIABLE)
-  if (options.network)
-  {
-    SendToServer_StartPlaying();
-
-    return;
-  }
-#endif
-
-  InitGame();
+  StartGameActions(network.enabled, TRUE, level.random_seed);
 }
 
-static void TapeAppendRecording()
+static void TapeAppendRecording(void)
 {
   if (!tape.playing || !tape.pausing)
     return;
@@ -629,7 +616,7 @@ static void TapeAppendRecording()
   tape.playing = FALSE;
   tape.fast_forward = FALSE;
   tape.warp_forward = FALSE;
-  tape.pause_before_death = FALSE;
+  tape.pause_before_end = FALSE;
   tape.deactivate_display = FALSE;
 
   // start recording
@@ -648,22 +635,23 @@ static void TapeAppendRecording()
   UpdateAndDisplayGameControlValues();
 }
 
-void TapeHaltRecording()
+void TapeHaltRecording(void)
 {
-  if (!tape.recording)
-    return;
-
   tape.counter++;
-  tape.pos[tape.counter].delay = 0;
+
+  // initialize delay for next tape entry (to be able to continue recording)
+  if (tape.counter < MAX_TAPE_LEN)
+    tape.pos[tape.counter].delay = 0;
 
   tape.length = tape.counter;
   tape.length_frames = GetTapeLengthFrames();
   tape.length_seconds = GetTapeLengthSeconds();
 }
 
-void TapeStopRecording()
+void TapeStopRecording(void)
 {
-  TapeHaltRecording();
+  if (tape.recording)
+    TapeHaltRecording();
 
   tape.recording = FALSE;
   tape.pausing = FALSE;
@@ -672,34 +660,11 @@ void TapeStopRecording()
   MapTapeEjectButton();
 }
 
-void TapeRecordAction(byte action_raw[MAX_PLAYERS])
+boolean TapeAddAction(byte action[MAX_PLAYERS])
 {
-  byte action[MAX_PLAYERS];
   int i;
 
-  if (!tape.recording)         /* (record action even when tape is paused) */
-    return;
-
-  if (tape.counter >= MAX_TAPE_LEN - 1)
-  {
-    TapeStopRecording();
-    return;
-  }
-
-  for (i = 0; i < MAX_PLAYERS; i++)
-    action[i] = action_raw[i];
-
-  if (tape.set_centered_player)
-  {
-    for (i = 0; i < MAX_PLAYERS; i++)
-      if (tape.centered_player_nr_next == i ||
-         tape.centered_player_nr_next == -1)
-       action[i] |= KEY_SET_FOCUS;
-
-    tape.set_centered_player = FALSE;
-  }
-
-  if (tape.pos[tape.counter].delay > 0)                /* already stored action */
+  if (tape.pos[tape.counter].delay > 0)                // already stored action
   {
     boolean changed_events = FALSE;
 
@@ -709,6 +674,9 @@ void TapeRecordAction(byte action_raw[MAX_PLAYERS])
 
     if (changed_events || tape.pos[tape.counter].delay >= 255)
     {
+      if (tape.counter >= MAX_TAPE_LEN - 1)
+       return FALSE;
+
       tape.counter++;
       tape.pos[tape.counter].delay = 0;
     }
@@ -716,79 +684,101 @@ void TapeRecordAction(byte action_raw[MAX_PLAYERS])
       tape.pos[tape.counter].delay++;
   }
 
-  if (tape.pos[tape.counter].delay == 0)       /* store new action */
+  if (tape.pos[tape.counter].delay == 0)       // store new action
   {
     for (i = 0; i < MAX_PLAYERS; i++)
       tape.pos[tape.counter].action[i] = action[i];
 
     tape.pos[tape.counter].delay++;
   }
+
+  return TRUE;
 }
 
-void TapeTogglePause(boolean toggle_manual)
+void TapeRecordAction(byte action_raw[MAX_PLAYERS])
 {
-  int state = 0;
+  byte action[MAX_PLAYERS];
+  int i;
 
-  tape.pausing = !tape.pausing;
+  if (!tape.recording)         // (record action even when tape is paused)
+    return;
 
-  if (tape.single_step && toggle_manual)
-    tape.single_step = FALSE;
+  for (i = 0; i < MAX_PLAYERS; i++)
+    action[i] = action_raw[i];
+
+  if (!tape.use_mouse && tape.set_centered_player)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.centered_player_nr_next == i ||
+         tape.centered_player_nr_next == -1)
+       action[i] |= KEY_SET_FOCUS;
 
-  state |= VIDEO_STATE_PAUSE(tape.pausing);
+    tape.set_centered_player = FALSE;
+  }
 
-  if (tape.pause_before_death)
-    state |= VIDEO_STATE_PBEND(!tape.pausing);
-  else if (tape.fast_forward)
-    state |= VIDEO_STATE_FFWD(!tape.pausing);
+  if (!TapeAddAction(action))
+    TapeStopRecording();
+}
 
-  if (tape.playing)
-    state |= VIDEO_STATE_PLAY_ON;
-  else
-    state |= VIDEO_STATE_1STEP(tape.single_step);
+void TapeTogglePause(boolean toggle_mode)
+{
+  if (tape.playing && tape.pausing && (toggle_mode & TAPE_TOGGLE_PLAY_PAUSE))
+  {
+    // continue playing in normal mode
+    tape.fast_forward = FALSE;
+    tape.warp_forward = FALSE;
+    tape.deactivate_display = FALSE;
 
-  DrawVideoDisplay(state, 0);
+    tape.pause_before_end = FALSE;
+  }
 
-  if (tape.warp_forward)
+  tape.pausing = !tape.pausing;
+
+  if (tape.single_step && (toggle_mode & TAPE_TOGGLE_MANUAL))
+    tape.single_step = FALSE;
+
+  DrawVideoDisplayCurrentState();
+
+  if (tape.deactivate_display)
   {
     if (tape.pausing)
-    {
       TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
-    }
     else
-    {
-      if (tape.deactivate_display)
-       TapeDeactivateDisplayOn();
+      TapeDeactivateDisplayOn();
+  }
 
-      DrawVideoDisplaySymbol(VIDEO_STATE_WARP_ON);
-    }
+  if (tape.quick_resume)
+  {
+    tape.quick_resume = FALSE;
 
-    if (tape.quick_resume)
-    {
-      tape.quick_resume = FALSE;
+    TapeStopWarpForward();
+    TapeAppendRecording();
 
-      TapeStopWarpForward();
-      TapeAppendRecording();
+    if (!CheckEngineSnapshotSingle())
+      SaveEngineSnapshotSingle();
 
-      if (!CheckEngineSnapshotSingle())
-       SaveEngineSnapshotSingle();
+    // restart step/move snapshots after quick loading tape
+    SaveEngineSnapshotToListInitial();
 
-      // restart step/move snapshots after quick loading tape
-      SaveEngineSnapshotToListInitial();
-    }
+    // do not map undo/redo buttons after quick loading tape
+    return;
   }
 
-  if (setup.show_snapshot_buttons &&
-      game_status == GAME_MODE_PLAYING &&
-      CheckEngineSnapshotList())
+  if (game_status == GAME_MODE_PLAYING)
   {
-    if (tape.pausing)
-      MapUndoRedoButtons();
-    else if (!tape.single_step)
-      UnmapUndoRedoButtons();
+    if (setup.show_snapshot_buttons && CheckEngineSnapshotList())
+    {
+      if (tape.pausing)
+       MapUndoRedoButtons();
+      else if (!tape.single_step)
+       UnmapUndoRedoButtons();
+    }
+
+    ModifyPauseButtons();
   }
 }
 
-void TapeStartPlaying()
+void TapeStartPlaying(void)
 {
   if (TAPE_IS_EMPTY(tape))
     return;
@@ -811,14 +801,14 @@ void TapeStartPlaying()
   audio.sound_deactivated = FALSE;
 }
 
-static void TapeStartGamePlaying()
+static void TapeStartGamePlaying(void)
 {
   TapeStartPlaying();
 
   InitGame();
 }
 
-void TapeStopPlaying()
+void TapeStopPlaying(void)
 {
   tape.playing = FALSE;
   tape.pausing = FALSE;
@@ -830,7 +820,7 @@ void TapeStopPlaying()
   MapTapeEjectButton();
 }
 
-byte *TapePlayAction()
+byte *TapePlayAction(void)
 {
   int update_delay = FRAMES_PER_SECOND / 2;
   boolean update_video_display = (FrameCounter % update_delay == 0);
@@ -841,15 +831,10 @@ byte *TapePlayAction()
   if (!tape.playing || tape.pausing)
     return NULL;
 
-  if (tape.pause_before_death)  // stop some seconds before end of tape
+  if (tape.pause_before_end)  // stop some seconds before end of tape
   {
     if (TapeTime > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
     {
-      tape.fast_forward = FALSE;
-      tape.pause_before_death = FALSE;
-
-      DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, 0);
-
       TapeStopWarpForward();
       TapeTogglePause(TAPE_TOGGLE_MANUAL);
 
@@ -857,27 +842,38 @@ byte *TapePlayAction()
     }
   }
 
-  if (update_video_display && !tape.deactivate_display)
-  {
-    if (tape.pause_before_death)
-      DrawVideoDisplayLabel(VIDEO_STATE_PBEND(update_draw_label_on));
-    else if (tape.fast_forward)
-      DrawVideoDisplayLabel(VIDEO_STATE_FFWD(update_draw_label_on));
-
-    if (tape.warp_forward)
-      DrawVideoDisplaySymbol(VIDEO_STATE_WARP2_ON);
-  }
-
-  if (tape.counter >= tape.length)     /* end of tape reached */
+  if (tape.counter >= tape.length)     // end of tape reached
   {
     if (tape.warp_forward && !tape.auto_play)
+    {
+      TapeStopWarpForward();
       TapeTogglePause(TAPE_TOGGLE_MANUAL);
+    }
     else
+    {
       TapeStop();
+    }
 
     return NULL;
   }
 
+  if (update_video_display && !tape.deactivate_display)
+  {
+    int state = 0;
+
+    if (tape.warp_forward)
+      state |= VIDEO_STATE_WARP(update_draw_label_on);
+    else if (tape.fast_forward)
+      state |= VIDEO_STATE_FFWD(update_draw_label_on);
+
+    if (tape.pause_before_end)
+      state |= VIDEO_STATE_PBEND(update_draw_label_on);
+
+    // draw labels and symbols separately to prevent labels overlapping symbols
+    DrawVideoDisplayLabel(state);
+    DrawVideoDisplaySymbol(state);
+  }
+
   for (i = 0; i < MAX_PLAYERS; i++)
     action[i] = tape.pos[tape.counter].action[i];
 
@@ -891,16 +887,19 @@ byte *TapePlayAction()
   tape.set_centered_player = FALSE;
   tape.centered_player_nr_next = -999;
 
-  for (i = 0; i < MAX_PLAYERS; i++)
+  if (!tape.use_mouse)
   {
-    if (action[i] & KEY_SET_FOCUS)
+    for (i = 0; i < MAX_PLAYERS; i++)
     {
-      tape.set_centered_player = TRUE;
-      tape.centered_player_nr_next =
-       (tape.centered_player_nr_next == -999 ? i : -1);
-    }
+      if (action[i] & KEY_SET_FOCUS)
+      {
+       tape.set_centered_player = TRUE;
+       tape.centered_player_nr_next =
+         (tape.centered_player_nr_next == -999 ? i : -1);
+      }
 
-    action[i] &= ~KEY_SET_FOCUS;
+      action[i] &= ~KEY_SET_FOCUS;
+    }
   }
 
   tape.delay_played++;
@@ -910,11 +909,17 @@ byte *TapePlayAction()
     tape.delay_played = 0;
   }
 
+  if (tape.auto_play)
+    PrintTapeReplayProgress(FALSE);
+
   return action;
 }
 
-void TapeStop()
+void TapeStop(void)
 {
+  if (tape.pausing)
+    TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
   TapeStopRecording();
   TapeStopPlaying();
 
@@ -928,7 +933,7 @@ void TapeStop()
   }
 }
 
-unsigned int GetTapeLengthFrames()
+unsigned int GetTapeLengthFrames(void)
 {
   unsigned int tape_length_frames = 0;
   int i;
@@ -942,55 +947,46 @@ unsigned int GetTapeLengthFrames()
   return tape_length_frames;
 }
 
-unsigned int GetTapeLengthSeconds()
+unsigned int GetTapeLengthSeconds(void)
 {
   return (GetTapeLengthFrames() * GAME_FRAME_DELAY / 1000);
 }
 
-static void TapeStartWarpForward()
+static float GetTapeLengthSecondsFloat(void)
 {
-  tape.warp_forward = TRUE;
+  return ((float)GetTapeLengthFrames() * GAME_FRAME_DELAY / 1000);
+}
 
-  if (!tape.fast_forward && !tape.pause_before_death)
-  {
-    tape.pausing = FALSE;
-    tape.pause_before_death = TRUE;
-    tape.deactivate_display = TRUE;
+static void TapeStartWarpForward(int mode)
+{
+  tape.fast_forward = (mode & AUTOPLAY_FFWD);
+  tape.warp_forward = (mode & AUTOPLAY_WARP);
+  tape.deactivate_display = (mode & AUTOPLAY_WARP_NO_DISPLAY);
 
+  tape.pausing = FALSE;
+
+  if (tape.deactivate_display)
     TapeDeactivateDisplayOn();
 
-    DrawVideoDisplay(VIDEO_STATE_PBEND_ON, 0);
-    DrawVideoDisplaySymbol(VIDEO_STATE_WARP_ON);
-  }
-  else
-  {
-    DrawVideoDisplaySymbol(VIDEO_STATE_WARP2_ON);
-  }
+  DrawVideoDisplayCurrentState();
 }
 
-static void TapeStopWarpForward()
+static void TapeStopWarpForward(void)
 {
-  int state = VIDEO_STATE_PAUSE(tape.pausing);
-
-  if (tape.deactivate_display)
-    tape.pause_before_death = FALSE;
-
+  tape.fast_forward = FALSE;
   tape.warp_forward = FALSE;
   tape.deactivate_display = FALSE;
 
-  TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
+  tape.pause_before_end = FALSE;
 
-  state |= VIDEO_STATE_WARP_OFF;
-  state |= (tape.pause_before_death ? VIDEO_STATE_PBEND_ON :
-           tape.fast_forward       ? VIDEO_STATE_FFWD_ON :
-           VIDEO_STATE_PLAY_ON);
+  TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
 
-  DrawVideoDisplay(state, 0);
+  DrawVideoDisplayCurrentState();
 }
 
-static void TapeSingleStep()
+static void TapeSingleStep(void)
 {
-  if (options.network)
+  if (network.enabled)
     return;
 
   if (!tape.pausing)
@@ -1001,7 +997,7 @@ static void TapeSingleStep()
   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
 }
 
-void TapeQuickSave()
+void TapeQuickSave(void)
 {
   if (game_status == GAME_MODE_MAIN)
   {
@@ -1014,7 +1010,7 @@ void TapeQuickSave()
     return;
 
   if (tape.recording)
-    TapeHaltRecording();       /* prepare tape for saving on-the-fly */
+    TapeHaltRecording();       // prepare tape for saving on-the-fly
 
   if (TAPE_IS_EMPTY(tape))
   {
@@ -1027,7 +1023,7 @@ void TapeQuickSave()
     SaveEngineSnapshotSingle();
 }
 
-void TapeQuickLoad()
+void TapeQuickLoad(void)
 {
   char *filename = getTapeFilename(level_nr);
 
@@ -1078,11 +1074,11 @@ void TapeQuickLoad()
   if (!TAPE_IS_EMPTY(tape))
   {
     TapeStartGamePlaying();
-    TapeStartWarpForward();
+    TapeStartWarpForward(AUTOPLAY_MODE_WARP_NO_DISPLAY);
 
     tape.quick_resume = TRUE;
   }
-  else /* this should not happen (basically checked above) */
+  else // this should not happen (basically checked above)
   {
     int reopen_door = (game_status == GAME_MODE_PLAYING ? REQ_REOPEN : 0);
 
@@ -1090,25 +1086,60 @@ void TapeQuickLoad()
   }
 }
 
-void InsertSolutionTape()
+boolean hasSolutionTape(void)
 {
-  if (!TAPE_IS_EMPTY(tape))
-    return;
+  boolean tape_file_exists = fileExists(getSolutionTapeFilename(level_nr));
+  boolean level_has_tape = (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
+                           level.native_sp_level->demo.is_available);
 
-  LoadSolutionTape(level_nr);
+  return (tape_file_exists || level_has_tape);
+}
 
-  if (TAPE_IS_EMPTY(tape))
+boolean InsertSolutionTape(void)
+{
+  if (!hasSolutionTape())
+  {
     Request("No solution tape for this level!", REQ_CONFIRM);
 
+    return FALSE;
+  }
+
+  if (!TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  // if tape recorder already contains a tape, remove it without asking
+  TapeErase();
+
+  LoadSolutionTape(level_nr);
+
   DrawCompleteVideoDisplay();
+
+  if (TAPE_IS_EMPTY(tape))
+  {
+    Request("Loading solution tape for this level failed!", REQ_CONFIRM);
+
+    return FALSE;
+  }
+
+  return TRUE;
 }
 
+boolean PlaySolutionTape(void)
+{
+  if (!InsertSolutionTape())
+    return FALSE;
+
+  TapeStartGamePlaying();
 
-/* ------------------------------------------------------------------------- *
- * tape autoplay functions
- * ------------------------------------------------------------------------- */
+  return TRUE;
+}
 
-void AutoPlayTape()
+
+// ----------------------------------------------------------------------------
+// tape autoplay functions
+// ----------------------------------------------------------------------------
+
+void AutoPlayTape(void)
 {
   static LevelDirTree *autoplay_leveldir = NULL;
   static boolean autoplay_initialized = FALSE;
@@ -1121,8 +1152,8 @@ void AutoPlayTape()
 
   if (autoplay_initialized)
   {
-    /* just finished auto-playing tape */
-    printf("%s.\n", tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
+    // just finished auto-playing tape
+    PrintTapeReplayProgress(TRUE);
 
     num_levels_played++;
 
@@ -1154,15 +1185,15 @@ void AutoPlayTape()
 
     autoplay_level_nr = autoplay_leveldir->first_level;
 
-    printf_line("=", 79);
-    printf("Automatically playing level tapes\n");
-    printf_line("-", 79);
-    printf("Level series identifier: '%s'\n", autoplay_leveldir->identifier);
-    printf("Level series name:       '%s'\n", autoplay_leveldir->name);
-    printf("Level series author:     '%s'\n", autoplay_leveldir->author);
-    printf("Number of levels:        %d\n",   autoplay_leveldir->levels);
-    printf_line("=", 79);
-    printf("\n");
+    PrintLine("=", 79);
+    Print("Automatically playing level tapes\n");
+    PrintLine("-", 79);
+    Print("Level series identifier: '%s'\n", autoplay_leveldir->identifier);
+    Print("Level series name:       '%s'\n", autoplay_leveldir->name);
+    Print("Level series author:     '%s'\n", autoplay_leveldir->author);
+    Print("Number of levels:        %d\n",   autoplay_leveldir->levels);
+    PrintLine("=", 79);
+    Print("\n");
 
     for (i = 0; i < MAX_TAPES_PER_SET; i++)
       level_failed[i] = FALSE;
@@ -1179,72 +1210,70 @@ void AutoPlayTape()
 
     TapeErase();
 
-    printf("Level %03d: ", level_nr);
-
     LoadLevel(level_nr);
-    if (level.no_valid_file)
+
+    if (level.no_level_file || level.no_valid_file)
     {
-      printf("(no level)\n");
+      Print("Level %03d: (no level)\n", level_nr);
+
       continue;
     }
 
 #if 0
-    /* ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY */
-    printf("(only testing level)\n");
+    // ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY
+    Print("Level %03d: (only testing level)\n", level_nr);
     continue;
 #endif
 
-    LoadSolutionTape(level_nr);
+    if (options.mytapes)
+      LoadTape(level_nr);
+    else
+      LoadSolutionTape(level_nr);
 
     if (tape.no_valid_file)
     {
       num_tape_missing++;
 
-      printf("(no tape)\n");
+      Print("Level %03d: (no tape)\n", level_nr);
 
       continue;
     }
 
-    printf("playing tape ... ");
+    InitCounter();
 
     TapeStartGamePlaying();
-
-    if (global.autoplay_mode == AUTOPLAY_FFWD)
-      tape.fast_forward = TRUE;
-
-    if (global.autoplay_mode != AUTOPLAY_PLAY)
-      TapeStartWarpForward();
+    TapeStartWarpForward(global.autoplay_mode);
 
     return;
   }
 
-  printf("\n");
-  printf_line("=", 79);
-  printf("Number of levels played: %d\n", num_levels_played);
-  printf("Number of levels solved: %d (%d%%)\n", num_levels_solved,
+  Print("\n");
+  PrintLine("=", 79);
+  Print("Number of levels played: %d\n", num_levels_played);
+  Print("Number of levels solved: %d (%d%%)\n", num_levels_solved,
         (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
-  printf_line("-", 79);
-  printf("Summary (for automatic parsing by scripts):\n");
-  printf("LEVELDIR '%s', SOLVED %d/%d (%d%%)",
+  PrintLine("-", 79);
+  Print("Summary (for automatic parsing by scripts):\n");
+  Print("LEVELDIR '%s', SOLVED %d/%d (%d%%)",
         autoplay_leveldir->identifier, num_levels_solved, num_levels_played,
         (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
 
   if (num_levels_played != num_levels_solved)
   {
-    printf(", FAILED:");
+    Print(", FAILED:");
     for (i = 0; i < MAX_TAPES_PER_SET; i++)
       if (level_failed[i])
-       printf(" %03d", i);
+       Print(" %03d", i);
   }
 
-  printf("\n");
-  printf_line("=", 79);
+  Print("\n");
+  PrintLine("=", 79);
 
   CloseAllAndExit(0);
 }
 
 
-/* ---------- new tape button stuff ---------------------------------------- */
+// ---------- new tape button stuff -------------------------------------------
 
 static struct
 {
@@ -1259,7 +1288,7 @@ static struct
     TAPE_CTRL_ID_EJECT,                        "eject tape"
   },
   {
-    /* (same position as "eject" button) */
+    // (same position as "eject" button)
     IMG_GFX_TAPE_BUTTON_EXTRA,         &tape.button.eject,
     TAPE_CTRL_ID_EXTRA,                        "extra functions"
   },
@@ -1278,16 +1307,25 @@ static struct
   {
     IMG_GFX_TAPE_BUTTON_PLAY,          &tape.button.play,
     TAPE_CTRL_ID_PLAY,                 "play tape"
+  },
+  {
+    IMG_GFX_TAPE_BUTTON_INSERT_SOLUTION,&tape.button.insert_solution,
+    TAPE_CTRL_ID_INSERT_SOLUTION,      "insert solution tape"
+  },
+  {
+    IMG_GFX_TAPE_BUTTON_PLAY_SOLUTION, &tape.button.play_solution,
+    TAPE_CTRL_ID_PLAY_SOLUTION,                "play solution tape"
   }
 };
 
-void CreateTapeButtons()
+void CreateTapeButtons(void)
 {
   int i;
 
   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
   {
-    struct GraphicInfo *gfx = &graphic_info[tapebutton_info[i].graphic];
+    int graphic = tapebutton_info[i].graphic;
+    struct GraphicInfo *gfx = &graphic_info[graphic];
     struct XY *pos = tapebutton_info[i].pos;
     struct GadgetInfo *gi;
     int gd_x = gfx->src_x;
@@ -1297,6 +1335,7 @@ void CreateTapeButtons()
     int id = i;
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_IMAGE_ID, graphic,
                      GDI_INFO_TEXT, tapebutton_info[i].infotext,
                      GDI_X, VX + pos->x,
                      GDI_Y, VY + pos->y,
@@ -1318,7 +1357,7 @@ void CreateTapeButtons()
   }
 }
 
-void FreeTapeButtons()
+void FreeTapeButtons(void)
 {
   int i;
 
@@ -1326,19 +1365,19 @@ void FreeTapeButtons()
     FreeGadget(tape_gadget[i]);
 }
 
-void MapTapeEjectButton()
+void MapTapeEjectButton(void)
 {
   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
   MapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
 }
 
-void MapTapeWarpButton()
+void MapTapeWarpButton(void)
 {
   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
   MapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
 }
 
-void MapTapeButtons()
+void MapTapeButtons(void)
 {
   int i;
 
@@ -1350,10 +1389,10 @@ void MapTapeButtons()
     MapTapeWarpButton();
 
   if (tape.show_game_buttons)
-    MapGameButtons();
+    MapGameButtonsOnTape();
 }
 
-void UnmapTapeButtons()
+void UnmapTapeButtons(void)
 {
   int i;
 
@@ -1361,7 +1400,32 @@ void UnmapTapeButtons()
     UnmapGadget(tape_gadget[i]);
 
   if (tape.show_game_buttons)
-    UnmapGameButtons();
+    UnmapGameButtonsOnTape();
+}
+
+void RedrawTapeButtons(void)
+{
+  int i;
+
+  for (i = 0; i < NUM_TAPE_BUTTONS; i++)
+    RedrawGadget(tape_gadget[i]);
+
+  if (tape.show_game_buttons)
+    RedrawGameButtonsOnTape();
+}
+
+void RedrawOrRemapTapeButtons(void)
+{
+  if (tape_gadget[TAPE_CTRL_ID_PLAY]->mapped)
+  {
+    // tape buttons already mapped
+    RedrawTapeButtons();
+  }
+  else
+  {
+    UnmapTapeButtons();
+    MapTapeButtons();
+  }
 }
 
 static void HandleTapeButtonsExt(int id)
@@ -1384,7 +1448,7 @@ static void HandleTapeButtonsExt(int id)
       else
       {
        if (tape.changed)
-         SaveTapeChecked(tape.level_nr);
+         SaveTapeChecked(level_nr);
 
        TapeErase();
       }
@@ -1395,46 +1459,44 @@ static void HandleTapeButtonsExt(int id)
     case TAPE_CTRL_ID_EXTRA:
       if (tape.playing)
       {
-       if (!tape.warp_forward)                 /* PLAY -> WARP FORWARD PLAY */
-       {
-         TapeStartWarpForward();
-       }
-       else if (tape.pausing)                  /* PAUSE -> WARP FORWARD PLAY */
-       {
-         TapeTogglePause(TAPE_TOGGLE_MANUAL);
-       }
-       else                                    /* WARP FORWARD PLAY -> PLAY */
-       {
-         TapeStopWarpForward();
-       }
+       tape.pause_before_end = !tape.pause_before_end;
+
+       DrawVideoDisplayCurrentState();
       }
       else if (tape.recording)
+      {
        TapeSingleStep();
+      }
 
       break;
 
     case TAPE_CTRL_ID_STOP:
       TapeStop();
+
       break;
 
     case TAPE_CTRL_ID_PAUSE:
       TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
       break;
 
     case TAPE_CTRL_ID_RECORD:
       if (TAPE_IS_STOPPED(tape))
+      {
        TapeStartGameRecording();
+      }
       else if (tape.pausing)
       {
-       if (tape.playing)                       /* PLAY -> PAUSE -> RECORD */
+       if (tape.playing)                       // PLAY -> PAUSE -> RECORD
          TapeAppendRecording();
        else
          TapeTogglePause(TAPE_TOGGLE_MANUAL);
       }
+
       break;
 
     case TAPE_CTRL_ID_PLAY:
-      if (tape.recording && tape.pausing)      /* PAUSE -> RECORD */
+      if (tape.recording && tape.pausing)      // PAUSE -> RECORD
       {
        // ("TAPE_IS_EMPTY(tape)" is TRUE here -- probably fix this)
 
@@ -1450,47 +1512,46 @@ static void HandleTapeButtonsExt(int id)
       }
       else if (tape.playing)
       {
-       if (tape.pausing)                       /* PAUSE -> PLAY */
+       if (tape.pausing)                       // PAUSE -> PLAY
        {
-         // continue playing in normal mode
-         tape.fast_forward = FALSE;
-         tape.warp_forward = FALSE;
-         tape.pause_before_death = FALSE;
-         tape.deactivate_display = FALSE;
-
-         TapeTogglePause(TAPE_TOGGLE_MANUAL);
+         TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
        }
-       else if (tape.warp_forward &&
-                !tape.fast_forward)            /* WARP FORWARD PLAY -> PLAY */
+       else if (!tape.fast_forward)            // PLAY -> FFWD
        {
-         TapeStopWarpForward();
+         tape.fast_forward = TRUE;
        }
-       else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
+       else if (!tape.warp_forward)            // FFWD -> WARP
        {
-         tape.fast_forward = TRUE;
-
-         DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
+         tape.warp_forward = TRUE;
        }
-       else if (!tape.pause_before_death)      /* FFWD PLAY -> AUTO PAUSE */
+       else if (!tape.deactivate_display)      // WARP -> WARP BLIND
        {
-         tape.pause_before_death = TRUE;
+         tape.deactivate_display = TRUE;
 
-         DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_ON, 0);
-
-         if (tape.warp_forward)
-           DrawVideoDisplaySymbol(VIDEO_STATE_WARP2_ON);
+         TapeDeactivateDisplayOn();
        }
-       else                                    /* AUTO PAUSE -> NORMAL PLAY */
+       else                                    // WARP BLIND -> PLAY
        {
-         if (tape.warp_forward)
-           TapeStopWarpForward();
-
          tape.fast_forward = FALSE;
-         tape.pause_before_death = FALSE;
+         tape.warp_forward = FALSE;
+         tape.deactivate_display = FALSE;
 
-         DrawVideoDisplay(VIDEO_STATE_PBEND_OFF | VIDEO_STATE_PLAY_ON, 0);
+         TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
        }
+
+       DrawVideoDisplayCurrentState();
       }
+
+      break;
+
+    case TAPE_CTRL_ID_INSERT_SOLUTION:
+      InsertSolutionTape();
+
+      break;
+
+    case TAPE_CTRL_ID_PLAY_SOLUTION:
+      PlaySolutionTape();
+
       break;
 
     default: