[rocksndiamonds] 02/04: New upstream version 4.0.1.1+dfsg

Stephen Kitt skitt at moszumanska.debian.org
Sat Feb 10 22:05:13 UTC 2018


This is an automated email from the git hooks/post-receive script.

skitt pushed a commit to branch master
in repository rocksndiamonds.

commit 0573579071444f3af95b18a53a6b58eba1821138
Author: Stephen Kitt <skitt at debian.org>
Date:   Sat Feb 10 22:46:11 2018 +0100

    New upstream version 4.0.1.1+dfsg
---
 src/anim.c           |  43 +++++++---
 src/config.c         |  26 ++++--
 src/config.h         |   1 +
 src/conftime.h       |   2 +-
 src/editor.c         |   2 +
 src/events.c         |  47 ++++++-----
 src/files.c          | 178 +++++++++++++++++++++++++++++----------
 src/files.h          |   2 +
 src/game.c           |  51 ++++++++++--
 src/game_sp/export.h |   4 +
 src/game_sp/file.c   |  10 ++-
 src/init.c           |   9 +-
 src/libgame/misc.c   |   6 ++
 src/libgame/sdl.c    |   2 +-
 src/libgame/sdl.h    |   8 +-
 src/libgame/sound.c  |  79 +++++++++++++-----
 src/libgame/sound.h  |   1 +
 src/libgame/system.c |  35 ++++++--
 src/libgame/system.h |  11 ++-
 src/main.c           |  29 +++++--
 src/main.h           |   4 +-
 src/screens.c        | 230 ++++++++++-----------------------------------------
 src/tape.c           |  82 +++++++++++-------
 src/tape.h           |   1 +
 src/tools.c          |  63 ++++++++++----
 25 files changed, 560 insertions(+), 366 deletions(-)

diff --git a/src/anim.c b/src/anim.c
index 37c7193..7060016 100644
--- a/src/anim.c
+++ b/src/anim.c
@@ -206,6 +206,7 @@ static unsigned int anim_sync_frame = 0;
 static int game_mode_anim_classes[NUM_GAME_MODES];
 static int anim_class_game_modes[NUM_ANIM_CLASSES];
 
+static int anim_status_last_before_fading = GAME_MODE_DEFAULT;
 static int anim_status_last = GAME_MODE_DEFAULT;
 static int anim_classes_last = ANIM_CLASS_NONE;
 
@@ -560,6 +561,7 @@ void InitGlobalAnimControls()
     anim_class_game_modes[anim_class_game_modes_list[i].class_bit] =
       anim_class_game_modes_list[i].game_mode;
 
+  anim_status_last_before_fading = GAME_MODE_LOADING;
   anim_status_last = GAME_MODE_LOADING;
   anim_classes_last = ANIM_CLASS_NONE;
 }
@@ -598,15 +600,23 @@ void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
     if (drawing_target == DRAW_TO_FADE_TARGET)
       after_fading = TRUE;
 
+    // special case: changing from/to this screen is done without fading
+    if (global.anim_status == GAME_MODE_PSEUDO_TYPENAME ||
+	anim_status_last   == GAME_MODE_PSEUDO_TYPENAME)
+      after_fading = TRUE;
+
     // ---------- part 1 ------------------------------------------------------
     // start or stop global animations by change of game mode
     // (special handling of animations for "current screen" and "all screens")
 
-    // stop animations for last screen
-    game_mode_anim_action[anim_status_last] = ANIM_STOP;
+    if (global.anim_status_next != anim_status_last_before_fading)
+    {
+      // stop animations for last screen before fading to new screen
+      game_mode_anim_action[anim_status_last] = ANIM_STOP;
 
-    // start animations for current screen
-    game_mode_anim_action[global.anim_status] = ANIM_START;
+      // start animations for current screen after fading to new screen
+      game_mode_anim_action[global.anim_status] = ANIM_START;
+    }
 
     // start animations for all screens after loading new artwork set
     if (anim_status_last == GAME_MODE_LOADING)
@@ -635,7 +645,10 @@ void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
     if (drawing_target == DRAW_TO_SCREEN)
     {
       if (after_fading)
+      {
 	anim_classes_last = anim_classes_next;
+	anim_status_last_before_fading = global.anim_status;
+      }
 
       anim_status_last = global.anim_status;
 
@@ -1452,6 +1465,7 @@ static void InitGlobalAnim_Clickable()
 
 static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
 {
+  boolean anything_clicked = FALSE;
   boolean any_part_clicked = FALSE;
   int mode_nr;
 
@@ -1460,12 +1474,14 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
     struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
     int anim_nr;
 
-    for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++)
+    // check animations in reverse draw order (to stop when clicked)
+    for (anim_nr = ctrl->num_anims - 1; anim_nr >= 0; anim_nr--)
     {
       struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
       int part_nr;
 
-      for (part_nr = 0; part_nr < anim->num_parts_all; part_nr++)
+      // check animation parts in reverse draw order (to stop when clicked)
+      for (part_nr = anim->num_parts_all - 1; part_nr >= 0; part_nr--)
       {
 	struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
 
@@ -1479,8 +1495,13 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
 	if (!part->clickable)
 	  continue;
 
+	// always handle "any" click events (clicking anywhere on screen) ...
 	if (isClickablePart(part, ANIM_EVENT_ANY))
-	  any_part_clicked = part->clicked = TRUE;
+	  anything_clicked = part->clicked = TRUE;
+
+	// ... but only handle the first (topmost) clickable animation
+	if (any_part_clicked)
+	  continue;
 
 	if (isClickedPart(part, mx, my, clicked))
 	{
@@ -1488,8 +1509,10 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
 	  printf("::: %d.%d CLICKED\n", anim_nr, part_nr);
 #endif
 
+	  any_part_clicked = TRUE;
+
 	  if (isClickablePart(part, ANIM_EVENT_SELF))
-	    any_part_clicked = part->clicked = TRUE;
+	    anything_clicked = part->clicked = TRUE;
 
 	  // check if this click is defined to trigger other animations
 	  int gic_anim_nr = part->old_anim_nr + 1;	// X as in "anim_X"
@@ -1511,7 +1534,7 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
 	      struct GlobalAnimPartControlInfo *part2 = &anim2->part[part2_nr];
 
 	      if (isClickablePart(part2, mask))
-		any_part_clicked = part2->clicked = TRUE;
+		anything_clicked = part2->clicked = TRUE;
 
 #if 0
 	      struct GraphicInfo *c = &part2->control_info;
@@ -1532,7 +1555,7 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, boolean clicked)
     }
   }
 
-  return any_part_clicked;
+  return anything_clicked;
 }
 
 static void ResetGlobalAnim_Clickable()
diff --git a/src/config.c b/src/config.c
index 608b066..8a92ebb 100644
--- a/src/config.c
+++ b/src/config.c
@@ -25,7 +25,7 @@ char *getProgramTitleString()
   return program.program_title;
 }
 
-char *getProgramVersionString()
+char *getProgramRealVersionString()
 {
   static char program_version_string[32];
 
@@ -36,6 +36,11 @@ char *getProgramVersionString()
   return program_version_string;
 }
 
+char *getProgramVersionString()
+{
+  return program.version_string;
+}
+
 char *getProgramInitString()
 {
   static char *program_init_string = NULL;
@@ -64,14 +69,23 @@ char *getWindowTitleString()
   window_title_string = checked_malloc(strlen(getProgramInitString()) + 20 +
 				       strlen(getSourceDateString()) + 2 + 1);
 
-  sprintf(window_title_string, "%s (%d %%) [%s]",
-	  getProgramInitString(), video.window_scaling_percent,
-	  getSourceDateString());
+  if (setup.internal.show_scaling_in_title)
+    sprintf(window_title_string, "%s (%d %%) [%s]",
+	    getProgramInitString(), video.window_scaling_percent,
+	    getSourceDateString());
+  else
+    sprintf(window_title_string, "%s [%s]",
+	    getProgramInitString(),
+	    getSourceDateString());
 #else
   window_title_string = checked_malloc(strlen(getProgramInitString()) + 20);
 
-  sprintf(window_title_string, "%s (%d %%)",
-	  getProgramInitString(), video.window_scaling_percent);
+  if (setup.internal.show_scaling_in_title)
+    sprintf(window_title_string, "%s (%d %%)",
+	    getProgramInitString(), video.window_scaling_percent);
+  else
+    sprintf(window_title_string, "%s",
+	    getProgramInitString());
 #endif
 
 #else
diff --git a/src/config.h b/src/config.h
index ba401a5..12e1961 100644
--- a/src/config.h
+++ b/src/config.h
@@ -16,6 +16,7 @@
 
 char *getSourceDateString(void);
 char *getProgramTitleString(void);
+char *getProgramRealVersionString(void);
 char *getProgramVersionString(void);
 char *getProgramInitString(void);
 char *getWindowTitleString(void);
diff --git a/src/conftime.h b/src/conftime.h
index 2ce7593..a4bd598 100644
--- a/src/conftime.h
+++ b/src/conftime.h
@@ -1 +1 @@
-#define SOURCE_DATE_STRING "2017-10-02 20:25"
+#define SOURCE_DATE_STRING "2018-02-03 00:17"
diff --git a/src/editor.c b/src/editor.c
index ca104b8..3d6b098 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -11366,6 +11366,8 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
       LoadLevel(level_nr);
       LoadScore(level_nr);
 
+      SaveLevelSetup_SeriesInfo();
+
       TapeErase();
 
       ResetUndoBuffer();
diff --git a/src/events.c b/src/events.c
index 5b1e577..b518398 100644
--- a/src/events.c
+++ b/src/events.c
@@ -128,6 +128,19 @@ static boolean SkipPressedMouseMotionEvent(const Event *event)
   return FALSE;
 }
 
+static boolean WaitValidEvent(Event *event)
+{
+  WaitEvent(event);
+
+  if (!FilterEvents(event))
+    return FALSE;
+
+  if (SkipPressedMouseMotionEvent(event))
+    return FALSE;
+
+  return TRUE;
+}
+
 /* this is especially needed for event modifications for the Android target:
    if mouse coordinates should be modified in the event filter function,
    using a properly installed SDL event filter does not work, because in
@@ -139,20 +152,8 @@ static boolean SkipPressedMouseMotionEvent(const Event *event)
 boolean NextValidEvent(Event *event)
 {
   while (PendingEvent())
-  {
-    boolean handle_this_event = FALSE;
-
-    NextEvent(event);
-
-    if (FilterEvents(event))
-      handle_this_event = TRUE;
-
-    if (SkipPressedMouseMotionEvent(event))
-      handle_this_event = FALSE;
-
-    if (handle_this_event)
+    if (WaitValidEvent(event))
       return TRUE;
-  }
 
   return FALSE;
 }
@@ -340,12 +341,10 @@ void EventLoop(void)
 
 void ClearEventQueue()
 {
-  while (PendingEvent())
-  {
-    Event event;
-
-    NextEvent(&event);
+  Event event;
 
+  while (NextValidEvent(&event))
+  {
     switch (event.type)
     {
       case EVENT_BUTTONRELEASE:
@@ -392,7 +391,8 @@ void SleepWhileUnmapped()
   {
     Event event;
 
-    NextEvent(&event);
+    if (!WaitValidEvent(&event))
+      continue;
 
     switch (event.type)
     {
@@ -1275,7 +1275,7 @@ void HandleButton(int mx, int my, int button, int button_nr)
   if (HandleGlobalAnimClicks(mx, my, button))
   {
     /* do not handle this button event anymore */
-    mx = my = -32;	/* force mouse event to be outside screen tiles */
+    return;		/* force mouse event not to be handled at all */
   }
 
   if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
@@ -1441,6 +1441,11 @@ static void HandleKeysSpecial(Key key)
     {
       SaveNativeLevel(&level);
     }
+    else if (is_string_suffix(cheat_input, ":frames-per-second") ||
+	     is_string_suffix(cheat_input, ":fps"))
+    {
+      global.show_frames_per_second = !global.show_frames_per_second;
+    }
   }
   else if (game_status == GAME_MODE_PLAYING)
   {
@@ -1470,7 +1475,7 @@ void HandleKeysDebug(Key key)
 
   if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
   {
-    boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
+    boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
 
     for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
     {
diff --git a/src/files.c b/src/files.c
index e9991e1..6c150ea 100644
--- a/src/files.c
+++ b/src/files.c
@@ -20,6 +20,7 @@
 #include "init.h"
 #include "tools.h"
 #include "tape.h"
+#include "config.h"
 
 #define ENABLE_UNUSED_CODE	0	/* currently unused functions */
 #define ENABLE_HISTORIC_CHUNKS	0	/* only for historic reference */
@@ -3774,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;
@@ -3786,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;
 }
 
@@ -3807,22 +3816,36 @@ 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.counter = 0;
+  tape.pos[tape.counter].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;
+    byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+    boolean success = 0;
+    int j;
+
+    for (j = 0; j < tape_repeat; j++)
+      success = TapeAddAction(action);
 
-    tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
-    tape.pos[i].delay = demo_repeat + 1;
+    if (!success)
+    {
+      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+	    MAX_TAPE_LEN);
+
+      break;
+    }
   }
 
-  tape.length_frames  = GetTapeLengthFrames();
-  tape.length_seconds = GetTapeLengthSeconds();
+  TapeHaltRecording();
 }
 
 
@@ -7347,7 +7370,16 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   for (i = 0; i < tape->length; i++)
   {
     if (i >= MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+	    MAX_TAPE_LEN);
+
+      // tape too large; read and ignore remaining tape data from this chunk
+      for (;i < tape->length; i++)
+	ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+
       break;
+    }
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
@@ -8061,24 +8093,26 @@ void SaveScore(int nr)
 
 /* internal setup */
 #define SETUP_TOKEN_INT_PROGRAM_TITLE		0
-#define SETUP_TOKEN_INT_PROGRAM_AUTHOR		1
-#define SETUP_TOKEN_INT_PROGRAM_EMAIL		2
-#define SETUP_TOKEN_INT_PROGRAM_WEBSITE		3
-#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT	4
-#define SETUP_TOKEN_INT_PROGRAM_COMPANY		5
-#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE	6
-#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET	7
-#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET	8
-#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET	9
-#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE	10
-#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE	11
-#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE	12
-#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES	13
-#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH	15
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT	16
-
-#define NUM_INTERNAL_SETUP_TOKENS		17
+#define SETUP_TOKEN_INT_PROGRAM_VERSION		1
+#define SETUP_TOKEN_INT_PROGRAM_AUTHOR		2
+#define SETUP_TOKEN_INT_PROGRAM_EMAIL		3
+#define SETUP_TOKEN_INT_PROGRAM_WEBSITE		4
+#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT	5
+#define SETUP_TOKEN_INT_PROGRAM_COMPANY		6
+#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE	7
+#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET	8
+#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET	9
+#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET	10
+#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE	11
+#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE	12
+#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE	13
+#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES	14
+#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
+#define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE	16
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH	17
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT	18
+
+#define NUM_INTERNAL_SETUP_TOKENS		19
 
 /* debug setup */
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0		0
@@ -8103,8 +8137,9 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9	19
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY	21
+#define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
 
-#define NUM_DEBUG_SETUP_TOKENS			22
+#define NUM_DEBUG_SETUP_TOKENS			23
 
 /* options setup */
 #define SETUP_TOKEN_OPTIONS_VERBOSE		0
@@ -8254,6 +8289,7 @@ static struct TokenInfo system_setup_tokens[] =
 static struct TokenInfo internal_setup_tokens[] =
 {
   { TYPE_STRING, &sxi.program_title,		"program_title"		},
+  { TYPE_STRING, &sxi.program_version,		"program_version"	},
   { TYPE_STRING, &sxi.program_author,		"program_author"	},
   { TYPE_STRING, &sxi.program_email,		"program_email"		},
   { TYPE_STRING, &sxi.program_website,		"program_website"	},
@@ -8268,6 +8304,7 @@ static struct TokenInfo internal_setup_tokens[] =
   { TYPE_STRING, &sxi.fallback_music_file,	"fallback_music_file"	},
   { TYPE_STRING, &sxi.default_level_series,	"default_level_series"	},
   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir,	"choose_from_top_leveldir" },
+  { TYPE_BOOLEAN,&sxi.show_scaling_in_title,	"show_scaling_in_title" },
   { TYPE_INTEGER,&sxi.default_window_width,	"default_window_width"	},
   { TYPE_INTEGER,&sxi.default_window_height,	"default_window_height"	},
 };
@@ -8296,6 +8333,7 @@ static struct TokenInfo debug_setup_tokens[] =
   { TYPE_KEY_X11, &sdi.frame_delay_key[9],	"debug.key.frame_delay_9" },
   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
+  { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
 };
 
 static struct TokenInfo options_setup_tokens[] =
@@ -8446,6 +8484,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
 
   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
+  si->internal.program_version   = getStringCopy(getProgramRealVersionString());
   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
@@ -8464,6 +8503,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
   si->internal.choose_from_top_leveldir = FALSE;
+  si->internal.show_scaling_in_title = TRUE;
 
   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
@@ -8493,6 +8533,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
 
+  si->debug.show_frames_per_second = FALSE;
+
   si->options.verbose = FALSE;
 
 #if defined(PLATFORM_ANDROID)
@@ -8520,6 +8562,60 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
   si->editor_cascade.el_dynamic		= FALSE;
 }
 
+#define MAX_HIDE_SETUP_TOKEN_SIZE		20
+
+static char *getHideSetupToken(void *setup_value)
+{
+  static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
+
+  if (setup_value != NULL)
+    snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
+
+  return hide_setup_token;
+}
+
+static void setHideSetupEntry(void *setup_value_raw)
+{
+  /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
+  void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
+
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  if (setup_value != NULL)
+    setHashEntry(hide_setup_hash, hide_setup_token, "");
+}
+
+boolean hideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  return (setup_value != NULL &&
+	  getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
+}
+
+static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
+				      struct TokenInfo *token_info,
+				      int token_nr, char *token_text)
+{
+  char *token_hide_text = getStringCat2(token_text, ".hide");
+  char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
+
+  /* set the value of this setup option in the setup option structure */
+  setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
+
+  /* check if this setup option should be hidden in the setup menu */
+  if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
+    setHideSetupEntry(token_info[token_nr].value);
+}
+
+static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
+				      struct TokenInfo *token_info,
+				      int token_nr)
+{
+  setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
+			    token_info[token_nr].text);
+}
+
 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 {
   int i, pnr;
@@ -8527,25 +8623,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   if (!setup_file_hash)
     return;
 
+  if (hide_setup_hash == NULL)
+    hide_setup_hash = newSetupFileHash();
+
   /* global setup */
   si = setup;
   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
-    setSetupInfo(global_setup_tokens, i,
-		 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
   setup = si;
 
   /* editor setup */
   sei = setup.editor;
   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
-    setSetupInfo(editor_setup_tokens, i,
-		 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
   setup.editor = sei;
 
   /* shortcut setup */
   ssi = setup.shortcut;
   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
-    setSetupInfo(shortcut_setup_tokens, i,
-		 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
   setup.shortcut = ssi;
 
   /* player setup */
@@ -8561,8 +8657,8 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
       char full_token[100];
 
       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
-      setSetupInfo(player_setup_tokens, i,
-		   getHashEntry(setup_file_hash, full_token));
+      setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
+				full_token);
     }
     setup.input[pnr] = sii;
   }
@@ -8570,29 +8666,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   /* system setup */
   syi = setup.system;
   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
-    setSetupInfo(system_setup_tokens, i,
-		 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
   setup.system = syi;
 
   /* internal setup */
   sxi = setup.internal;
   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
-    setSetupInfo(internal_setup_tokens, i,
-		 getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
   setup.internal = sxi;
 
   /* debug setup */
   sdi = setup.debug;
   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
-    setSetupInfo(debug_setup_tokens, i,
-		 getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
   setup.debug = sdi;
 
   /* options setup */
   soi = setup.options;
   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
-    setSetupInfo(options_setup_tokens, i,
-		 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
   setup.options = soi;
 }
 
diff --git a/src/files.h b/src/files.h
index 1db4988..f12faca 100644
--- a/src/files.h
+++ b/src/files.h
@@ -65,6 +65,8 @@ void SaveSetup_EditorCascade();
 
 void SaveSetup_AddGameControllerMapping(char *);
 
+boolean hideSetupEntry(void *);
+
 void LoadCustomElementDescriptions();
 void InitMenuDesignSettings_Static();
 void LoadMenuDesignSettings();
diff --git a/src/game.c b/src/game.c
index 8978e70..a3ad6f1 100644
--- a/src/game.c
+++ b/src/game.c
@@ -1052,6 +1052,7 @@ static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
 static void PlayLevelSoundActionIfLoop(int, int, int);
 static void StopLevelSoundActionIfLoop(int, int, int);
 static void PlayLevelMusic();
+static void FadeLevelSoundsAndMusic();
 
 static void HandleGameButtons(struct GadgetInfo *);
 
@@ -3118,7 +3119,7 @@ void InitGame()
   if (CheckIfGlobalBorderHasChanged())
     fade_mask = REDRAW_ALL;
 
-  FadeSoundsAndMusic();
+  FadeLevelSoundsAndMusic();
 
   ExpireSoundLoops(TRUE);
 
@@ -11212,7 +11213,7 @@ void GameActionsExt()
 
   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
 
-  if (options.debug)			/* calculate frames per second */
+  if (global.show_frames_per_second)
   {
     static unsigned int fps_counter = 0;
     static int fps_frames = 0;
@@ -11220,15 +11221,20 @@ void GameActionsExt()
 
     fps_frames++;
 
-    if (fps_delay_ms >= 500)	/* calculate fps every 0.5 seconds */
+    if (fps_delay_ms >= 500)	/* calculate FPS every 0.5 seconds */
     {
       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
 
       fps_frames = 0;
       fps_counter = Counter();
+
+      /* always draw FPS to screen after FPS value was updated */
+      redraw_mask |= REDRAW_FPS;
     }
 
-    redraw_mask |= REDRAW_FPS;
+    /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
+    if (GetDrawDeactivationMask() == REDRAW_NONE)
+      redraw_mask |= REDRAW_FPS;
   }
 }
 
@@ -14227,12 +14233,43 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action)
     StopSound(sound_effect);
 }
 
-static void PlayLevelMusic()
+static int getLevelMusicNr()
 {
   if (levelset.music[level_nr] != MUS_UNDEFINED)
-    PlayMusic(levelset.music[level_nr]);	/* from config file */
+    return levelset.music[level_nr];		/* from config file */
   else
-    PlayMusic(MAP_NOCONF_MUSIC(level_nr));	/* from music dir */
+    return MAP_NOCONF_MUSIC(level_nr);		/* from music dir */
+}
+
+static void FadeLevelSounds()
+{
+  FadeSounds();
+}
+
+static void FadeLevelMusic()
+{
+  int music_nr = getLevelMusicNr();
+  char *curr_music = getCurrentlyPlayingMusicFilename();
+  char *next_music = getMusicInfoEntryFilename(music_nr);
+
+  if (!strEqual(curr_music, next_music))
+    FadeMusic();
+}
+
+void FadeLevelSoundsAndMusic()
+{
+  FadeLevelSounds();
+  FadeLevelMusic();
+}
+
+static void PlayLevelMusic()
+{
+  int music_nr = getLevelMusicNr();
+  char *curr_music = getCurrentlyPlayingMusicFilename();
+  char *next_music = getMusicInfoEntryFilename(music_nr);
+
+  if (!strEqual(curr_music, next_music))
+    PlayMusic(music_nr);
 }
 
 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
diff --git a/src/game_sp/export.h b/src/game_sp/export.h
index 4edac47..6470068 100644
--- a/src/game_sp/export.h
+++ b/src/game_sp/export.h
@@ -27,6 +27,10 @@
 #define SP_STD_LEVEL_SIZE		(SP_HEADER_SIZE + SP_STD_PLAYFIELD_SIZE)
 
 #define SP_FRAMES_PER_SECOND		35
+
+// use a much higher value to be able to load ultra-long MPX demo files
+// (like for level collection 78, level 88 ("WAITING FOR GODOT AGAIN"))
+// #define SP_MAX_TAPE_LEN			500000
 #define SP_MAX_TAPE_LEN			64010	/* (see "spfix63.doc") */
 
 
diff --git a/src/game_sp/file.c b/src/game_sp/file.c
index a38c806..9fa3908 100644
--- a/src/game_sp/file.c
+++ b/src/game_sp/file.c
@@ -254,13 +254,13 @@ static void LoadNativeLevelFromFileStream_SP(File *file, int width, int height,
       native_sp_level.demo.data[i] = getFile8Bit(file);
 
       if (native_sp_level.demo.data[i] == 0xff)	/* "end of demo" byte */
-      {
-	i++;
-
 	break;
-      }
     }
 
+    if (i >= SP_MAX_TAPE_LEN)
+      Error(ERR_WARN, "SP demo truncated: size exceeds maximum SP demo size %d",
+	    SP_MAX_TAPE_LEN);
+
     native_sp_level.demo.length = i;
     native_sp_level.demo.is_available = (native_sp_level.demo.length > 0);
   }
@@ -574,6 +574,8 @@ void SaveNativeLevel_SP(char *filename)
 
     for (i = 0; i < native_sp_level.demo.length; i++)
       putFile8Bit(file, native_sp_level.demo.data[i]);
+
+    putFile8Bit(file, 0xff);	/* "end of demo" byte */
   }
 
   fclose(file);
diff --git a/src/init.c b/src/init.c
index ea7c8f9..54f4277 100644
--- a/src/init.c
+++ b/src/init.c
@@ -97,6 +97,9 @@ void DrawInitAnim()
   int sync_frame = FrameCounter;
   int x, y;
 
+  /* prevent OS (Windows) from complaining about program not responding */
+  CheckQuitEvent();
+
   if (game_status != GAME_MODE_LOADING)
     return;
 
@@ -4835,6 +4838,7 @@ static void InitGlobal()
   global.create_images_dir = NULL;
 
   global.frames_per_second = 0;
+  global.show_frames_per_second = FALSE;
 
   global.border_status = GAME_MODE_LOADING;
   global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
@@ -5054,6 +5058,9 @@ static void InitSetup()
 
   if (setup.options.verbose)
     options.verbose = TRUE;
+
+  if (setup.debug.show_frames_per_second)
+    global.show_frames_per_second = TRUE;
 }
 
 static void InitGameInfo()
@@ -6108,7 +6115,7 @@ void CloseAllAndExit(int exit_value)
   CloseVideoDisplay();
   ClosePlatformDependentStuff();
 
-  if (exit_value != 0)
+  if (exit_value != 0 && !options.execute_command)
   {
     /* fall back to default level set (current set may have caused an error) */
     SaveLevelSetup_LastSeries_Deactivate();
diff --git a/src/libgame/misc.c b/src/libgame/misc.c
index ed34319..bddd38d 100644
--- a/src/libgame/misc.c
+++ b/src/libgame/misc.c
@@ -1005,6 +1005,12 @@ void GetOptions(int argc, char *argv[],
 #endif
 #endif
 
+#if DEBUG
+#if defined(PLATFORM_ANDROID)
+  options.debug = TRUE;
+#endif
+#endif
+
   while (*options_left)
   {
     char option_str[MAX_OPTION_LEN];
diff --git a/src/libgame/sdl.c b/src/libgame/sdl.c
index 14ad28f..9d2dc17 100644
--- a/src/libgame/sdl.c
+++ b/src/libgame/sdl.c
@@ -2536,7 +2536,7 @@ void SDLCloseAudio(void)
 /* event functions                                                           */
 /* ========================================================================= */
 
-void SDLNextEvent(Event *event)
+void SDLWaitEvent(Event *event)
 {
   SDL_WaitEvent(event);
 }
diff --git a/src/libgame/sdl.h b/src/libgame/sdl.h
index 9ee6c69..b6fad44 100644
--- a/src/libgame/sdl.h
+++ b/src/libgame/sdl.h
@@ -430,6 +430,12 @@ struct MouseCursorInfo
 #define KMOD_Meta		(KMOD_Meta_L    | KMOD_Meta_R)
 #define KMOD_Alt		(KMOD_Alt_L     | KMOD_Alt_R)
 
+/* this only contains "valid" key modifiers (and ignores keys like "NumLock") */
+#define KMOD_Valid		(KMOD_Shift   |	\
+				 KMOD_Control |	\
+				 KMOD_Meta    |	\
+				 KMOD_Alt)
+
 #if defined(TARGET_SDL2)
 #define KMOD_TextInput		(KMOD_Shift | KMOD_Alt_R)
 #endif
@@ -484,7 +490,7 @@ void SDLSetMouseCursor(struct MouseCursorInfo *);
 void SDLOpenAudio(void);
 void SDLCloseAudio(void);
 
-void SDLNextEvent(Event *);
+void SDLWaitEvent(Event *);
 void SDLHandleWindowManagerEvent(Event *);
 
 void HandleJoystickEvent(Event *);
diff --git a/src/libgame/sound.c b/src/libgame/sound.c
index cb788e7..15354b6 100644
--- a/src/libgame/sound.c
+++ b/src/libgame/sound.c
@@ -225,6 +225,16 @@ static void Mixer_PlayMusicChannel()
     // (this may happen when switching on music while playing the game)
     Mix_VolumeMusic(mixer[audio.music_channel].volume);
     Mix_FadeInMusic(mixer[audio.music_channel].data_ptr, -1, 100);
+
+#if defined(PLATFORM_WIN32)
+    // playing MIDI music is broken since Windows Vista, as it sets the volume
+    // for MIDI music also for all other sounds and music, which cannot be set
+    // back to normal unless playing MIDI music again with that desired volume
+    // (more details: https://www.artsoft.org/forum/viewtopic.php?f=7&t=2253)
+    // => workaround: always play MIDI music with maximum volume
+    if (Mix_GetMusicType(NULL) == MUS_MID)
+      Mix_VolumeMusic(SOUND_MAX_VOLUME);
+#endif
   }
 }
 
@@ -264,6 +274,16 @@ static void Mixer_FadeMusicChannel()
 
   Mix_FadeOutMusic(SOUND_FADING_INTERVAL);
 
+#if defined(PLATFORM_WIN32)
+  // playing MIDI music is broken since Windows Vista, as it sets the volume
+  // for MIDI music also for all other sounds and music, which cannot be set
+  // back to normal unless playing MIDI music again with that desired volume
+  // (more details: https://www.artsoft.org/forum/viewtopic.php?f=7&t=2253)
+  // => workaround: never fade MIDI music to lower volume, but just stop it
+  if (Mix_GetMusicType(NULL) == MUS_MID)
+    Mixer_StopMusicChannel();
+#endif
+
   setString(&currently_playing_music_filename, NULL);
 }
 
@@ -283,32 +303,11 @@ static void Mixer_InsertSound(SoundControl snd_ctrl)
 {
   SoundInfo *snd_info;
   int i, k;
-  int num_sounds = getSoundListSize();
-  int num_music  = getMusicListSize();
 
   if (IS_MUSIC(snd_ctrl))
-  {
-    if (snd_ctrl.nr >= num_music)	/* invalid music */
-      return;
-
-    if (snd_ctrl.nr < 0)		/* undefined music */
-    {
-      if (num_music_noconf == 0)	/* no fallback music available */
-	return;
-
-      snd_ctrl.nr = UNMAP_NOCONF_MUSIC(snd_ctrl.nr) % num_music_noconf;
-      snd_info = Music_NoConf[snd_ctrl.nr];
-    }
-    else
-      snd_info = getMusicInfoEntryFromMusicID(snd_ctrl.nr);
-  }
+    snd_info = getMusicInfoEntryFromMusicID(snd_ctrl.nr);
   else
-  {
-    if (snd_ctrl.nr < 0 || snd_ctrl.nr >= num_sounds)
-      return;
-
     snd_info = getSoundInfoEntryFromSoundID(snd_ctrl.nr);
-  }
 
   if (snd_info == NULL)
     return;
@@ -667,44 +666,80 @@ int getMusicListSize()
 
 struct FileInfo *getSoundListEntry(int pos)
 {
+  int num_sounds = getSoundListSize();
   int num_list_entries = sound_info->num_file_list_entries;
   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
 
+  if (pos < 0 || pos >= num_sounds)	/* invalid sound */
+    return NULL;
+
   return (pos < num_list_entries ? &sound_info->file_list[list_pos] :
 	  &sound_info->dynamic_file_list[list_pos]);
 }
 
 struct FileInfo *getMusicListEntry(int pos)
 {
+  int num_music = getMusicListSize();
   int num_list_entries = music_info->num_file_list_entries;
   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
 
+  if (pos < 0 || pos >= num_music)	/* invalid music */
+    return NULL;
+
   return (pos < num_list_entries ? &music_info->file_list[list_pos] :
 	  &music_info->dynamic_file_list[list_pos]);
 }
 
 static SoundInfo *getSoundInfoEntryFromSoundID(int pos)
 {
+  int num_sounds = getSoundListSize();
   int num_list_entries = sound_info->num_file_list_entries;
   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
   SoundInfo **snd_info =
     (SoundInfo **)(pos < num_list_entries ? sound_info->artwork_list :
 		   sound_info->dynamic_artwork_list);
 
+  if (pos < 0 || pos >= num_sounds)	/* invalid sound */
+    return NULL;
+
   return snd_info[list_pos];
 }
 
 static MusicInfo *getMusicInfoEntryFromMusicID(int pos)
 {
+  int num_music = getMusicListSize();
   int num_list_entries = music_info->num_file_list_entries;
   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
   MusicInfo **mus_info =
     (MusicInfo **)(pos < num_list_entries ? music_info->artwork_list :
 		   music_info->dynamic_artwork_list);
 
+  if (pos >= num_music)			/* invalid music */
+    return NULL;
+
+  if (pos < 0)				/* undefined music */
+  {
+    if (num_music_noconf == 0)		/* no fallback music available */
+      return NULL;
+
+    pos = UNMAP_NOCONF_MUSIC(pos) % num_music_noconf;
+
+    return Music_NoConf[pos];
+  }
+
   return mus_info[list_pos];
 }
 
+char *getMusicInfoEntryFilename(int pos)
+{
+  MusicInfo *mus_info = getMusicInfoEntryFromMusicID(pos);
+
+  if (mus_info == NULL)
+    return NULL;
+
+  return getBaseNamePtr(mus_info->source_filename);
+}
+
 char *getCurrentlyPlayingMusicFilename()
 {
   return currently_playing_music_filename;
diff --git a/src/libgame/sound.h b/src/libgame/sound.h
index 8eb344e..9d496c3 100644
--- a/src/libgame/sound.h
+++ b/src/libgame/sound.h
@@ -121,6 +121,7 @@ int getSoundListSize();
 int getMusicListSize();
 struct FileInfo *getSoundListEntry(int);
 struct FileInfo *getMusicListEntry(int);
+char *getMusicInfoEntryFilename(int);
 char *getCurrentlyPlayingMusicFilename();
 int getSoundListPropertyMappingSize();
 int getMusicListPropertyMappingSize();
diff --git a/src/libgame/system.c b/src/libgame/system.c
index 4d4c448..a87ecc8 100644
--- a/src/libgame/system.c
+++ b/src/libgame/system.c
@@ -68,7 +68,7 @@ int			FrameCounter = 0;
 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
 		     char *program_title, char *icon_title,
 		     char *icon_filename, char *cookie_prefix,
-		     int program_version)
+		     char *program_version_string, int program_version)
 {
   program.command_basepath = getBasePath(argv0);
   program.command_basename = getBaseName(argv0);
@@ -92,6 +92,8 @@ void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
   program.version_build = VERSION_BUILD(program_version);
   program.version_ident = program_version;
 
+  program.version_string = program_version_string;
+
   program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
   program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
   program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
@@ -329,6 +331,11 @@ void SetDrawDeactivationMask(int draw_deactivation_mask)
   gfx.draw_deactivation_mask = draw_deactivation_mask;
 }
 
+int GetDrawDeactivationMask()
+{
+  return gfx.draw_deactivation_mask;
+}
+
 void SetDrawBackgroundMask(int draw_background_mask)
 {
   gfx.draw_background_mask = draw_background_mask;
@@ -518,6 +525,18 @@ Bitmap *CreateBitmap(int width, int height, int depth)
 
 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
 {
+  if (*bitmap != NULL)
+  {
+    /* if new bitmap size fits into old one, no need to re-create it */
+    if (width  <= (*bitmap)->width &&
+        height <= (*bitmap)->height)
+      return;
+
+    /* else adjust size so that old and new bitmap size fit into it */
+    width  = MAX(width,  (*bitmap)->width);
+    height = MAX(height, (*bitmap)->height);
+  }
+
   Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
 
   if (*bitmap == NULL)
@@ -1474,9 +1493,9 @@ boolean PendingEvent(void)
   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
 }
 
-void NextEvent(Event *event)
+void WaitEvent(Event *event)
 {
-  SDLNextEvent(event);
+  SDLWaitEvent(event);
 }
 
 void PeekEvent(Event *event)
@@ -1488,6 +1507,12 @@ void PeekEvent(Event *event)
 #endif
 }
 
+void CheckQuitEvent(void)
+{
+  if (SDL_QuitRequested())
+    program.exit_function(0);
+}
+
 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
 {
 #if defined(TARGET_SDL2)
@@ -1578,9 +1603,9 @@ KeyMod GetKeyModStateFromEvents()
 void StartTextInput(int x, int y, int width, int height)
 {
 #if defined(TARGET_SDL2)
+#if defined(HAS_SCREEN_KEYBOARD)
   SDL_StartTextInput();
 
-#if defined(HAS_SCREEN_KEYBOARD)
   if (y + height > SCREEN_KEYBOARD_POS(video.height))
   {
     video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
@@ -1594,9 +1619,9 @@ void StartTextInput(int x, int y, int width, int height)
 void StopTextInput()
 {
 #if defined(TARGET_SDL2)
+#if defined(HAS_SCREEN_KEYBOARD)
   SDL_StopTextInput();
 
-#if defined(HAS_SCREEN_KEYBOARD)
   if (video.shifted_up)
   {
     video.shifted_up_pos = 0;
diff --git a/src/libgame/system.h b/src/libgame/system.h
index 2f6c280..3cf8bea 100644
--- a/src/libgame/system.h
+++ b/src/libgame/system.h
@@ -779,6 +779,8 @@ struct ProgramInfo
   int version_build;
   int version_ident;
 
+  char *version_string;
+
   char *(*window_title_function)(void);
   void (*exit_message_function)(char *, va_list);
   void (*exit_function)(int);
@@ -1065,6 +1067,7 @@ struct SetupSystemInfo
 struct SetupInternalInfo
 {
   char *program_title;
+  char *program_version;
   char *program_author;
   char *program_email;
   char *program_website;
@@ -1087,6 +1090,7 @@ struct SetupInternalInfo
   int default_window_height;
 
   boolean choose_from_top_leveldir;
+  boolean show_scaling_in_title;
 };
 
 struct SetupDebugInfo
@@ -1095,6 +1099,7 @@ struct SetupDebugInfo
   Key frame_delay_key[10];
   boolean frame_delay_use_mod_key;
   boolean frame_delay_game_only;
+  boolean show_frames_per_second;
 };
 
 struct SetupInfo
@@ -1447,7 +1452,7 @@ extern int			FrameCounter;
 /* function definitions */
 
 void InitProgramInfo(char *, char *, char *, char *, char *, char *, char *,
-		     int);
+		     char *, int);
 
 void InitScoresInfo();
 void SetWindowTitle();
@@ -1476,6 +1481,7 @@ void SetOverlayEnabled(boolean);
 void SetOverlayActive(boolean);
 boolean GetOverlayActive();
 void SetDrawDeactivationMask(int);
+int GetDrawDeactivationMask(void);
 void SetDrawBackgroundMask(int);
 void SetWindowBackgroundBitmap(Bitmap *);
 void SetMainBackgroundBitmap(Bitmap *);
@@ -1538,8 +1544,9 @@ void CloseAudio(void);
 void SetAudioMode(boolean);
 
 boolean PendingEvent(void);
-void NextEvent(Event *event);
+void WaitEvent(Event *event);
 void PeekEvent(Event *event);
+void CheckQuitEvent(void);
 Key GetEventKey(KeyEvent *, boolean);
 KeyMod HandleKeyModState(Key, int);
 KeyMod GetKeyModState();
diff --git a/src/main.c b/src/main.c
index 690296d..1dd7a16 100644
--- a/src/main.c
+++ b/src/main.c
@@ -183,6 +183,7 @@ SetupFileHash	       *image_config_hash = NULL;
 SetupFileHash	       *element_token_hash = NULL;
 SetupFileHash	       *graphic_token_hash = NULL;
 SetupFileHash	       *font_token_hash = NULL;
+SetupFileHash	       *hide_setup_hash = NULL;
 
 
 /* ------------------------------------------------------------------------- */
@@ -5737,13 +5738,20 @@ static void print_usage()
 
 static void print_version()
 {
-  Print("%s %d.%d.%d.%d%s\n",
-	PROGRAM_TITLE_STRING,
-	PROGRAM_VERSION_MAJOR,
-	PROGRAM_VERSION_MINOR,
-	PROGRAM_VERSION_PATCH,
-	PROGRAM_VERSION_BUILD,
-	PROGRAM_VERSION_EXTRA);
+  Print("%s", getProgramInitString());
+
+  if (!strEqual(getProgramVersionString(), getProgramRealVersionString()))
+  {
+    Print(" (%s %d.%d.%d.%d%s)",
+	  PROGRAM_TITLE_STRING,
+	  PROGRAM_VERSION_MAJOR,
+	  PROGRAM_VERSION_MINOR,
+	  PROGRAM_VERSION_PATCH,
+	  PROGRAM_VERSION_BUILD,
+	  PROGRAM_VERSION_EXTRA);
+  }
+
+  Print("\n");
 
   if (options.debug)
   {
@@ -5779,6 +5787,7 @@ static void InitProgramConfig(char *command_filename)
 {
   char *program_title = PROGRAM_TITLE_STRING;
   char *program_icon_file = PROGRAM_ICON_FILENAME;
+  char *program_version = getProgramRealVersionString();
   char *config_filename = getProgramConfigFilename(command_filename);
   char *userdata_basename = getBaseNameNoSuffix(command_filename);
   char *userdata_subdir;
@@ -5804,6 +5813,11 @@ static void InitProgramConfig(char *command_filename)
       strlen(setup.internal.program_title) > 0)
     program_title = getStringCopy(setup.internal.program_title);
 
+  // set program version from potentially redefined program version
+  if (setup.internal.program_version != NULL &&
+      strlen(setup.internal.program_version) > 0)
+    program_version = getStringCopy(setup.internal.program_version);
+
   // set program icon file from potentially redefined program icon file
   if (setup.internal.program_icon_file != NULL &&
       strlen(setup.internal.program_icon_file) > 0)
@@ -5832,6 +5846,7 @@ static void InitProgramConfig(char *command_filename)
 		  program_title,
 		  program_icon_file,
 		  COOKIE_PREFIX,
+		  program_version,
 		  GAME_VERSION_ACTUAL);
 }
 
diff --git a/src/main.h b/src/main.h
index 90d6e88..7b15ad9 100644
--- a/src/main.h
+++ b/src/main.h
@@ -2080,7 +2080,7 @@
 #define PROGRAM_VERSION_MAJOR		4
 #define PROGRAM_VERSION_MINOR		0
 #define PROGRAM_VERSION_PATCH		1
-#define PROGRAM_VERSION_BUILD		0
+#define PROGRAM_VERSION_BUILD		1
 #define PROGRAM_VERSION_EXTRA		""
 
 #define PROGRAM_TITLE_STRING		"Rocks'n'Diamonds"
@@ -2649,6 +2649,7 @@ struct GlobalInfo
   int num_toons;
 
   float frames_per_second;
+  boolean show_frames_per_second;
 
   /* global values for fading screens and masking borders */
   int border_status;
@@ -3180,6 +3181,7 @@ extern SetupFileHash	       *image_config_hash;
 extern SetupFileHash	       *element_token_hash;
 extern SetupFileHash	       *graphic_token_hash;
 extern SetupFileHash	       *font_token_hash;
+extern SetupFileHash	       *hide_setup_hash;
 extern struct ConfigTypeInfo	image_config_suffix[];
 extern struct ConfigTypeInfo	sound_config_suffix[];
 extern struct ConfigTypeInfo	music_config_suffix[];
diff --git a/src/screens.c b/src/screens.c
index ce99012..ab1d2be 100644
--- a/src/screens.c
+++ b/src/screens.c
@@ -1482,6 +1482,8 @@ void DrawMainMenu()
   LoadLevel(level_nr);
   LoadScore(level_nr);
 
+  SaveLevelSetup_SeriesInfo();
+
   // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it)
   SetDrawDeactivationMask(REDRAW_NONE);
   SetDrawBackgroundMask(REDRAW_FIELD);
@@ -1785,6 +1787,8 @@ void HandleMainMenu_SelectLevel(int step, int direction, int selected_level_nr)
     LoadTape(level_nr);
     DrawCompleteVideoDisplay();
 
+    SaveLevelSetup_SeriesInfo();
+
     /* needed because DrawPreviewLevelInitial() takes some time */
     BackToFront();
     /* SyncDisplay(); */
@@ -3308,6 +3312,13 @@ void DrawInfoScreen_Version()
   DrawTextF(xstart1, ystart2, font_header, "Version");
   DrawTextF(xstart2, ystart2, font_text, getProgramVersionString());
 
+  if (!strEqual(getProgramVersionString(), getProgramRealVersionString()))
+  {
+    ystart2 += ystep;
+    DrawTextF(xstart1, ystart2, font_header, "Version (real)");
+    DrawTextF(xstart2, ystart2, font_text, getProgramRealVersionString());
+  }
+
   ystart2 += ystep;
   DrawTextF(xstart1, ystart2, font_header, "Platform");
   DrawTextF(xstart2, ystart2, font_text, PLATFORM_STRING);
@@ -5593,12 +5604,10 @@ static Key getSetupKey()
 
   while (!got_key_event)
   {
-    if (PendingEvent())		/* got event */
-    {
-      Event event;
-
-      NextEvent(&event);
+    Event event;
 
+    if (NextValidEvent(&event))
+    {
       switch (event.type)
       {
         case EVENT_KEYPRESS:
@@ -5631,7 +5640,9 @@ static Key getSetupKey()
 
 static int getSetupValueFont(int type, void *value)
 {
-  if (type & TYPE_KEY)
+  if (type & TYPE_GHOSTED)
+    return FONT_OPTION_OFF;
+  else if (type & TYPE_KEY)
     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
   else if (type & TYPE_STRING)
     return FONT_VALUE_2;
@@ -5809,6 +5820,28 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
     ToggleFullscreenOrChangeWindowScalingIfNeeded();
 }
 
+static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *setup_info_orig)
+{
+  static struct TokenInfo *setup_info_hide = NULL;
+  int list_size = 0;
+  int list_pos = 0;
+  int i;
+
+  /* determine maximum list size of target list */
+  while (setup_info_orig[list_size++].type != 0);
+
+  /* free, allocate and clear memory for target list */
+  checked_free(setup_info_hide);
+  setup_info_hide = checked_calloc(list_size * sizeof(struct TokenInfo));
+
+  /* copy setup info list without setup entries marked as hidden */
+  for (i = 0; setup_info_orig[i].type != 0; i++)
+    if (!hideSetupEntry(setup_info_orig[i].value))
+      setup_info_hide[list_pos++] = setup_info_orig[i];
+
+  return setup_info_hide;
+}
+
 static void DrawSetupScreen_Generic()
 {
   int fade_mask = REDRAW_FIELD;
@@ -5908,6 +5941,9 @@ static void DrawSetupScreen_Generic()
     title_string = "Setup Shortcuts";
   }
 
+  /* use modified setup info without setup entries marked as hidden */
+  setup_info = getSetupInfoFinal(setup_info);
+
   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
 
   // determine maximal number of setup entries that can be displayed on screen
@@ -6252,12 +6288,10 @@ void CustomizeKeyboard(int player_nr)
 
   while (!finished)
   {
-    if (PendingEvent())		/* got event */
-    {
-      Event event;
-
-      NextEvent(&event);
+    Event event;
 
+    if (NextValidEvent(&event))
+    {
       switch (event.type)
       {
         case EVENT_KEYPRESS:
@@ -6340,180 +6374,6 @@ void CustomizeKeyboard(int player_nr)
   DrawSetupScreen_Input();
 }
 
-#if 0
-static boolean OLD_CalibrateJoystickMain(int player_nr)
-{
-  int new_joystick_xleft = JOYSTICK_XMIDDLE;
-  int new_joystick_xright = JOYSTICK_XMIDDLE;
-  int new_joystick_yupper = JOYSTICK_YMIDDLE;
-  int new_joystick_ylower = JOYSTICK_YMIDDLE;
-  int new_joystick_xmiddle, new_joystick_ymiddle;
-
-  char *device_name = setup.input[player_nr].joy.device_name;
-  int joystick_nr = getJoystickNrFromDeviceName(device_name);
-  boolean joystick_active = CheckJoystickOpened(joystick_nr);
-
-  int x, y, last_x, last_y, xpos = 8, ypos = 3;
-  boolean check[3][3];
-  int check_remaining = 3 * 3;
-  int joy_x, joy_y;
-  int joy_value;
-  int result = -1;
-
-  if (joystick.status == JOYSTICK_NOT_AVAILABLE)
-    return FALSE;
-
-  if (!joystick_active || !setup.input[player_nr].use_joystick)
-    return FALSE;
-
-  FadeSetEnterMenu();
-  FadeOut(REDRAW_FIELD);
-
-  ClearField();
-
-  for (y = 0; y < 3; y++)
-  {
-    for (x = 0; x < 3; x++)
-    {
-      DrawFixedGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
-      check[x][y] = FALSE;
-    }
-  }
-
-  DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
-  DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
-  DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
-  DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
-  DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
-  DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
-  DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
-
-  joy_value = JoystickExt(joystick_nr, TRUE);
-  last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
-  last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
-
-  /* eventually uncalibrated center position (joystick could be uncentered) */
-  if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
-    return FALSE;
-
-  new_joystick_xmiddle = joy_x;
-  new_joystick_ymiddle = joy_y;
-
-  DrawFixedGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
-
-  FadeIn(REDRAW_FIELD);
-
-  /* wait for potentially still pressed button to be released */
-  while (JoystickExt(joystick_nr, TRUE) & JOY_BUTTON);
-
-  while (result < 0)
-  {
-    while (PendingEvent())		/* got event */
-    {
-      Event event;
-
-      NextEvent(&event);
-
-      switch (event.type)
-      {
-	case EVENT_KEYPRESS:
-	  switch (GetEventKey((KeyEvent *)&event, TRUE))
-	  {
-	    case KSYM_Return:
-	      if (check_remaining == 0)
-		result = 1;
-	      break;
-
-	    case KSYM_Escape:
-	      FadeSkipNextFadeIn();
-	      result = 0;
-	      break;
-
-	    default:
-	      break;
-	  }
-	  break;
-
-	case EVENT_KEYRELEASE:
-	  key_joystick_mapping = 0;
-	  break;
-
-	default:
-	  HandleOtherEvents(&event);
-	  break;
-      }
-    }
-
-    if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
-      return FALSE;
-
-    new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
-    new_joystick_xright = MAX(new_joystick_xright, joy_x);
-    new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
-    new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
-
-    setup.input[player_nr].joy.xleft = new_joystick_xleft;
-    setup.input[player_nr].joy.yupper = new_joystick_yupper;
-    setup.input[player_nr].joy.xright = new_joystick_xright;
-    setup.input[player_nr].joy.ylower = new_joystick_ylower;
-    setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
-    setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
-
-    CheckJoystickData();
-
-    joy_value = JoystickExt(joystick_nr, TRUE);
-
-    if (joy_value & JOY_BUTTON && check_remaining == 0)
-      result = 1;
-
-    x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
-    y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
-
-    if (x != last_x || y != last_y)
-    {
-      DrawFixedGraphic(xpos + last_x, ypos + last_y,
-		       IMG_MENU_CALIBRATE_YELLOW, 0);
-      DrawFixedGraphic(xpos + x,      ypos + y,
-		       IMG_MENU_CALIBRATE_RED,    0);
-
-      last_x = x;
-      last_y = y;
-
-      if (check_remaining > 0 && !check[x+1][y+1])
-      {
-	check[x+1][y+1] = TRUE;
-	check_remaining--;
-      }
-    }
-
-    BackToFront();
-  }
-
-  /* calibrated center position (joystick should now be centered) */
-  if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
-    return FALSE;
-
-  new_joystick_xmiddle = joy_x;
-  new_joystick_ymiddle = joy_y;
-
-  /* wait until the last pressed button was released */
-  while (JoystickExt(joystick_nr, TRUE) & JOY_BUTTON)
-  {
-    if (PendingEvent())		/* got event */
-    {
-      Event event;
-
-      NextEvent(&event);
-      HandleOtherEvents(&event);
-
-      BackToFront();
-    }
-  }
-
-  return TRUE;
-}
-#endif
-
 /* game controller mapping generator by Gabriel Jacobo <gabomdq at gmail.com> */
 
 #define MARKER_BUTTON		1
diff --git a/src/tape.c b/src/tape.c
index a8ac4ce..e425403 100644
--- a/src/tape.c
+++ b/src/tape.c
@@ -637,11 +637,11 @@ static void TapeAppendRecording()
 
 void TapeHaltRecording()
 {
-  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();
@@ -650,7 +650,8 @@ void TapeHaltRecording()
 
 void TapeStopRecording()
 {
-  TapeHaltRecording();
+  if (tape.recording)
+    TapeHaltRecording();
 
   tape.recording = FALSE;
   tape.pausing = FALSE;
@@ -659,33 +660,10 @@ 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 */
   {
     boolean changed_events = FALSE;
@@ -696,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;
     }
@@ -710,6 +691,33 @@ void TapeRecordAction(byte action_raw[MAX_PLAYERS])
 
     tape.pos[tape.counter].delay++;
   }
+
+  return TRUE;
+}
+
+void TapeRecordAction(byte action_raw[MAX_PLAYERS])
+{
+  byte action[MAX_PLAYERS];
+  int i;
+
+  if (!tape.recording)		/* (record action even when tape is paused) */
+    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 (!TapeAddAction(action))
+    TapeStopRecording();
 }
 
 void TapeTogglePause(boolean toggle_mode)
@@ -1066,13 +1074,23 @@ void TapeQuickLoad()
 
 void InsertSolutionTape()
 {
-  if (!TAPE_IS_EMPTY(tape))
+  boolean level_has_tape = (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
+			    level.native_sp_level->demo.is_available);
+
+  if (!fileExists(getSolutionTapeFilename(level_nr)) && !level_has_tape)
+  {
+    Request("No solution tape for this level!", REQ_CONFIRM);
+
     return;
+  }
+
+  // if tape recorder already contains a tape, remove it without asking
+  TapeErase();
 
   LoadSolutionTape(level_nr);
 
   if (TAPE_IS_EMPTY(tape))
-    Request("No solution tape for this level!", REQ_CONFIRM);
+    Request("Loading solution tape for this level failed!", REQ_CONFIRM);
 
   DrawCompleteVideoDisplay();
 }
@@ -1384,7 +1402,7 @@ static void HandleTapeButtonsExt(int id)
       else
       {
 	if (tape.changed)
-	  SaveTapeChecked(tape.level_nr);
+	  SaveTapeChecked(level_nr);
 
 	TapeErase();
       }
diff --git a/src/tape.h b/src/tape.h
index bc80f44..f4d1904 100644
--- a/src/tape.h
+++ b/src/tape.h
@@ -211,6 +211,7 @@ void TapeSetDateFromNow();
 void TapeStartRecording(int);
 void TapeHaltRecording(void);
 void TapeStopRecording(void);
+boolean TapeAddAction(byte *);
 void TapeRecordAction(byte *);
 void TapeTogglePause(boolean);
 void TapeStartPlaying(void);
diff --git a/src/tools.c b/src/tools.c
index d15f800..1230cf0 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -622,11 +622,24 @@ void DrawFramesPerSecond()
   char text[100];
   int font_nr = FONT_TEXT_2;
   int font_width = getFontWidth(font_nr);
+  int draw_deactivation_mask = GetDrawDeactivationMask();
+  boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
 
-  sprintf(text, "%04.1f fps", global.frames_per_second);
+  /* draw FPS with leading space (needed if field buffer deactivated) */
+  sprintf(text, " %04.1f fps", global.frames_per_second);
 
-  DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
-	      font_nr, BLIT_OPAQUE);
+  /* override draw deactivation mask (required for invisible warp mode) */
+  SetDrawDeactivationMask(REDRAW_NONE);
+
+  /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
+  DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
+	      font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
+
+  /* set draw deactivation mask to previous value */
+  SetDrawDeactivationMask(draw_deactivation_mask);
+
+  /* force full-screen redraw in this frame */
+  redraw_mask = REDRAW_ALL;
 }
 
 #if DEBUG_FRAME_TIME
@@ -2968,7 +2981,7 @@ void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
   redraw_mask |= REDRAW_FIELD;
 }
 
-static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
+static void DrawPreviewLevelPlayfield(int from_x, int from_y)
 {
   boolean show_level_border = (BorderElement != EL_EMPTY);
   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
@@ -3033,9 +3046,8 @@ static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
   return max_text_width / font_width;
 }
 
-static void DrawPreviewLevelLabelExt(int mode)
+static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
 {
-  struct TextPosInfo *pos = &menu.main.text.level_info_2;
   char label_text[MAX_OUTPUT_LINESIZE + 1];
   int max_len_label_text;
   int font_nr = pos->font;
@@ -3078,6 +3090,19 @@ static void DrawPreviewLevelLabelExt(int mode)
   redraw_mask |= REDRAW_FIELD;
 }
 
+static void DrawPreviewLevelLabel(int mode)
+{
+  DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
+}
+
+static void DrawPreviewLevelInfo(int mode)
+{
+  if (mode == MICROLABEL_LEVEL_NAME)
+    DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
+  else if (mode == MICROLABEL_LEVEL_AUTHOR)
+    DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
+}
+
 static void DrawPreviewLevelExt(boolean restart)
 {
   static unsigned int scroll_delay = 0;
@@ -3109,8 +3134,11 @@ static void DrawPreviewLevelExt(boolean restart)
     label_state = 1;
     label_counter = 0;
 
-    DrawPreviewLevelPlayfieldExt(from_x, from_y);
-    DrawPreviewLevelLabelExt(label_state);
+    DrawPreviewLevelPlayfield(from_x, from_y);
+    DrawPreviewLevelLabel(label_state);
+
+    DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
+    DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
 
     /* initialize delay counters */
     DelayReached(&scroll_delay, 0);
@@ -3189,7 +3217,7 @@ static void DrawPreviewLevelExt(boolean restart)
 	break;
     }
 
-    DrawPreviewLevelPlayfieldExt(from_x, from_y);
+    DrawPreviewLevelPlayfield(from_x, from_y);
   }
 
   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
@@ -3230,7 +3258,7 @@ static void DrawPreviewLevelExt(boolean restart)
       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
 		     MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
 
-    DrawPreviewLevelLabelExt(label_state);
+    DrawPreviewLevelLabel(label_state);
   }
 }
 
@@ -3749,12 +3777,10 @@ void WaitForEventToContinue()
 
   while (still_wait)
   {
-    if (PendingEvent())
-    {
-      Event event;
-
-      NextEvent(&event);
+    Event event;
 
+    if (NextValidEvent(&event))
+    {
       switch (event.type)
       {
 	case EVENT_BUTTONPRESS:
@@ -4853,6 +4879,9 @@ unsigned int MoveDoor(unsigned int door_state)
 	SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
 
 	current_move_delay += max_step_delay;
+
+	/* prevent OS (Windows) from complaining about program not responding */
+	CheckQuitEvent();
       }
 
       if (door_part_done_all)
@@ -8469,7 +8498,7 @@ void PlayMenuMusicExt(int music)
 void PlayMenuMusic()
 {
   char *curr_music = getCurrentlyPlayingMusicFilename();
-  char *next_music = getMusicListEntry(menu.music[game_status])->filename;
+  char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
 
   if (!strEqual(curr_music, next_music))
     PlayMenuMusicExt(menu.music[game_status]);
@@ -8489,7 +8518,7 @@ static void FadeMenuSounds()
 static void FadeMenuMusic()
 {
   char *curr_music = getCurrentlyPlayingMusicFilename();
-  char *next_music = getMusicListEntry(menu.music[game_status])->filename;
+  char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
 
   if (!strEqual(curr_music, next_music))
     FadeMusic();

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/rocksndiamonds.git



More information about the Pkg-games-commits mailing list