fixed displaying new high score entry if not on first page
[rocksndiamonds.git] / src / screens.c
index 0a610b07b4015835c35788f5db223dbd93110691..39650b5cf6a4f9aa32acd0fb999518ccbd6815c1 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // screens.c
 // ============================================================================
 // (should also match corresponding entries in src/conf_gfx.c)
 #define SETUP_MODE_MAIN                        0
 #define SETUP_MODE_GAME                        1
-#define SETUP_MODE_EDITOR              2
-#define SETUP_MODE_GRAPHICS            3
-#define SETUP_MODE_SOUND               4
-#define SETUP_MODE_ARTWORK             5
-#define SETUP_MODE_INPUT               6
-#define SETUP_MODE_TOUCH               7
-#define SETUP_MODE_SHORTCUTS           8
-#define SETUP_MODE_SHORTCUTS_1         9
-#define SETUP_MODE_SHORTCUTS_2         10
-#define SETUP_MODE_SHORTCUTS_3         11
-#define SETUP_MODE_SHORTCUTS_4         12
-#define SETUP_MODE_SHORTCUTS_5         13
+#define SETUP_MODE_ENGINES             2
+#define SETUP_MODE_EDITOR              3
+#define SETUP_MODE_GRAPHICS            4
+#define SETUP_MODE_SOUND               5
+#define SETUP_MODE_ARTWORK             6
+#define SETUP_MODE_INPUT               7
+#define SETUP_MODE_TOUCH               8
+#define SETUP_MODE_SHORTCUTS           9
+#define SETUP_MODE_SHORTCUTS_1         10
+#define SETUP_MODE_SHORTCUTS_2         11
+#define SETUP_MODE_SHORTCUTS_3         12
+#define SETUP_MODE_SHORTCUTS_4         13
+#define SETUP_MODE_SHORTCUTS_5         14
 
 // sub-screens on the setup screen (generic)
-#define SETUP_MODE_CHOOSE_ARTWORK      14
-#define SETUP_MODE_CHOOSE_OTHER                15
+#define SETUP_MODE_CHOOSE_ARTWORK      15
+#define SETUP_MODE_CHOOSE_OTHER                16
 
 // sub-screens on the setup screen (specific)
-#define SETUP_MODE_CHOOSE_GAME_SPEED   16
-#define SETUP_MODE_CHOOSE_SCROLL_DELAY 17
-#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE        18
-#define SETUP_MODE_CHOOSE_WINDOW_SIZE  19
-#define SETUP_MODE_CHOOSE_SCALING_TYPE 20
-#define SETUP_MODE_CHOOSE_RENDERING    21
-#define SETUP_MODE_CHOOSE_VSYNC                22
-#define SETUP_MODE_CHOOSE_GRAPHICS     23
-#define SETUP_MODE_CHOOSE_SOUNDS       24
-#define SETUP_MODE_CHOOSE_MUSIC                25
-#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE        26
-#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 27
-#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 28
-#define SETUP_MODE_CHOOSE_TOUCH_CONTROL        29
-#define SETUP_MODE_CHOOSE_MOVE_DISTANCE        30
-#define SETUP_MODE_CHOOSE_DROP_DISTANCE        31
-#define SETUP_MODE_CHOOSE_TRANSPARENCY 32
-#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 33
-#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 34
-#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 35
-#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 36
-#define SETUP_MODE_CONFIG_VIRT_BUTTONS 37
-
-#define MAX_SETUP_MODES                        38
+#define SETUP_MODE_CHOOSE_GAME_SPEED   17
+#define SETUP_MODE_CHOOSE_SCROLL_DELAY 18
+#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE        19
+#define SETUP_MODE_CHOOSE_WINDOW_SIZE  20
+#define SETUP_MODE_CHOOSE_SCALING_TYPE 21
+#define SETUP_MODE_CHOOSE_RENDERING    22
+#define SETUP_MODE_CHOOSE_VSYNC                23
+#define SETUP_MODE_CHOOSE_GRAPHICS     24
+#define SETUP_MODE_CHOOSE_SOUNDS       25
+#define SETUP_MODE_CHOOSE_MUSIC                26
+#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE        27
+#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 28
+#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 29
+#define SETUP_MODE_CHOOSE_TOUCH_CONTROL        30
+#define SETUP_MODE_CHOOSE_MOVE_DISTANCE        31
+#define SETUP_MODE_CHOOSE_DROP_DISTANCE        32
+#define SETUP_MODE_CHOOSE_TRANSPARENCY 33
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 34
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 35
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 36
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 37
+#define SETUP_MODE_CONFIG_VIRT_BUTTONS 38
+
+#define MAX_SETUP_MODES                        39
 
 #define MAX_MENU_MODES                 MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
 
 // setup screen titles
 #define STR_SETUP_MAIN                 "Setup"
 #define STR_SETUP_GAME                 "Game & Menu"
+#define STR_SETUP_ENGINES              "Game Engines"
 #define STR_SETUP_EDITOR               "Editor"
 #define STR_SETUP_GRAPHICS             "Graphics"
 #define STR_SETUP_SOUND                        "Sound & Music"
 #define STR_SETUP_CHOOSE_GRID_XSIZE_1  "Horiz. Buttons"
 #define STR_SETUP_CHOOSE_GRID_YSIZE_1  "Vert. Buttons"
 
+// other screen text constants
+#define STR_CHOOSE_TREE_EDIT           "Edit"
+#define MENU_CHOOSE_TREE_FONT(x)       (FONT_TEXT_1 + (x))
+
 // for input setup functions
 #define SETUPINPUT_SCREEN_POS_START    0
 #define SETUPINPUT_SCREEN_POS_EMPTY1   3
 #define MENU_SCREEN_START_XPOS         1
 #define MENU_SCREEN_START_YPOS         2
 #define MENU_SCREEN_VALUE_XPOS         (SCR_FIELDX - 3)
+#define MENU_SCREEN_TEXT2_XPOS         (SCR_FIELDX - 2)
 #define MENU_SCREEN_MAX_XPOS           (SCR_FIELDX - 1)
 #define MENU_TITLE1_YPOS               8
 #define MENU_TITLE2_YPOS               46
 #define SCREEN_CTRL_ID_NEXT_PLAYER     6
 #define SCREEN_CTRL_ID_INSERT_SOLUTION 7
 #define SCREEN_CTRL_ID_PLAY_SOLUTION   8
-#define SCREEN_CTRL_ID_SCROLL_UP       9
-#define SCREEN_CTRL_ID_SCROLL_DOWN     10
-#define SCREEN_CTRL_ID_SCROLL_VERTICAL 11
-#define SCREEN_CTRL_ID_NETWORK_SERVER  12
-
-#define NUM_SCREEN_GADGETS             13
-
-#define NUM_SCREEN_MENUBUTTONS         9
+#define SCREEN_CTRL_ID_SWITCH_ECS_AGA  9
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 10
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 11
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2        12
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2        13
+#define SCREEN_CTRL_ID_SCROLL_UP       14
+#define SCREEN_CTRL_ID_SCROLL_DOWN     15
+#define SCREEN_CTRL_ID_SCROLL_VERTICAL 16
+#define SCREEN_CTRL_ID_NETWORK_SERVER  17
+
+#define NUM_SCREEN_GADGETS             18
+
+#define NUM_SCREEN_MENUBUTTONS         14
 #define NUM_SCREEN_SCROLLBUTTONS       2
 #define NUM_SCREEN_SCROLLBARS          1
 #define NUM_SCREEN_TEXTINPUT           1
 #define SCREEN_MASK_MAIN               (1 << 0)
 #define SCREEN_MASK_MAIN_HAS_SOLUTION  (1 << 1)
 #define SCREEN_MASK_INPUT              (1 << 2)
+#define SCREEN_MASK_TOUCH              (1 << 3)
+#define SCREEN_MASK_TOUCH2             (1 << 4)
 
 // graphic position and size values for buttons and scrollbars
 #define SC_MENUBUTTON_XSIZE            TILEX
@@ -232,16 +246,19 @@ static void CustomizeKeyboard(int);
 static void ConfigureJoystick(int);
 static void ConfigureVirtualButtons(void);
 static void execSetupGame(void);
+static void execSetupEngines(void);
 static void execSetupGraphics(void);
 static void execSetupSound(void);
 static void execSetupTouch(void);
 static void execSetupArtwork(void);
 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
 
+static void DrawChoosePlayerName(void);
 static void DrawChooseLevelSet(void);
 static void DrawChooseLevelNr(void);
 static void DrawInfoScreen(void);
 static void DrawSetupScreen(void);
+static void DrawTypeName(void);
 
 static void DrawInfoScreen_NotAvailable(char *, char *);
 static void DrawInfoScreen_HelpAnim(int, int, boolean);
@@ -316,6 +333,9 @@ static TreeInfo *transparency_current = NULL;
 static TreeInfo *grid_sizes[2][2] = { { NULL, NULL }, { NULL, NULL } };
 static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } };
 
+static TreeInfo *player_name = NULL;
+static TreeInfo *player_name_current = NULL;
+
 static TreeInfo *level_number = NULL;
 static TreeInfo *level_number_current = NULL;
 
@@ -530,7 +550,9 @@ static int align_yoffset = 0;
 #define DRAW_MODE(s)           ((s) >= GAME_MODE_MAIN &&               \
                                 (s) <= GAME_MODE_SETUP ? (s) :         \
                                 (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
-                                GAME_MODE_MAIN : GAME_MODE_DEFAULT)
+                                GAME_MODE_MAIN :                       \
+                                (s) == GAME_MODE_PSEUDO_TYPENAMES ?    \
+                                GAME_MODE_NAMES : GAME_MODE_DEFAULT)
 
 // (there are no draw offset definitions needed for INFO_MODE_TITLE)
 #define DRAW_MODE_INFO(i)      ((i) >= INFO_MODE_TITLE &&              \
@@ -670,6 +692,8 @@ static char *main_text_title_1                      = NULL;
 static char *main_text_title_2                 = NULL;
 static char *main_text_title_3                 = NULL;
 
+extern char debug_xsn_mode[];
+
 struct MainControlInfo
 {
   int nr;
@@ -918,10 +942,10 @@ static int getTitleSound(struct TitleControlInfo *tci)
   int sound_local = base + nr;
 
 #if 0
-  printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
-        nr, initial, is_image,
-        sound_global, getSoundListEntry(sound_global)->filename,
-        sound_local, getSoundListEntry(sound_local)->filename);
+  Debug("screens:getTitleSound", "%d, %d, %d: %d ['%s'], %d ['%s']",
+       nr, initial, is_image,
+       sound_global, getSoundListEntry(sound_global)->filename,
+       sound_local, getSoundListEntry(sound_local)->filename);
 #endif
 
   if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME))
@@ -948,10 +972,10 @@ static int getTitleMusic(struct TitleControlInfo *tci)
   int music_local = base + nr;
 
 #if 0
-  printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
-        nr, initial, is_image,
-        music_global, getMusicListEntry(music_global)->filename,
-        music_local, getMusicListEntry(music_local)->filename);
+  Debug("screens:getTitleMusic", "%d, %d, %d: %d ['%s'], %d ['%s']",
+       nr, initial, is_image,
+       music_global, getMusicListEntry(music_global)->filename,
+       music_local, getMusicListEntry(music_local)->filename);
 #endif
 
   if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME))
@@ -1316,10 +1340,11 @@ static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
   int rect_y = ALIGNED_TEXT_YPOS(rect);
 
 #if 0
-  printf("::: insideTextPosRect: (%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d\n",
-        x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
-        (x >= rect_x && x < rect_x + rect->width &&
-         y >= rect_y && y < rect_y + rect->height));
+  Debug("screens:insideTextPosRect",
+       "(%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d",
+       x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
+       (x >= rect_x && x < rect_x + rect->width &&
+        y >= rect_y && y < rect_y + rect->height));
 #endif
 
   return (x >= rect_x && x < rect_x + rect->width &&
@@ -1410,6 +1435,40 @@ static void drawChooseTreeCursor(int ypos, boolean active)
   drawCursorExt(0, ypos, active, -1);
 }
 
+static int getChooseTreeEditFont(boolean active)
+{
+  return (active ? FONT_MENU_2_ACTIVE : FONT_MENU_2);
+}
+
+static int getChooseTreeEditXPos(int pos)
+{
+  boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
+  int xoffset = (has_scrollbar ? -1 : 0);
+  int xpos = MENU_SCREEN_TEXT2_XPOS + xoffset;
+  int sx = amSX + xpos * TILEX;
+  int font_nr = getChooseTreeEditFont(FALSE);
+  int width = getTextWidth(STR_CHOOSE_TREE_EDIT, font_nr);
+
+  return (pos == POS_RIGHT ? sx + width - 1 : sx);
+}
+
+static int getChooseTreeEditYPos(int ypos_raw)
+{
+  int ypos = MENU_SCREEN_START_YPOS + ypos_raw;
+  int sy = amSY + ypos * TILEY;
+
+  return sy;
+}
+
+static void drawChooseTreeEdit(int ypos_raw, boolean active)
+{
+  int sx = getChooseTreeEditXPos(POS_LEFT);
+  int sy = getChooseTreeEditYPos(ypos_raw);
+  int font_nr = getChooseTreeEditFont(active);
+
+  DrawText(sx, sy, STR_CHOOSE_TREE_EDIT, font_nr);
+}
+
 static void DrawHeadline(void)
 {
   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, main_text_title_1);
@@ -1570,10 +1629,6 @@ void DrawMainMenu(void)
     return;
   }
 
-  // needed if last screen was the setup screen and fullscreen state changed
-  // (moved to "execSetupGraphics()" to change fullscreen state directly)
-  // ToggleFullscreenOrChangeWindowScalingIfNeeded();
-
   // leveldir_current may be invalid (level group, parent link)
   if (!validLevelSeries(leveldir_current))
     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
@@ -1691,7 +1746,7 @@ static void gotoTopLevelDir(void)
     if (leveldir_current->node_parent->node_group->cl_first == -1)
     {
       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
-      int leveldir_pos = posTreeInfo(leveldir_current);
+      int leveldir_pos = getPosFromTreeInfo(leveldir_current);
       int num_page_entries = MIN(num_leveldirs, NUM_MENU_ENTRIES_ON_SCREEN);
       int cl_first, cl_cursor;
 
@@ -2069,9 +2124,20 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
        }
        else
        {
-         SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
+         if (setup.multiple_users)
+         {
+           CloseDoor(DOOR_CLOSE_2);
+
+           SetGameStatus(GAME_MODE_NAMES);
 
-         HandleTypeName(strlen(setup.player_name), 0);
+           DrawChoosePlayerName();
+         }
+         else
+         {
+           SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
+
+           DrawTypeName();
+         }
        }
       }
       else if (pos == MAIN_CONTROL_LEVELS)
@@ -2102,10 +2168,10 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       else if (pos == MAIN_CONTROL_EDITOR)
       {
        if (leveldir_current->readonly &&
-           !strEqual(setup.player_name, "Artsoft"))
-         Request("This level is read only!", REQ_CONFIRM);
+           setup.editor.show_read_only_warning)
+         Request("This level is read-only!", REQ_CONFIRM | REQ_STAY_OPEN);
 
-       CloseDoor(DOOR_CLOSE_2);
+       CloseDoor(DOOR_CLOSE_ALL);
 
        SetGameStatus(GAME_MODE_EDITOR);
 
@@ -2418,6 +2484,9 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
   static int num_page_entries_all_last[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
   static int choice_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
   static int first_entry_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
+  boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
+  int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
+  int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
   int *num_page_entries_last = num_page_entries_all_last[game_status];
   int *choice_store = choice_stores[game_status];
   int *first_entry_store = first_entry_stores[game_status];
@@ -2639,7 +2708,7 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
 
   if (!anyScrollbarGadgetActive() &&
       IN_VIS_MENU(x, y) &&
-      mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
+      mx < mx_right_border &&
       y >= 0 && y < num_page_entries)
   {
     if (button)
@@ -3671,7 +3740,9 @@ static void DrawInfoScreen_Version(void)
   }
 
   DrawTextF(xstart1, ystart, font_head, "Platform");
-  DrawTextF(xstart2, ystart, font_text, PLATFORM_STRING);
+  DrawTextF(xstart2, ystart, font_text, "%s (%s)",
+           PLATFORM_STRING,
+           PLATFORM_XX_BIT_STRING);
   ystart += ystep_line;
 
   DrawTextF(xstart1, ystart, font_head, "Target");
@@ -3680,6 +3751,10 @@ static void DrawInfoScreen_Version(void)
 
   DrawTextF(xstart1, ystart, font_head, "Source date");
   DrawTextF(xstart2, ystart, font_text, getSourceDateString());
+  ystart += ystep_line;
+
+  DrawTextF(xstart1, ystart, font_head, "Commit hash");
+  DrawTextF(xstart2, ystart, font_text, getSourceHashString());
   ystart += ystep_para;
 
   DrawTextF(xstart1, ystart, font_head, "Library");
@@ -3749,16 +3824,26 @@ static void DrawInfoScreen_Version(void)
   DrawTextF(xstart3, ystart, font_head, "Used");
   ystart += ystep_head;
 
-  driver_name = getStringCopyNStatic(SDL_GetVideoDriver(0), driver_name_len);
+  driver_name =
+    getStringCopyNStatic(SDLGetRendererName(), driver_name_len);
+
+  DrawTextF(xstart1, ystart, font_text, "Render Driver");
+  DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_renderdriver);
+  DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
+  ystart += ystep_line;
+
+  driver_name =
+    getStringCopyNStatic(SDL_GetCurrentVideoDriver(), driver_name_len);
 
-  DrawTextF(xstart1, ystart, font_text, "SDL_VideoDriver");
+  DrawTextF(xstart1, ystart, font_text, "Video Driver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
   ystart += ystep_line;
 
-  driver_name = getStringCopyNStatic(SDL_GetAudioDriver(0), driver_name_len);
+  driver_name =
+    getStringCopyNStatic(SDL_GetCurrentAudioDriver(), driver_name_len);
 
-  DrawTextF(xstart1, ystart, font_text, "SDL_AudioDriver");
+  DrawTextF(xstart1, ystart, font_text, "Audio Driver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
 
@@ -3932,80 +4017,279 @@ void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
 // type name functions
 // ============================================================================
 
-void HandleTypeName(int newxpos, Key key)
+static TreeInfo *type_name_node = NULL;
+static char type_name_last[MAX_PLAYER_NAME_LEN + 1] = { 0 };
+static int type_name_nr = 0;
+
+static int getPlayerNameColor(char *name)
+{
+  return (strEqual(name, EMPTY_PLAYER_NAME) ? FC_BLUE : FC_RED);
+}
+
+static void drawTypeNameText(char *name, struct TextPosInfo *pos,
+                             boolean active)
+{
+  char text[MAX_PLAYER_NAME_LEN + 2] = { 0 };
+  boolean multiple_users = (game_status == GAME_MODE_PSEUDO_TYPENAMES);
+  int sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos));
+  int sy = (multiple_users ? amSY + pos->y : mSY + ALIGNED_TEXT_YPOS(pos));
+  int font_nr = (active ? FONT_ACTIVE(pos->font) : pos->font);
+  int font_width = getFontWidth(font_nr);
+
+  DrawBackgroundForFont(sx, sy, pos->width, pos->height, font_nr);
+
+  sprintf(text, "%s%c", name, (active ? '_' : '\0'));
+
+  pos->width = strlen(text) * font_width;
+  sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos));
+
+  DrawText(sx, sy, text, font_nr);
+}
+
+static void getTypeNameValues(char *name, struct TextPosInfo *pos, int *xpos)
 {
-  static char last_player_name[MAX_PLAYER_NAME_LEN + 1];
   struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
-  struct TextPosInfo *pos = mci->pos_input;
-  int startx = mSX + ALIGNED_TEXT_XPOS(pos);
-  int starty = mSY + ALIGNED_TEXT_YPOS(pos);
-  static int xpos = 0;
-  int font_nr = pos->font;
-  int font_active_nr = FONT_ACTIVE(font_nr);
-  int font_width = getFontWidth(font_active_nr);
-  char key_char = getValidConfigValueChar(getCharFromKey(key));
-  boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
-  boolean is_active = TRUE;
 
-  DrawBackgroundForFont(startx,starty, pos->width, pos->height, font_active_nr);
+  *pos = *mci->pos_input;
 
-  if (newxpos)
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
   {
-    strcpy(last_player_name, setup.player_name);
+    TreeInfo *ti = player_name_current;
+    int first_entry = ti->cl_first;
+    int entry_pos = first_entry + ti->cl_cursor;
+    TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
+    int xpos = MENU_SCREEN_START_XPOS;
+    int ypos = MENU_SCREEN_START_YPOS + ti->cl_cursor;
+
+    type_name_node = getTreeInfoFromPos(node_first, entry_pos);
+    type_name_nr = entry_pos;
 
-    xpos = newxpos;
+    strcpy(name, type_name_node->name);
 
-    StartTextInput(startx, starty, pos->width, pos->height);
+    pos->x = xpos * 32;
+    pos->y = ypos * 32;
+    pos->width = MAX_PLAYER_NAME_LEN * 32;
   }
-  else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
+  else
   {
-    setup.player_name[xpos] = key_char;
-    setup.player_name[xpos + 1] = 0;
+    type_name_nr = user.nr;
 
-    xpos++;
+    strcpy(name, setup.player_name);
   }
-  else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
+
+  strcpy(type_name_last, name);
+
+  if (strEqual(name, EMPTY_PLAYER_NAME))
+    strcpy(name, "");
+
+  *xpos = strlen(name);
+}
+
+static void setTypeNameValues_Name(char *name, struct TextPosInfo *pos)
+{
+  // change name of edited user in global list of user names
+  setString(&global.user_names[type_name_nr], name);
+
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
   {
-    xpos--;
+    TreeInfo *node = type_name_node;
+
+    // change name of edited user in local menu tree structure
+    setString(&node->name, name);
+    setString(&node->name_sorting, name);
 
-    setup.player_name[xpos] = 0;
+    node->color = getPlayerNameColor(name);
+    pos->font = MENU_CHOOSE_TREE_FONT(node->color);
   }
-  else if (key == KSYM_Return && xpos > 0)
+}
+
+static void setTypeNameValues(char *name, struct TextPosInfo *pos,
+                             boolean changed)
+{
+  boolean reset_setup = strEqual(name, "");
+  boolean remove_user = strEqual(name, EMPTY_PLAYER_NAME);
+  boolean create_user = strEqual(type_name_last, EMPTY_PLAYER_NAME);
+
+  if (!changed)
+    strcpy(name, type_name_last);
+
+  if (strEqual(name, ""))
+    strcpy(name, EMPTY_PLAYER_NAME);
+
+  setTypeNameValues_Name(name, pos);
+
+  // if player name not changed, no further action required
+  if (strEqual(name, type_name_last))
+    return;
+
+  // redraw player name before (possibly) opening request dialogs
+  drawTypeNameText(name, pos, FALSE);
+
+  int last_user_nr = user.nr;
+
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
   {
+    // save setup of currently active user (may differ from edited user)
     SaveSetup();
 
-    is_active = FALSE;
+    // temporarily change active user to edited user
+    user.nr = type_name_nr;
 
-    SetGameStatus(GAME_MODE_MAIN);
+    // load setup of edited user (unless creating user with current setup)
+    if (!create_user ||
+       !Request("Use current setup values for the new player?", REQ_ASK))
+      LoadSetup();
   }
-  else if (key == KSYM_Escape)
+
+  char *setup_filename = getSetupFilename();
+  boolean setup_exists = fileExists(setup_filename);
+
+  // change name of edited user in setup structure
+  strcpy(setup.player_name, name);
+
+  // save setup of edited user
+  SaveSetup();
+
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES || reset_setup)
   {
-    strcpy(setup.player_name, last_player_name);
+    if (reset_setup)
+    {
+      if (Request("Reset setup values for this player?", REQ_ASK))
+      {
+       // remove setup config file
+       unlink(setup_filename);
 
-    is_active = FALSE;
+       // set player name to default player name
+       LoadSetup();
 
-    SetGameStatus(GAME_MODE_MAIN);
+       // update player name used by name typing functions
+       strcpy(name, setup.player_name);
+
+       setTypeNameValues_Name(name, pos);
+      }
+    }
+    else if (remove_user && type_name_nr != 0)
+    {
+      if (Request("Remove settings and tapes for deleted player?", REQ_ASK))
+      {
+       char *user_dir = getUserGameDataDir();
+       char *user_dir_removed =
+         getStringCat3WithSeparator(user_dir, "REMOVED",
+                                    getCurrentTimestamp(), ".");
+
+       if (rename(user_dir, user_dir_removed) != 0)
+         Request("Removing settings and tapes failed!", REQ_CONFIRM);
+
+       checked_free(user_dir_removed);
+      }
+    }
+    else if (create_user && type_name_nr != 0 && !setup_exists)
+    {
+      if (Request("Create empty level set for the new player?", REQ_ASK))
+      {
+       char *levelset_subdir = getNewUserLevelSubdir();
+
+       if (CreateUserLevelSet(levelset_subdir, name, name, 100, FALSE))
+       {
+         AddUserLevelSetToLevelInfo(levelset_subdir);
+
+         LevelDirTree *leveldir_current_last = leveldir_current;
+
+         leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
+                                                      levelset_subdir);
+
+         // set level number of newly created level set to default value
+         LoadLevelSetup_SeriesInfo();
+
+         // set newly created level set as current level set for new user
+         SaveLevelSetup_LastSeries();
+         SaveLevelSetup_SeriesInfo();
+
+         leveldir_current = leveldir_current_last;
+       }
+       else
+       {
+         Request("Creating new level set failed!", REQ_CONFIRM);
+       }
+      }
+    }
+
+    // restore currently active user
+    user.nr = last_user_nr;
+
+    // restore setup of currently active user
+    LoadSetup();
+
+    // restore last level set of currently active user
+    LoadLevelSetup_LastSeries();
+    LoadLevelSetup_SeriesInfo();
   }
+}
 
-  if (is_active)
+static void HandleTypeNameExt(boolean initialize, Key key)
+{
+  static struct TextPosInfo pos_name = { 0 };
+  static char name[MAX_PLAYER_NAME_LEN + 1] = { 0 };
+  static int xpos = 0;
+  struct TextPosInfo *pos = &pos_name;
+  char key_char = getValidConfigValueChar(getCharFromKey(key));
+  boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
+  boolean active = TRUE;
+
+  if (initialize)
   {
-    pos->width = (strlen(setup.player_name) + 1) * font_width;
-    startx = mSX + ALIGNED_TEXT_XPOS(pos);
+    getTypeNameValues(name, pos, &xpos);
 
-    DrawText(startx, starty, setup.player_name, font_active_nr);
-    DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
+    int sx = mSX + ALIGNED_TEXT_XPOS(pos);
+    int sy = mSY + ALIGNED_TEXT_YPOS(pos);
+
+    StartTextInput(sx, sy, pos->width, pos->height);
   }
-  else
+  else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
   {
-    pos->width = strlen(setup.player_name) * font_width;
-    startx = mSX + ALIGNED_TEXT_XPOS(pos);
+    name[xpos] = key_char;
+    name[xpos + 1] = 0;
 
-    DrawText(startx, starty, setup.player_name, font_nr);
+    xpos++;
+  }
+  else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
+  {
+    xpos--;
+
+    name[xpos] = 0;
+  }
+  else if (key == KSYM_Return || key == KSYM_Escape)
+  {
+    boolean changed = (key == KSYM_Return);
 
     StopTextInput();
+
+    setTypeNameValues(name, pos, changed);
+
+    active = FALSE;
+  }
+
+  drawTypeNameText(name, pos, active);
+
+  if (!active)
+  {
+    SetGameStatus(game_status_last_screen);
+
+    if (game_status == GAME_MODE_MAIN)
+      InitializeMainControls();
   }
 }
 
+static void DrawTypeName(void)
+{
+  HandleTypeNameExt(TRUE, 0);
+}
+
+void HandleTypeName(Key key)
+{
+  HandleTypeNameExt(FALSE, key);
+}
+
 
 // ============================================================================
 // tree menu functions
@@ -4025,7 +4309,8 @@ static int getAlignXOffsetFromTreeInfo(TreeInfo *ti)
 
   int num_entries = numTreeInfoInGroup(ti);
   boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
-  int text_width = max_text_size * getFontWidth(FONT_TEXT_1);
+  int font_nr = MENU_CHOOSE_TREE_FONT(FC_RED);
+  int text_width = max_text_size * getFontWidth(font_nr);
   int button_width = SC_MENUBUTTON_XSIZE;
   int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
   int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
@@ -4045,7 +4330,8 @@ static int getAlignYOffsetFromTreeInfo(TreeInfo *ti)
 
   int num_entries = numTreeInfoInGroup(ti);
   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
-  int font_height = getFontHeight(FONT_TEXT_1);
+  int font_nr = MENU_CHOOSE_TREE_FONT(FC_RED);
+  int font_height = getFontHeight(font_nr);
   int text_height = font_height * num_page_entries;
   int page_height = font_height * NUM_MENU_ENTRIES_ON_SCREEN;
   int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].valign;
@@ -4082,7 +4368,9 @@ static void DrawChooseTree(TreeInfo **ti_ptr)
   // needed if different viewport properties defined for choosing level (set)
   ChangeViewportPropertiesIfNeeded();
 
-  if (game_status == GAME_MODE_LEVELNR)
+  if (game_status == GAME_MODE_NAMES)
+    SetMainBackgroundImage(IMG_BACKGROUND_NAMES);
+  else if (game_status == GAME_MODE_LEVELNR)
     SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
   else if (game_status == GAME_MODE_LEVELS)
     SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
@@ -4091,8 +4379,8 @@ static void DrawChooseTree(TreeInfo **ti_ptr)
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
 
-  HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
   MapScreenTreeGadgets(*ti_ptr);
+  HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
 
   DrawMaskedBorder(fade_mask);
 
@@ -4106,8 +4394,6 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
   boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
   int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
   int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
-  int font_nr = FONT_TEXT_1;
-  int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
   int i;
   char *title_string = NULL;
   int yoffset_sets = MENU_TITLE1_YPOS;
@@ -4125,6 +4411,12 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
   {
     TreeInfo *node, *node_first;
     int entry_pos = first_entry + i;
+
+    node_first = getTreeInfoFirstGroupEntry(ti);
+    node = getTreeInfoFromPos(node_first, entry_pos);
+
+    int font_nr = MENU_CHOOSE_TREE_FONT(node->color);
+    int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
     int xpos = MENU_SCREEN_START_XPOS;
     int ypos = MENU_SCREEN_START_YPOS + i;
     int startx = amSX + xpos * 32;
@@ -4135,13 +4427,10 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
     int max_buffer_len = max_text_size / getFontWidth(font_nr);
     char buffer[max_buffer_len + 1];
 
-    node_first = getTreeInfoFirstGroupEntry(ti);
-    node = getTreeInfoFromPos(node_first, entry_pos);
-
     strncpy(buffer, node->name, max_buffer_len);
     buffer[max_buffer_len] = '\0';
 
-    DrawText(startx, starty, buffer, font_nr + node->color);
+    DrawText(startx, starty, buffer, font_nr);
 
     if (node->parent_link)
       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
@@ -4149,6 +4438,9 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
     else
       initCursor(i, IMG_MENU_BUTTON);
+
+    if (game_status == GAME_MODE_NAMES)
+      drawChooseTreeEdit(i, FALSE);
   }
 
   redraw_mask |= REDRAW_FIELD;
@@ -4193,6 +4485,11 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
                             TreeInfo **ti_ptr)
 {
   TreeInfo *ti = *ti_ptr;
+  boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
+  int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
+  int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
+  int sx1_edit_name = getChooseTreeEditXPos(POS_LEFT);
+  int sx2_edit_name = getChooseTreeEditXPos(POS_RIGHT);
   int x = 0;
   int y = ti->cl_cursor;
   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
@@ -4203,7 +4500,7 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
   if (button == MB_MENU_INITIALIZE)
   {
     int num_entries = numTreeInfoInGroup(ti);
-    int entry_pos = posTreeInfo(ti);
+    int entry_pos = getPosFromTreeInfo(ti);
 
     align_xoffset = getAlignXOffsetFromTreeInfo(ti);
     align_yoffset = getAlignYOffsetFromTreeInfo(ti);
@@ -4294,6 +4591,9 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
   {
     x = (mx - amSX) / 32;
     y = (my - amSY) / 32 - MENU_SCREEN_START_YPOS;
+
+    if (game_status == GAME_MODE_NAMES)
+      drawChooseTreeEdit(ti->cl_cursor, FALSE);
   }
   else if (dx || dy)   // keyboard or scrollbar/scrollbutton input
   {
@@ -4387,11 +4687,17 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
   if (!anyScrollbarGadgetActive() &&
       IN_VIS_MENU(x, y) &&
-      mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
+      mx < mx_right_border &&
       y >= 0 && y < num_page_entries)
   {
     if (button)
     {
+      if (game_status == GAME_MODE_NAMES)
+      {
+       if (mx >= sx1_edit_name && mx <= sx2_edit_name)
+         drawChooseTreeEdit(y, TRUE);
+      }
+
       if (y != ti->cl_cursor)
       {
        PlaySound(SND_MENU_ITEM_ACTIVATING);
@@ -4511,6 +4817,39 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
            HandleMainMenu_SelectLevel(0, 0, new_level_nr);
          }
+         else if (game_status == GAME_MODE_NAMES)
+         {
+           if (mx >= sx1_edit_name && mx <= sx2_edit_name)
+           {
+             SetGameStatus(GAME_MODE_PSEUDO_TYPENAMES);
+
+             DrawTypeName();
+
+             return;
+           }
+
+           // change active user to selected user
+           user.nr = entry_pos;
+
+           // save number of new active user
+           SaveUserSetup();
+
+           // load setup of new active user
+           LoadSetup();
+
+           // load last level set of new active user
+           LoadLevelSetup_LastSeries();
+           LoadLevelSetup_SeriesInfo();
+
+           TapeErase();
+
+           ToggleFullscreenIfNeeded();
+           ChangeWindowScalingIfNeeded();
+
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_GRAPHICS);
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_SOUNDS);
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_MUSIC);
+         }
 
          SetGameStatus(GAME_MODE_MAIN);
 
@@ -4521,6 +4860,60 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
   }
 }
 
+void DrawChoosePlayerName(void)
+{
+  int i;
+
+  FadeMenuSoundsAndMusic();
+
+  if (player_name != NULL)
+  {
+    freeTreeInfo(player_name);
+
+    player_name = NULL;
+  }
+
+  for (i = 0; i < MAX_PLAYER_NAMES; i++)
+  {
+    TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_PLAYER_NAME);
+    char identifier[32], name[MAX_PLAYER_NAME_LEN + 1];
+    int value = i;
+
+    ti->node_top = &player_name;
+    ti->sort_priority = 10000 + value;
+    ti->color = getPlayerNameColor(global.user_names[i]);
+
+    snprintf(identifier, sizeof(identifier), "%d", value);
+    snprintf(name, sizeof(name), "%s", global.user_names[i]);
+
+    setString(&ti->identifier, identifier);
+    setString(&ti->name, name);
+    setString(&ti->name_sorting, name);
+
+    pushTreeInfo(&player_name, ti);
+  }
+
+  // sort player entries by player number
+  sortTreeInfo(&player_name);
+
+  // set current player entry to selected player entry
+  player_name_current =
+    getTreeInfoFromIdentifier(player_name, i_to_a(user.nr));
+
+  // if that fails, set current player name to first available name
+  if (player_name_current == NULL)
+    player_name_current = player_name;
+
+  DrawChooseTree(&player_name_current);
+
+  PlayMenuSoundsAndMusic();
+}
+
+void HandleChoosePlayerName(int mx, int my, int dx, int dy, int button)
+{
+  HandleChooseTree(mx, my, dx, dy, button, &player_name_current);
+}
+
 void DrawChooseLevelSet(void)
 {
   FadeMenuSoundsAndMusic();
@@ -4687,9 +5080,15 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
   if (button == MB_MENU_INITIALIZE)
   {
     level_nr = mx;
-    first_entry = 0;
     highlight_position = my;
 
+    first_entry = highlight_position - (NUM_MENU_ENTRIES_ON_SCREEN + 1) / 2 + 1;
+
+    if (first_entry < 0)
+      first_entry = 0;
+    else if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
+      first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
+
     drawHallOfFameList(level_nr, first_entry, highlight_position);
 
     return;
@@ -5010,6 +5409,13 @@ static void execSetupChooseSnapshotMode(void)
   DrawSetupScreen();
 }
 
+static void execSetupEngines(void)
+{
+  setup_mode = SETUP_MODE_ENGINES;
+
+  DrawSetupScreen();
+}
+
 static void execSetupEditor(void)
 {
   setup_mode = SETUP_MODE_EDITOR;
@@ -5267,13 +5673,21 @@ static void execSetupGraphics(void)
   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
     execSetupGraphics_setWindowSizes(FALSE);
 
+  // update "setup.vsync_mode" from list selection
+  // (in this case, vsync mode was changed on setup screen)
+  if (setup_mode == SETUP_MODE_CHOOSE_VSYNC)
+    execSetupGraphics_setVsyncModes(FALSE);
+
   // update list selection from "setup.window_scaling_percent"
   // (window scaling may have changed by resizing the window)
   execSetupGraphics_setWindowSizes(TRUE);
 
+  // update list selection from "setup.vsync_mode"
+  // (vsync_mode may have changed by re-creating the renderer)
+  execSetupGraphics_setVsyncModes(TRUE);
+
   execSetupGraphics_setScalingTypes();
   execSetupGraphics_setRenderingModes();
-  execSetupGraphics_setVsyncModes(FALSE);
 
   setup_mode = SETUP_MODE_GRAPHICS;
 
@@ -5284,7 +5698,7 @@ static void execSetupGraphics(void)
     ModifyGameSpeedIfNeeded();
 
   // window scaling may have changed at this point
-  ToggleFullscreenOrChangeWindowScalingIfNeeded();
+  ChangeWindowScalingIfNeeded();
 
   // window scaling quality may have changed at this point
   if (!strEqual(setup.window_scaling_quality, video.window_scaling_quality))
@@ -5293,8 +5707,23 @@ static void execSetupGraphics(void)
   // screen rendering mode may have changed at this point
   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
 
+  int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
+  int video_vsync_mode = video.vsync_mode;
+
   // screen vsync mode may have changed at this point
-  SDLSetScreenVsyncMode(setup.vsync_mode);
+  ChangeVsyncModeIfNeeded();
+
+  // check if setting vsync mode to selected value failed
+  if (setup_vsync_mode != video_vsync_mode &&
+      setup_vsync_mode != video.vsync_mode)
+  {
+    // changing vsync mode to selected value failed -- reset displayed value
+    execSetupGraphics_setVsyncModes(TRUE);
+
+    Request("Setting VSync failed!", REQ_CONFIRM);
+
+    DrawSetupScreen();
+  }
 }
 
 static void execSetupChooseWindowSize(void)
@@ -5897,10 +6326,10 @@ static void execSetupTouch(void)
 static void execSetupArtwork(void)
 {
 #if 0
-  printf("::: '%s', '%s', '%s'\n",
-        artwork.gfx_current->subdir,
-        artwork.gfx_current->fullpath,
-        artwork.gfx_current->basepath);
+  Debug("screens:execSetupArtwork", "'%s', '%s', '%s'",
+       artwork.gfx_current->subdir,
+       artwork.gfx_current->fullpath,
+       artwork.gfx_current->basepath);
 #endif
 
   setup.graphics_set = artwork.gfx_current->identifier;
@@ -6187,6 +6616,7 @@ static struct
   { &setup.touch.grid_ysize[1],                &grid_size_text[1][1]           },
 
   { &setup.internal.menu_game,         execSetupGame                   },
+  { &setup.internal.menu_engines,      execSetupEngines                },
   { &setup.internal.menu_editor,       execSetupEditor                 },
   { &setup.internal.menu_graphics,     execSetupGraphics               },
   { &setup.internal.menu_sound,                execSetupSound                  },
@@ -6212,6 +6642,7 @@ void setHideRelatedSetupEntries(void)
 static struct TokenInfo setup_info_main[] =
 {
   { TYPE_ENTER_MENU,   execSetupGame,          STR_SETUP_GAME          },
+  { TYPE_ENTER_MENU,   execSetupEngines,       STR_SETUP_ENGINES       },
   { TYPE_ENTER_MENU,   execSetupEditor,        STR_SETUP_EDITOR        },
   { TYPE_ENTER_MENU,   execSetupGraphics,      STR_SETUP_GRAPHICS      },
   { TYPE_ENTER_MENU,   execSetupSound,         STR_SETUP_SOUND         },
@@ -6233,6 +6664,7 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_PLAYER,       &setup.network_player_nr,"Preferred Network Player:" },
   { TYPE_TEXT_INPUT,   execGadgetNetworkServer, "Network Server Hostname:" },
   { TYPE_STRING,       &network_server_text,   ""                      },
+  { TYPE_SWITCH,       &setup.multiple_users,  "Multiple Users/Teams:" },
   { TYPE_YES_NO,       &setup.input_on_focus,  "Only Move Focussed Player:" },
   { TYPE_SWITCH,       &setup.time_limit,      "Time Limit:"           },
   { TYPE_SWITCH,       &setup.handicap,        "Handicap:"             },
@@ -6258,6 +6690,22 @@ static struct TokenInfo setup_info_game[] =
   { 0,                 NULL,                   NULL                    }
 };
 
+static struct TokenInfo setup_info_engines[] =
+{
+  { TYPE_HEADLINE,     NULL,                   "Emerald Mine"          },
+  { TYPE_SWITCH,       &setup.forced_scroll_delay, "Scroll Delay:"     },
+  { TYPE_ECS_AGA,      &setup.prefer_aga_graphics, "Amiga Graphics Chipset:" },
+  { TYPE_SWITCH,       &setup.prefer_lowpass_sounds,"Low-Pass Filter Sounds:" },
+  { TYPE_SWITCH,       &setup.prefer_extra_panel_items,"Show Dynamite and Keys:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_HEADLINE,     NULL,                   "Supaplex"              },
+  { TYPE_SWITCH,       &setup.sp_show_border_elements, "Border Elements:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 0,                 NULL,                   NULL                    }
+};
+
 static struct TokenInfo setup_info_editor[] =
 {
 #if 0
@@ -6287,6 +6735,8 @@ static struct TokenInfo setup_info_editor[] =
 #endif
   { TYPE_SWITCH, &setup.editor.show_element_token,     "Show element token:" },
   { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_SWITCH, &setup.editor.show_read_only_warning, "Show read-only warning:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
 
   { 0,                 NULL,                   NULL                    }
@@ -6314,9 +6764,8 @@ static struct TokenInfo setup_info_graphics[] =
   { TYPE_SWITCH,       &setup.quick_doors,     "Quick Menu Doors:"     },
   { TYPE_SWITCH,       &setup.show_titlescreen,"Show Title Screens:"   },
   { TYPE_SWITCH,       &setup.toons,           "Show Menu Animations:" },
-  { TYPE_ECS_AGA,      &setup.prefer_aga_graphics,"EMC graphics preference:" },
-  { TYPE_SWITCH, &setup.sp_show_border_elements,"Supaplex Border Elements:" },
   { TYPE_SWITCH,       &setup.small_game_graphics, "Small Game Graphics:" },
+  { TYPE_YES_NO_AUTO,  &setup.debug.xsn_mode,  debug_xsn_mode          },
   { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
 
@@ -6687,6 +7136,10 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
 
     if (startx + font_xoffset < text_startx + text_width + text_font_xoffset)
     {
+      // when using narrow font, left-shifting text "auto" not needed
+      if (type & TYPE_YES_NO_AUTO)
+       xpos += 1;
+
       xpos += 1;
       startx = mSX + xpos * 32;
 
@@ -6803,7 +7256,7 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
 
   // fullscreen state may have changed at this point
   if (si->value == &setup.fullscreen)
-    ToggleFullscreenOrChangeWindowScalingIfNeeded();
+    ToggleFullscreenIfNeeded();
 
   // network mode may have changed at this point
   if (si->value == &setup.network_mode)
@@ -6886,6 +7339,11 @@ static void DrawSetupScreen_Generic(void)
     setup_info = setup_info_game;
     title_string = STR_SETUP_GAME;
   }
+  else if (setup_mode == SETUP_MODE_ENGINES)
+  {
+    setup_info = setup_info_engines;
+    title_string = STR_SETUP_ENGINES;
+  }
   else if (setup_mode == SETUP_MODE_EDITOR)
   {
     setup_info = setup_info_editor;
@@ -7468,7 +7926,7 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
   Event event;
   int alpha = 200, alpha_step = -1;
   int alpha_ticks = 0;
-  char mapping[4096], temp[4096];
+  char mapping[4096], temp[256];
   int font_name = MENU_SETUP_FONT_TITLE;
   int font_info = MENU_SETUP_FONT_TEXT;
   int spacing_name = menu.line_spacing_setup[SETUP_MODE_INPUT];
@@ -7531,9 +7989,9 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
 
 #if DEBUG_JOYSTICKS
   // print info about the joystick we are watching
-  Error(ERR_DEBUG, "watching joystick %d: (%s)\n",
+  Debug("joystick", "watching joystick %d: (%s)",
        SDL_JoystickInstanceID(joystick), name);
-  Error(ERR_DEBUG, "joystick has %d axes, %d hats, %d balls, and %d buttons\n",
+  Debug("joystick", "joystick has %d axes, %d hats, %d balls, and %d buttons",
        SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
        SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
 #endif
@@ -7790,7 +8248,7 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
   if (success)
   {
 #if DEBUG_JOYSTICKS
-    Error(ERR_DEBUG, "New game controller mapping:\n\n%s\n\n", mapping);
+    Debug("joystick", "New game controller mapping:\n\n%s\n\n", mapping);
 #endif
 
     // activate mapping for this game
@@ -7886,6 +8344,22 @@ void ConfigureJoystick(int player_nr)
   DrawSetupScreen_Input();
 }
 
+static void MapScreenMenuGadgets_OverlayTouchButtons(int y)
+{
+  if (y < video.screen_height / 3)
+  {
+    // remap touch gadgets to access upper part of the screen
+    UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH);
+    MapScreenMenuGadgets(SCREEN_MASK_TOUCH2);
+  }
+  else if (y > 2 * video.screen_height / 3)
+  {
+    // remap touch gadgets to access lower part of the screen
+    MapScreenMenuGadgets(SCREEN_MASK_TOUCH);
+    UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH2);
+  }
+}
+
 static boolean ConfigureVirtualButtonsMain(void)
 {
   static char *customize_step_text[] =
@@ -7906,6 +8380,13 @@ static boolean ConfigureVirtualButtonsMain(void)
     CHAR_GRID_BUTTON_SNAP,
     CHAR_GRID_BUTTON_DROP
   };
+  enum
+  {
+    ACTION_NONE,
+    ACTION_ESCAPE,
+    ACTION_BACK,
+    ACTION_NEXT
+  };
   int font_nr = FONT_INPUT_1_ACTIVE;
   int font_height = getFontHeight(font_nr);
   int ypos1 = SYSIZE / 2 - font_height * 2;
@@ -7926,6 +8407,8 @@ static boolean ConfigureVirtualButtonsMain(void)
 
   overlay.grid_button_highlight = grid_button[step_nr];
 
+  UnmapAllGadgets();
+
   FadeSetEnterMenu();
   FadeOut(REDRAW_FIELD);
 
@@ -7939,80 +8422,45 @@ static boolean ConfigureVirtualButtonsMain(void)
 
   SetOverlayShowGrid(TRUE);
 
+  // map gadgets for setup touch buttons menu screen
+  MapScreenMenuGadgets(SCREEN_MASK_TOUCH);
+
   while (!finished)
   {
     Event event;
 
     while (NextValidEvent(&event))
     {
+      int action = ACTION_NONE;
+
+      // ---------- handle events and set the resulting action ----------
+
       switch (event.type)
       {
+       case EVENT_USER:
+         {
+           UserEvent *user = (UserEvent *)&event;
+           int id = user->value1;
+
+           action = (id == SCREEN_CTRL_ID_TOUCH_PREV_PAGE ||
+                     id == SCREEN_CTRL_ID_TOUCH_PREV_PAGE2 ? ACTION_BACK :
+                     id == SCREEN_CTRL_ID_TOUCH_NEXT_PAGE ||
+                     id == SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2 ? ACTION_NEXT :
+                     ACTION_NONE);
+         }
+         break;
+
         case EVENT_KEYPRESS:
          {
            Key key = GetEventKey((KeyEvent *)&event, FALSE);
 
-           // press 'Escape' to abort and keep the old key bindings
-           if (key == KSYM_Escape)
-           {
-             for (x = 0; x < MAX_GRID_XSIZE; x++)
-               for (y = 0; y < MAX_GRID_YSIZE; y++)
-                 overlay.grid_button[x][y] = grid_button_old[x][y];
-
-             FadeSkipNextFadeIn();
-
-             finished = TRUE;
-
-             break;
-           }
-
-           // press 'Enter' to keep the existing key binding
-           if (key == KSYM_Return ||
-               key == KSYM_Menu ||
-               key == KSYM_space)
-           {
-             step_nr++;
-           }
-           else if (key == KSYM_BackSpace ||
-                    key == KSYM_Back)
-           {
-             if (step_nr == 0)
-             {
-               FadeSkipNextFadeIn();
-
-               finished = TRUE;
-
-               break;
-             }
-
-             step_nr--;
-           }
-           else
-           {
-             break;
-           }
-
-           // all virtual buttons configured
-           if (step_nr == 6)
-           {
-             finished = TRUE;
-             success = TRUE;
-
-             break;
-           }
-
-           for (x = 0; x < MAX_GRID_XSIZE; x++)
-             for (y = 0; y < MAX_GRID_YSIZE; y++)
-               grid_button_tmp[x][y] = overlay.grid_button[x][y];
-
-           overlay.grid_button_highlight = grid_button[step_nr];
-
-           // query next virtual button
-
-           ClearField();
-
-           DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
-           DrawTextSCentered(ypos1, font_nr, "Select tiles to");
-           DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
+           action = (key == KSYM_Escape ?      ACTION_ESCAPE :
+                     key == KSYM_BackSpace ||
+                     key == KSYM_Back ?        ACTION_BACK :
+                     key == KSYM_Return ||
+                     key == KSYM_Menu ||
+                     key == KSYM_space ?       ACTION_NEXT :
+                     ACTION_NONE);
          }
          break;
 
@@ -8025,6 +8473,19 @@ static boolean ConfigureVirtualButtonsMain(void)
          {
            ButtonEvent *button = (ButtonEvent *)&event;
 
+           motion_status = FALSE;
+
+           if (button->type == EVENT_BUTTONPRESS)
+             button_status = button->button;
+           else
+             button_status = MB_RELEASED;
+
+           if (HandleGadgets(button->x, button->y, button_status))
+           {
+             // do not handle this button event anymore
+             break;
+           }
+
            button->x += video.screen_xoffset;
            button->y += video.screen_yoffset;
 
@@ -8033,18 +8494,14 @@ static boolean ConfigureVirtualButtonsMain(void)
 
            if (button->type == EVENT_BUTTONPRESS)
            {
-             button_status = button->button;
-
              grid_button_draw =
                (overlay.grid_button[x][y] != grid_button[step_nr] ?
                 grid_button[step_nr] : CHAR_GRID_BUTTON_NONE);
 
              set_grid_button = TRUE;
            }
-           else
-           {
-             button_status = MB_RELEASED;
-           }
+
+           MapScreenMenuGadgets_OverlayTouchButtons(button->y);
          }
          break;
 
@@ -8052,6 +8509,14 @@ static boolean ConfigureVirtualButtonsMain(void)
          {
            MotionEvent *motion = (MotionEvent *)&event;
 
+           motion_status = TRUE;
+
+           if (HandleGadgets(motion->x, motion->y, button_status))
+           {
+             // do not handle this button event anymore
+             break;
+           }
+
            motion->x += video.screen_xoffset;
            motion->y += video.screen_yoffset;
 
@@ -8059,6 +8524,8 @@ static boolean ConfigureVirtualButtonsMain(void)
            y = motion->y * overlay.grid_ysize / video.screen_height;
 
            set_grid_button = TRUE;
+
+           MapScreenMenuGadgets_OverlayTouchButtons(motion->y);
          }
          break;
 
@@ -8090,6 +8557,64 @@ static boolean ConfigureVirtualButtonsMain(void)
          break;
       }
 
+      // ---------- perform action set by handling events ----------
+
+      if (action == ACTION_ESCAPE)
+      {
+       // abort and restore the old key bindings
+
+       for (x = 0; x < MAX_GRID_XSIZE; x++)
+         for (y = 0; y < MAX_GRID_YSIZE; y++)
+           overlay.grid_button[x][y] = grid_button_old[x][y];
+
+       FadeSkipNextFadeIn();
+
+       finished = TRUE;
+      }
+      else if (action == ACTION_BACK)
+      {
+       // keep the configured key bindings and go to previous page
+
+       step_nr--;
+
+       if (step_nr < 0)
+       {
+         FadeSkipNextFadeIn();
+
+         finished = TRUE;
+       }
+      }
+      else if (action == ACTION_NEXT)
+      {
+       // keep the configured key bindings and go to next page
+
+       step_nr++;
+
+       // all virtual buttons configured
+       if (step_nr == 6)
+       {
+         finished = TRUE;
+         success = TRUE;
+       }
+      }
+
+      if (action != ACTION_NONE && !finished)
+      {
+       for (x = 0; x < MAX_GRID_XSIZE; x++)
+         for (y = 0; y < MAX_GRID_YSIZE; y++)
+           grid_button_tmp[x][y] = overlay.grid_button[x][y];
+
+       overlay.grid_button_highlight = grid_button[step_nr];
+
+       // configure next virtual button
+
+       ClearField();
+
+       DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
+       DrawTextSCentered(ypos1, font_nr, "Select tiles to");
+       DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
+      }
+
       if (set_grid_button)
       {
        overlay.grid_button[x][y] =
@@ -8119,6 +8644,9 @@ void ConfigureVirtualButtons(void)
 {
   boolean success = ConfigureVirtualButtonsMain();
 
+  UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH |
+                        SCREEN_MASK_TOUCH2);
+
   if (success)
   {
     int font_nr = FONT_TITLE_1;
@@ -8295,7 +8823,7 @@ void HandleGameActions(void)
   GameActions();       // main game loop
 
   if (tape.auto_play && !tape.playing)
-    AutoPlayTape();    // continue automatically playing next tape
+    AutoPlayTapes();   // continue automatically playing next tape
 }
 
 
@@ -8305,83 +8833,125 @@ static struct
 {
   int gfx_unpressed, gfx_pressed;
   struct MenuPosInfo *pos;
+  boolean *check_value;
   int gadget_id;
   int screen_mask;
   unsigned int event_mask;
+  boolean is_touch_button;
   char *infotext;
 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
 {
   {
     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
-    &menu.main.button.prev_level,
+    &menu.main.button.prev_level, NULL,
     SCREEN_CTRL_ID_PREV_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
-    "previous level"
+    FALSE, "previous level"
   },
   {
     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
-    &menu.main.button.next_level,
+    &menu.main.button.next_level, NULL,
     SCREEN_CTRL_ID_NEXT_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
-    "next level"
+    FALSE, "next level"
   },
   {
     IMG_MENU_BUTTON_FIRST_LEVEL, IMG_MENU_BUTTON_FIRST_LEVEL_ACTIVE,
-    &menu.main.button.first_level,
+    &menu.main.button.first_level, NULL,
     SCREEN_CTRL_ID_FIRST_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
-    "first level"
+    FALSE, "first level"
   },
   {
     IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE,
-    &menu.main.button.last_level,
+    &menu.main.button.last_level, NULL,
     SCREEN_CTRL_ID_LAST_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
-    "last level"
+    FALSE, "last level"
   },
   {
     IMG_MENU_BUTTON_LEVEL_NUMBER, IMG_MENU_BUTTON_LEVEL_NUMBER_ACTIVE,
-    &menu.main.button.level_number,
+    &menu.main.button.level_number, NULL,
     SCREEN_CTRL_ID_LEVEL_NUMBER,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
-    "level number"
+    FALSE, "level number"
   },
   {
     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
-    &menu.setup.button.prev_player,
+    &menu.setup.button.prev_player, NULL,
     SCREEN_CTRL_ID_PREV_PLAYER,
     SCREEN_MASK_INPUT,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
-    "previous player"
+    FALSE, "previous player"
   },
   {
     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
-    &menu.setup.button.next_player,
+    &menu.setup.button.next_player, NULL,
     SCREEN_CTRL_ID_NEXT_PLAYER,
     SCREEN_MASK_INPUT,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
-    "next player"
+    FALSE, "next player"
   },
   {
     IMG_MENU_BUTTON_INSERT_SOLUTION, IMG_MENU_BUTTON_INSERT_SOLUTION_ACTIVE,
-    &menu.main.button.insert_solution,
+    &menu.main.button.insert_solution, NULL,
     SCREEN_CTRL_ID_INSERT_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
     GD_EVENT_RELEASED,
-    "insert solution tape"
+    FALSE, "insert solution tape"
   },
   {
     IMG_MENU_BUTTON_PLAY_SOLUTION, IMG_MENU_BUTTON_PLAY_SOLUTION_ACTIVE,
-    &menu.main.button.play_solution,
+    &menu.main.button.play_solution, NULL,
     SCREEN_CTRL_ID_PLAY_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
     GD_EVENT_RELEASED,
-    "play solution tape"
+    FALSE, "play solution tape"
+  },
+  {
+    IMG_MENU_BUTTON_SWITCH_ECS_AGA, IMG_MENU_BUTTON_SWITCH_ECS_AGA_ACTIVE,
+    &menu.main.button.switch_ecs_aga, &setup.prefer_aga_graphics,
+    SCREEN_CTRL_ID_SWITCH_ECS_AGA,
+    SCREEN_MASK_MAIN,
+    GD_EVENT_RELEASED | GD_EVENT_OFF_BORDERS,
+    FALSE, "switch ECS/AGA chipset"
+  },
+  {
+    IMG_MENU_BUTTON_TOUCH_BACK, IMG_MENU_BUTTON_TOUCH_BACK,
+    &menu.setup.button.touch_back, NULL,
+    SCREEN_CTRL_ID_TOUCH_PREV_PAGE,
+    SCREEN_MASK_TOUCH,
+    GD_EVENT_RELEASED,
+    TRUE, "previous page"
+  },
+  {
+    IMG_MENU_BUTTON_TOUCH_NEXT, IMG_MENU_BUTTON_TOUCH_NEXT,
+    &menu.setup.button.touch_next, NULL,
+    SCREEN_CTRL_ID_TOUCH_NEXT_PAGE,
+    SCREEN_MASK_TOUCH,
+    GD_EVENT_RELEASED,
+    TRUE, "next page"
+  },
+  {
+    IMG_MENU_BUTTON_TOUCH_BACK2, IMG_MENU_BUTTON_TOUCH_BACK2,
+    &menu.setup.button.touch_back2, NULL,
+    SCREEN_CTRL_ID_TOUCH_PREV_PAGE2,
+    SCREEN_MASK_TOUCH2,
+    GD_EVENT_RELEASED,
+    TRUE, "previous page"
+  },
+  {
+    IMG_MENU_BUTTON_TOUCH_NEXT2, IMG_MENU_BUTTON_TOUCH_NEXT2,
+    &menu.setup.button.touch_next2, NULL,
+    SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2,
+    SCREEN_MASK_TOUCH2,
+    GD_EVENT_RELEASED,
+    TRUE, "next page"
   },
 };
 
@@ -8456,16 +9026,21 @@ static void CreateScreenMenubuttons(void)
   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
   {
     struct MenuPosInfo *pos = menubutton_info[i].pos;
+    boolean is_touch_button = menubutton_info[i].is_touch_button;
+    boolean is_check_button = menubutton_info[i].check_value != NULL;
     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
     int gfx_unpressed, gfx_pressed;
     int x, y, width, height;
     int gd_x1, gd_x2, gd_y1, gd_y2;
+    int gd_x1a, gd_x2a, gd_y1a, gd_y2a;
     int id = menubutton_info[i].gadget_id;
+    int type = GD_TYPE_NORMAL_BUTTON;
+    boolean checked = FALSE;
 
     event_mask = menubutton_info[i].event_mask;
 
-    x = mSX + GDI_ACTIVE_POS(pos->x);
-    y = mSY + GDI_ACTIVE_POS(pos->y);
+    x = (is_touch_button ? pos->x : mSX + GDI_ACTIVE_POS(pos->x));
+    y = (is_touch_button ? pos->y : mSY + GDI_ACTIVE_POS(pos->y));
 
     width  = graphic_info[menubutton_info[i].gfx_pressed].width;
     height = graphic_info[menubutton_info[i].gfx_pressed].height;
@@ -8478,6 +9053,27 @@ static void CreateScreenMenubuttons(void)
     gd_y1 = graphic_info[gfx_unpressed].src_y;
     gd_x2 = graphic_info[gfx_pressed].src_x;
     gd_y2 = graphic_info[gfx_pressed].src_y;
+    gd_x1a = gd_x1;
+    gd_y1a = gd_y1;
+    gd_x2a = gd_x2;
+    gd_y2a = gd_y2;
+
+    if (is_touch_button)
+    {
+      gd_x2 += graphic_info[gfx_pressed].pressed_xoffset;
+      gd_y2 += graphic_info[gfx_pressed].pressed_yoffset;
+    }
+
+    if (is_check_button)
+    {
+      gd_x1a += graphic_info[gfx_unpressed].active_xoffset;
+      gd_y1a += graphic_info[gfx_unpressed].active_yoffset;
+      gd_x2a += graphic_info[gfx_pressed].active_xoffset;
+      gd_y2a += graphic_info[gfx_pressed].active_yoffset;
+
+      type = GD_TYPE_CHECK_BUTTON;
+      checked = *menubutton_info[i].check_value;
+    }
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
@@ -8487,17 +9083,21 @@ static void CreateScreenMenubuttons(void)
                      GDI_Y, y,
                      GDI_WIDTH, width,
                      GDI_HEIGHT, height,
-                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_TYPE, type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_CHECKED, checked,
                      GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
                      GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
+                      GDI_ALT_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1a, gd_y1a,
+                      GDI_ALT_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2a, gd_y2a,
                      GDI_DIRECT_DRAW, FALSE,
+                     GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleScreenGadgets,
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8565,7 +9165,7 @@ static void CreateScreenScrollbuttons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8646,7 +9246,7 @@ static void CreateScreenScrollbars(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8692,7 +9292,7 @@ static void CreateScreenTextInputGadgets(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8819,8 +9419,22 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       PlaySolutionTape();
       break;
 
+    case SCREEN_CTRL_ID_SWITCH_ECS_AGA:
+      setup.prefer_aga_graphics = !setup.prefer_aga_graphics;
+      DrawMainMenu();
+      break;
+
+    case SCREEN_CTRL_ID_TOUCH_PREV_PAGE:
+    case SCREEN_CTRL_ID_TOUCH_NEXT_PAGE:
+    case SCREEN_CTRL_ID_TOUCH_PREV_PAGE2:
+    case SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2:
+      PushUserEvent(USEREVENT_GADGET_PRESSED, id, 0);
+      break;
+
     case SCREEN_CTRL_ID_SCROLL_UP:
-      if (game_status == GAME_MODE_LEVELS)
+      if (game_status == GAME_MODE_NAMES)
+       HandleChoosePlayerName(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
+      else if (game_status == GAME_MODE_LEVELS)
        HandleChooseLevelSet(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
       else if (game_status == GAME_MODE_LEVELNR)
        HandleChooseLevelNr(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
@@ -8831,7 +9445,9 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       break;
 
     case SCREEN_CTRL_ID_SCROLL_DOWN:
-      if (game_status == GAME_MODE_LEVELS)
+      if (game_status == GAME_MODE_NAMES)
+       HandleChoosePlayerName(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
+      else if (game_status == GAME_MODE_LEVELS)
        HandleChooseLevelSet(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
       else if (game_status == GAME_MODE_LEVELNR)
        HandleChooseLevelNr(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
@@ -8842,7 +9458,9 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       break;
 
     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
-      if (game_status == GAME_MODE_LEVELS)
+      if (game_status == GAME_MODE_NAMES)
+       HandleChoosePlayerName(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
+      else if (game_status == GAME_MODE_LEVELS)
        HandleChooseLevelSet(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
       else if (game_status == GAME_MODE_LEVELNR)
        HandleChooseLevelNr(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);