added reading credits info (global or level set specific) from files
[rocksndiamonds.git] / src / screens.c
index b8c34820eecdb12837868ce57088e204a65c3222..7a6bfe41b9fff219d2430bec383145f9f6d53a38 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_SCORES_TYPE  17
+#define SETUP_MODE_CHOOSE_GAME_SPEED   18
+#define SETUP_MODE_CHOOSE_SCROLL_DELAY 19
+#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE        20
+#define SETUP_MODE_CHOOSE_WINDOW_SIZE  21
+#define SETUP_MODE_CHOOSE_SCALING_TYPE 22
+#define SETUP_MODE_CHOOSE_RENDERING    23
+#define SETUP_MODE_CHOOSE_VSYNC                24
+#define SETUP_MODE_CHOOSE_GRAPHICS     25
+#define SETUP_MODE_CHOOSE_SOUNDS       26
+#define SETUP_MODE_CHOOSE_MUSIC                27
+#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE        28
+#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 29
+#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 30
+#define SETUP_MODE_CHOOSE_TOUCH_CONTROL        31
+#define SETUP_MODE_CHOOSE_MOVE_DISTANCE        32
+#define SETUP_MODE_CHOOSE_DROP_DISTANCE        33
+#define SETUP_MODE_CHOOSE_TRANSPARENCY 34
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 35
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 36
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 37
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 38
+#define SETUP_MODE_CONFIG_VIRT_BUTTONS 39
+
+#define MAX_SETUP_MODES                        40
 
 #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_EXIT                 "Exit"
 #define STR_SETUP_SAVE_AND_EXIT                "Save and Exit"
 
+#define STR_SETUP_CHOOSE_SCORES_TYPE   "Scores Type"
 #define STR_SETUP_CHOOSE_GAME_SPEED    "Game Speed"
 #define STR_SETUP_CHOOSE_SCROLL_DELAY  "Scroll Delay"
 #define STR_SETUP_CHOOSE_SNAPSHOT_MODE "Snapshot Mode"
 #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))
+#define MENU_CHOOSE_TREE_COLOR(ti, a)  TREE_COLOR(ti, a)
+
 // 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
 // screen gadget identifiers
 #define SCREEN_CTRL_ID_PREV_LEVEL      0
 #define SCREEN_CTRL_ID_NEXT_LEVEL      1
-#define SCREEN_CTRL_ID_FIRST_LEVEL     2
-#define SCREEN_CTRL_ID_LAST_LEVEL      3
-#define SCREEN_CTRL_ID_LEVEL_NUMBER    4
-#define SCREEN_CTRL_ID_PREV_PLAYER     5
-#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_TOUCH_PREV_PAGE 9
-#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 10
-#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2        11
-#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2        12
-#define SCREEN_CTRL_ID_SCROLL_UP       13
-#define SCREEN_CTRL_ID_SCROLL_DOWN     14
-#define SCREEN_CTRL_ID_SCROLL_VERTICAL 15
-#define SCREEN_CTRL_ID_NETWORK_SERVER  16
-
-#define NUM_SCREEN_GADGETS             17
-
-#define NUM_SCREEN_MENUBUTTONS         13
+#define SCREEN_CTRL_ID_PREV_LEVEL2     2
+#define SCREEN_CTRL_ID_NEXT_LEVEL2     3
+#define SCREEN_CTRL_ID_PREV_SCORE      4
+#define SCREEN_CTRL_ID_NEXT_SCORE      5
+#define SCREEN_CTRL_ID_FIRST_LEVEL     6
+#define SCREEN_CTRL_ID_LAST_LEVEL      7
+#define SCREEN_CTRL_ID_LEVEL_NUMBER    8
+#define SCREEN_CTRL_ID_PREV_PLAYER     9
+#define SCREEN_CTRL_ID_NEXT_PLAYER     10
+#define SCREEN_CTRL_ID_INSERT_SOLUTION 11
+#define SCREEN_CTRL_ID_PLAY_SOLUTION   12
+#define SCREEN_CTRL_ID_SWITCH_ECS_AGA  13
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 14
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 15
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2        16
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2        17
+
+#define NUM_SCREEN_MENUBUTTONS         18
+
+#define SCREEN_CTRL_ID_SCROLL_UP       18
+#define SCREEN_CTRL_ID_SCROLL_DOWN     19
+#define SCREEN_CTRL_ID_SCROLL_VERTICAL 20
+#define SCREEN_CTRL_ID_NETWORK_SERVER  21
+
+#define NUM_SCREEN_GADGETS             22
+
 #define NUM_SCREEN_SCROLLBUTTONS       2
 #define NUM_SCREEN_SCROLLBARS          1
 #define NUM_SCREEN_TEXTINPUT           1
 #define SCREEN_MASK_INPUT              (1 << 2)
 #define SCREEN_MASK_TOUCH              (1 << 3)
 #define SCREEN_MASK_TOUCH2             (1 << 4)
+#define SCREEN_MASK_SCORES             (1 << 5)
+#define SCREEN_MASK_SCORES_INFO                (1 << 6)
 
 // graphic position and size values for buttons and scrollbars
 #define SC_MENUBUTTON_XSIZE            TILEX
@@ -238,37 +257,54 @@ 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 DrawScoreInfo(int);
+static void DrawScoreInfo_Content(int);
 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);
 static void DrawInfoScreen_HelpText(int, int, int, int);
 static void HandleInfoScreen_Main(int, int, int, int, int);
-static void HandleInfoScreen_TitleScreen(int);
-static void HandleInfoScreen_Elements(int);
-static void HandleInfoScreen_Music(int);
-static void HandleInfoScreen_Credits(int);
+static void HandleInfoScreen_TitleScreen(int, int, int);
+static void HandleInfoScreen_Elements(int, int, int);
+static void HandleInfoScreen_Music(int, int, int);
+static void HandleInfoScreen_Credits(int, int, int);
 static void HandleInfoScreen_Program(int);
 static void HandleInfoScreen_Version(int);
 
 static void ModifyGameSpeedIfNeeded(void);
 static void DisableVsyncIfNeeded(void);
 
+static void RedrawScreenMenuGadgets(int);
 static void MapScreenMenuGadgets(int);
 static void UnmapScreenMenuGadgets(int);
 static void MapScreenGadgets(int);
+static void UnmapScreenGadgets(void);
 static void MapScreenTreeGadgets(TreeInfo *);
+static void UnmapScreenTreeGadgets(void);
 
 static void UpdateScreenMenuGadgets(int, boolean);
+static void AdjustScoreInfoButtons(int, int, int);
+
+static boolean OfferUploadTapes(void);
+static void execOfferUploadTapes(void);
+
+static void DrawHallOfFame_setScoreEntries(void);
+static void HandleHallOfFame_SelectLevel(int, int);
+static char *getHallOfFameRankText(int, int);
+static char *getHallOfFameScoreText(int, int);
 
 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
 
@@ -293,6 +329,9 @@ static TreeInfo *scroll_delay_current = NULL;
 static TreeInfo *snapshot_modes = NULL;
 static TreeInfo *snapshot_mode_current = NULL;
 
+static TreeInfo *scores_types = NULL;
+static TreeInfo *scores_type_current = NULL;
+
 static TreeInfo *game_speeds_normal = NULL;
 static TreeInfo *game_speeds_extended = NULL;
 static TreeInfo *game_speeds = NULL;
@@ -322,9 +361,15 @@ 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;
 
+static TreeInfo *score_entries = NULL;
+static TreeInfo *score_entry_current = NULL;
+
 static struct ValueTextInfo window_sizes_list[] =
 {
   {    50,     "50 %"                          },
@@ -374,6 +419,15 @@ static struct StringValueTextInfo vsync_modes_list[] =
   {    NULL,                            NULL           },
 };
 
+static struct StringValueTextInfo scores_types_list[] =
+{
+  {    STR_SCORES_TYPE_LOCAL_ONLY,         "Local scores only"         },
+  {    STR_SCORES_TYPE_SERVER_ONLY,        "Server scores only"        },
+  {    STR_SCORES_TYPE_LOCAL_AND_SERVER,   "Local and server scores"   },
+
+  {    NULL,                           NULL            },
+};
+
 static struct ValueTextInfo game_speeds_list_normal[] =
 {
   {    30,     "Very Slow"                     },
@@ -536,7 +590,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 &&              \
@@ -570,6 +626,10 @@ static int align_yoffset = 0;
                                 menu.extra_spacing[GAME_MODE_SETUP] :  \
                                 menu.extra_spacing_setup[DRAW_MODE_SETUP(i)])
 
+#define EXTRA_SPACING_SCORES(i)        (EXTRA_SPACING_INFO(i))
+
+#define EXTRA_SPACING_SCOREINFO(i) (menu.extra_spacing[GAME_MODE_SCOREINFO])
+
 #define DRAW_XOFFSET(s)                ((s) == GAME_MODE_INFO ?                \
                                 DRAW_XOFFSET_INFO(info_mode) :         \
                                 (s) == GAME_MODE_SETUP ?               \
@@ -584,6 +644,8 @@ static int align_yoffset = 0;
                                 EXTRA_SPACING_INFO(info_mode) :        \
                                 (s) == GAME_MODE_SETUP ?               \
                                 EXTRA_SPACING_SETUP(setup_mode) :      \
+                                (s) == GAME_MODE_SCORES ?              \
+                                EXTRA_SPACING_SCORES(info_mode) :      \
                                 menu.extra_spacing[DRAW_MODE(s)])
 
 #define mSX                    (SX + DRAW_XOFFSET(game_status))
@@ -621,6 +683,13 @@ struct TitleControlInfo
 
 struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS];
 
+
+// credits screens definitions
+
+static int num_credits_screens = 0;
+static boolean use_global_credits_screens = FALSE;
+
+
 // main menu display and control definitions
 
 #define MAIN_CONTROL_NAME                      0
@@ -676,6 +745,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;
@@ -924,10 +995,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))
@@ -954,10 +1025,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))
@@ -1322,10 +1393,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 &&
@@ -1356,10 +1428,10 @@ static void AdjustScrollbar(int id, int items_max, int items_visible,
               GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
 }
 
-static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
+static void AdjustChooseTreeScrollbar(TreeInfo *ti, int id)
 {
   AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN,
-                 first_entry);
+                 ti->cl_first);
 }
 
 static void clearMenuListArea(void)
@@ -1416,6 +1488,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);
@@ -1576,22 +1682,19 @@ 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);
+  // leveldir_current may be invalid (level group, parent link, node copy)
+  leveldir_current = getValidLevelSeries(leveldir_current, leveldir_last_valid);
 
   if (leveldir_current != leveldir_last_valid)
+  {
+    UpdateLastPlayedLevels_TreeInfo();
+
     levelset_has_changed = TRUE;
+  }
 
   // store valid level series information
   leveldir_last_valid = leveldir_current;
 
-  init_last = init;                    // switch to new busy animation
-
   // needed if last screen (level choice) changed graphics, sounds or music
   ReloadCustomArtwork(0);
 
@@ -1685,6 +1788,11 @@ void DrawMainMenu(void)
   SetMouseCursor(CURSOR_DEFAULT);
 
   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
+
+  SyncEmscriptenFilesystem();
+
+  // needed once after program start or after user change
+  CheckApiServerTasks();
 }
 
 static void gotoTopLevelDir(void)
@@ -1697,7 +1805,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;
 
@@ -1817,7 +1925,7 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
   {
     return_to_main_menu = TRUE;
   }
-  else if (button == MB_MENU_CHOICE)
+  else if (button == MB_MENU_CHOICE || dx)
   {
     if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0)
     {
@@ -1830,9 +1938,10 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
       return;
     }
 
-    title_screen_nr++;
+    title_screen_nr +=
+      (game_status_last_screen == GAME_MODE_INFO && dx < 0 ? -1 : +1);
 
-    if (title_screen_nr < num_title_screens)
+    if (title_screen_nr >= 0 && title_screen_nr < num_title_screens)
     {
       tci = &title_controls[title_screen_nr];
 
@@ -1902,6 +2011,16 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
   }
 }
 
+static void HandleMainMenu_ToggleTeamMode(void)
+{
+  setup.team_mode = !setup.team_mode;
+
+  InitializeMainControls();
+  DrawCursorAndText_Main(MAIN_CONTROL_NAME, TRUE, FALSE);
+
+  DrawPreviewPlayers();
+}
+
 static void HandleMainMenu_SelectLevel(int step, int direction,
                                       int selected_level_nr)
 {
@@ -2051,9 +2170,16 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       }
       else if (dx != 0)
       {
-       if (choice != MAIN_CONTROL_INFO &&
-           choice != MAIN_CONTROL_SETUP)
+       if (choice == MAIN_CONTROL_NAME)
+       {
+         // special case: cursor left or right pressed -- toggle team mode
+         HandleMainMenu_ToggleTeamMode();
+       }
+       else if (choice != MAIN_CONTROL_INFO &&
+                choice != MAIN_CONTROL_SETUP)
+       {
          HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
+       }
       }
     }
     else
@@ -2066,18 +2192,24 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
            insideTextPosRect(main_controls[i].pos_text, mx - mSX, my - mSY))
        {
          // special case: menu text "name/team" clicked -- toggle team mode
-         setup.team_mode = !setup.team_mode;
-
-         InitializeMainControls();
-         DrawCursorAndText_Main(choice, TRUE, FALSE);
-
-         DrawPreviewPlayers();
+         HandleMainMenu_ToggleTeamMode();
        }
        else
        {
-         SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
+         if (setup.multiple_users)
+         {
+           CloseDoor(DOOR_CLOSE_2);
+
+           SetGameStatus(GAME_MODE_NAMES);
+
+           DrawChoosePlayerName();
+         }
+         else
+         {
+           SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
 
-         HandleTypeName(strlen(setup.player_name), 0);
+           DrawTypeName();
+         }
        }
       }
       else if (pos == MAIN_CONTROL_LEVELS)
@@ -2091,6 +2223,9 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
          SaveLevelSetup_LastSeries();
          SaveLevelSetup_SeriesInfo();
 
+         // restore level set if chosen from "last played level set" menu
+         RestoreLastPlayedLevels(&leveldir_current);
+
          if (setup.internal.choose_from_top_leveldir)
            gotoTopLevelDir();
 
@@ -2103,15 +2238,15 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
        SetGameStatus(GAME_MODE_SCORES);
 
-       DrawHallOfFame(level_nr, -1);
+       DrawHallOfFame(level_nr);
       }
       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);
 
@@ -2148,8 +2283,13 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
        SaveLevelSetup_LastSeries();
        SaveLevelSetup_SeriesInfo();
 
-        if (Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED))
+#if defined(PLATFORM_EMSCRIPTEN)
+       Request("Close the browser window to quit!", REQ_CONFIRM);
+#else
+       if (!setup.ask_on_quit_program ||
+           Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED))
          SetGameStatus(GAME_MODE_QUIT);
+#endif
       }
     }
   }
@@ -2424,6 +2564,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];
@@ -2645,7 +2788,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)
@@ -2955,9 +3098,9 @@ static void DrawInfoScreen_TitleScreen(void)
   DrawTitleScreen();
 }
 
-void HandleInfoScreen_TitleScreen(int button)
+void HandleInfoScreen_TitleScreen(int dx, int dy, int button)
 {
-  HandleTitleScreen(0, 0, 0, 0, button);
+  HandleTitleScreen(0, 0, dx, dy, button);
 }
 
 static void DrawInfoScreen_Elements(void)
@@ -2969,12 +3112,12 @@ static void DrawInfoScreen_Elements(void)
   LoadHelpAnimInfo();
   LoadHelpTextInfo();
 
-  HandleInfoScreen_Elements(MB_MENU_INITIALIZE);
+  HandleInfoScreen_Elements(0, 0, MB_MENU_INITIALIZE);
 
   FadeIn(REDRAW_FIELD);
 }
 
-void HandleInfoScreen_Elements(int button)
+void HandleInfoScreen_Elements(int dx, int dy, int button)
 {
   static unsigned int info_delay = 0;
   static int num_anims;
@@ -3013,16 +3156,16 @@ void HandleInfoScreen_Elements(int button)
 
     return;
   }
-  else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
+  else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx)
   {
     if (button != MB_MENU_INITIALIZE)
     {
       PlaySound(SND_MENU_ITEM_SELECTING);
 
-      page++;
+      page += (dx < 0 ? -1 : +1);
     }
 
-    if (page >= num_pages)
+    if (page < 0 || page >= num_pages)
     {
       FadeMenuSoundsAndMusic();
 
@@ -3032,7 +3175,7 @@ void HandleInfoScreen_Elements(int button)
       return;
     }
 
-    if (page > 0)
+    if (button != MB_MENU_INITIALIZE)
       FadeSetNextScreen();
 
     if (button != MB_MENU_INITIALIZE)
@@ -3064,12 +3207,12 @@ static void DrawInfoScreen_Music(void)
 
   LoadMusicInfo();
 
-  HandleInfoScreen_Music(MB_MENU_INITIALIZE);
+  HandleInfoScreen_Music(0, 0, MB_MENU_INITIALIZE);
 
   FadeIn(REDRAW_FIELD);
 }
 
-void HandleInfoScreen_Music(int button)
+void HandleInfoScreen_Music(int dx, int dy, int button)
 {
   static struct MusicFileInfo *list = NULL;
   int font_title = MENU_INFO_FONT_TITLE;
@@ -3115,14 +3258,14 @@ void HandleInfoScreen_Music(int button)
 
     return;
   }
-  else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
+  else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx)
   {
     if (button != MB_MENU_INITIALIZE)
     {
       PlaySound(SND_MENU_ITEM_SELECTING);
 
       if (list != NULL)
-       list = list->next;
+       list = (dx < 0 ? list->prev : list->next);
     }
 
     if (list == NULL)
@@ -3150,7 +3293,7 @@ void HandleInfoScreen_Music(int button)
     {
       int sound = list->music;
 
-      if (sound_info[sound].loop)
+      if (IS_LOOP_SOUND(sound))
        PlaySoundLoop(sound);
       else
        PlaySound(sound);
@@ -3161,7 +3304,7 @@ void HandleInfoScreen_Music(int button)
     {
       int music = list->music;
 
-      if (music_info[music].loop)
+      if (IS_LOOP_MUSIC(music))
        PlayMusicLoop(music);
       else
        PlayMusic(music);
@@ -3229,24 +3372,18 @@ void HandleInfoScreen_Music(int button)
       FadeIn(REDRAW_FIELD);
   }
 
-  if (list != NULL && list->is_sound && sound_info[list->music].loop)
+  if (list != NULL && list->is_sound && IS_LOOP_SOUND(list->music))
     PlaySoundLoop(list->music);
 }
 
 static void DrawInfoScreen_CreditsScreen(int screen_nr)
 {
   int font_title = MENU_INFO_FONT_TITLE;
-  int font_head  = MENU_INFO_FONT_HEAD;
   int font_text  = MENU_INFO_FONT_TEXT;
   int font_foot  = MENU_INFO_FONT_FOOT;
   int spacing_title = menu.headline1_spacing_info[info_mode];
-  int spacing_head  = menu.headline2_spacing_info[info_mode];
-  int spacing_para  = menu.paragraph_spacing_info[info_mode];
   int spacing_line  = menu.line_spacing_info[info_mode];
   int ystep_title = getMenuTextStep(spacing_title, font_title);
-  int ystep_head  = getMenuTextStep(spacing_head,  font_head);
-  int ystep_para  = getMenuTextStep(spacing_para,  font_text);
-  int ystep_line  = getMenuTextStep(spacing_line,  font_text);
   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
 
@@ -3254,211 +3391,21 @@ static void DrawInfoScreen_CreditsScreen(int screen_nr)
   DrawHeadline();
 
   DrawTextSCentered(ystart, font_title, "Credits:");
-  ystart += ystep_title;
 
-  if (screen_nr == 0)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Special thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Peter Liepa");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for creating");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "\"Boulder Dash\"");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "in the year");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "1984");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "published by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "First Star Software");
-  }
-  else if (screen_nr == 1)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Special thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Klaus Heinz & Volker Wertich");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for creating");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "\"Emerald Mine\"");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "in the year");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "1987");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "published by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Kingsoft");
-  }
-  else if (screen_nr == 2)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Special thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Michael Stopp & Philip Jespersen");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for creating");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "\"Supaplex\"");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "in the year");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "1991");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "published by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Digital Integration");
-  }
-  else if (screen_nr == 3)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Special thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Hiroyuki Imabayashi");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for creating");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "\"Sokoban\"");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "in the year");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "1982");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "published by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Thinking Rabbit");
-  }
-  else if (screen_nr == 4)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Special thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Alan Bond");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "and");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "J\xfcrgen Bonhagen");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for the continuous creation");
-    ystart += ystep_line;
-    DrawTextSCentered(ystart, font_head,
-                     "of outstanding level sets");
-  }
-  else if (screen_nr == 5)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Peter Elzner");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for ideas and inspiration by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Diamond Caves");
-    ystart += ystep_para;
-
-    DrawTextSCentered(ystart, font_head,
-                     "Thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Steffest");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for ideas and inspiration by");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "DX-Boulderdash");
-  }
-  else if (screen_nr == 6)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "David Tritscher");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for the code base used for the");
-    ystart += ystep_line;
-    DrawTextSCentered(ystart, font_head,
-                     "native Emerald Mine engine");
-  }
-  else if (screen_nr == 7)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "Thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Guido Schulz");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for the initial DOS port");
-    ystart += ystep_para;
-
-    DrawTextSCentered(ystart, font_head,
-                     "Thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "Karl H\xf6rnell");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "for some additional toons");
-  }
-  else if (screen_nr == 8)
-  {
-    DrawTextSCentered(ystart, font_head,
-                     "And not to forget:");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_head,
-                     "Many thanks to");
-    ystart += ystep_head;
-    DrawTextSCentered(ystart, font_text,
-                     "All those who contributed");
-    ystart += ystep_line;
-    DrawTextSCentered(ystart, font_text,
-                     "levels to this game");
-    ystart += ystep_line;
-    DrawTextSCentered(ystart, font_text,
-                     "since 1995");
-  }
+  char *filename = getCreditsFilename(screen_nr, use_global_credits_screens);
+  int width = SXSIZE;
+  int height = MENU_SCREEN_INFO_YBOTTOM - MENU_SCREEN_INFO_YSTART1;
+  int chars = width / getFontWidth(font_text);
+  int lines = height / getFontHeight(font_text);
+  int padx = (width - chars * getFontWidth(font_text)) / 2;
+  int line_spacing = getMenuTextSpacing(spacing_line, font_text);
+  boolean autowrap = FALSE;
+  boolean centered = TRUE;
+  boolean parse_comments = TRUE;
+
+  DrawTextFile(mSX + padx, mSY + MENU_SCREEN_INFO_YSTART1 + ystep_title,
+              filename, font_text, chars, -1, lines, line_spacing, -1,
+              autowrap, centered, parse_comments);
 
   DrawTextSCentered(ybottom, font_foot,
                    "Press any key or button for next page");
@@ -3472,24 +3419,57 @@ static void DrawInfoScreen_Credits(void)
 
   FadeOut(REDRAW_FIELD);
 
-  HandleInfoScreen_Credits(MB_MENU_INITIALIZE);
+  HandleInfoScreen_Credits(0, 0, MB_MENU_INITIALIZE);
 
   FadeIn(REDRAW_FIELD);
 }
 
-void HandleInfoScreen_Credits(int button)
+void HandleInfoScreen_Credits(int dx, int dy, int button)
 {
   static int screen_nr = 0;
-  int num_screens = 9;
 
   if (button == MB_MENU_INITIALIZE)
   {
+    int i;
+
+    // determine number of (global or level set specific) credits screens
+    for (i = 0; i < 2; i++)
+    {
+      num_credits_screens = 0;
+      use_global_credits_screens = i;
+
+      while (getCreditsFilename(num_credits_screens,
+                               use_global_credits_screens) != NULL)
+       num_credits_screens++;
+
+      if (num_credits_screens > 0)
+       break;
+    }
+
+    if (num_credits_screens == 0)
+    {
+      int font_title = MENU_INFO_FONT_TITLE;
+      int font_foot  = MENU_INFO_FONT_FOOT;
+      int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
+      int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
+
+      ClearField();
+      DrawHeadline();
+
+      DrawTextSCentered(ystart, font_title,
+                       "No credits for this level set.");
+
+      DrawTextSCentered(ybottom, font_foot,
+                       "Press any key or button for info menu");
+
+      return;
+    }
+
     screen_nr = 0;
 
-    // DrawInfoScreen_CreditsScreen(screen_nr);
+    DrawInfoScreen_CreditsScreen(screen_nr);
   }
-
-  if (button == MB_MENU_LEAVE)
+  else if (button == MB_MENU_LEAVE)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
@@ -3498,16 +3478,13 @@ void HandleInfoScreen_Credits(int button)
 
     return;
   }
-  else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
+  else if (button == MB_MENU_CHOICE || dx)
   {
-    if (button != MB_MENU_INITIALIZE)
-    {
-      PlaySound(SND_MENU_ITEM_SELECTING);
+    PlaySound(SND_MENU_ITEM_SELECTING);
 
-      screen_nr++;
-    }
+    screen_nr += (dx < 0 ? -1 : +1);
 
-    if (screen_nr >= num_screens)
+    if (screen_nr < 0 || screen_nr >= num_credits_screens)
     {
       FadeMenuSoundsAndMusic();
 
@@ -3517,16 +3494,13 @@ void HandleInfoScreen_Credits(int button)
       return;
     }
 
-    if (screen_nr > 0)
-      FadeSetNextScreen();
+    FadeSetNextScreen();
 
-    if (button != MB_MENU_INITIALIZE)
-      FadeOut(REDRAW_FIELD);
+    FadeOut(REDRAW_FIELD);
 
     DrawInfoScreen_CreditsScreen(screen_nr);
 
-    if (button != MB_MENU_INITIALIZE)
-      FadeIn(REDRAW_FIELD);
+    FadeIn(REDRAW_FIELD);
   }
   else
   {
@@ -3677,7 +3651,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");
@@ -3686,6 +3662,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");
@@ -3755,16 +3735,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);
 
@@ -3916,13 +3906,13 @@ static void DrawInfoScreen(void)
 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
 {
   if (info_mode == INFO_MODE_TITLE)
-    HandleInfoScreen_TitleScreen(button);
+    HandleInfoScreen_TitleScreen(dx, dy, button);
   else if (info_mode == INFO_MODE_ELEMENTS)
-    HandleInfoScreen_Elements(button);
+    HandleInfoScreen_Elements(dx, dy, button);
   else if (info_mode == INFO_MODE_MUSIC)
-    HandleInfoScreen_Music(button);
+    HandleInfoScreen_Music(dx, dy, button);
   else if (info_mode == INFO_MODE_CREDITS)
-    HandleInfoScreen_Credits(button);
+    HandleInfoScreen_Credits(dx, dy, button);
   else if (info_mode == INFO_MODE_PROGRAM)
     HandleInfoScreen_Program(button);
   else if (info_mode == INFO_MODE_VERSION)
@@ -3935,195 +3925,913 @@ void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
 
 
 // ============================================================================
-// type name functions
+// rename player API functions
 // ============================================================================
 
-void HandleTypeName(int newxpos, Key key)
+struct ApiRenamePlayerThreadData
 {
-  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;
+  char *player_name;
+  char *player_uuid;
+};
 
-  DrawBackgroundForFont(startx,starty, pos->width, pos->height, font_active_nr);
+static void *CreateThreadData_ApiRenamePlayer(void)
+{
+  struct ApiRenamePlayerThreadData *data =
+    checked_malloc(sizeof(struct ApiRenamePlayerThreadData));
 
-  if (newxpos)
-  {
-    strcpy(last_player_name, setup.player_name);
+  data->player_name = getStringCopy(setup.player_name);
+  data->player_uuid = getStringCopy(setup.player_uuid);
 
-    xpos = newxpos;
+  return data;
+}
 
-    StartTextInput(startx, starty, pos->width, pos->height);
-  }
-  else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
-  {
-    setup.player_name[xpos] = key_char;
-    setup.player_name[xpos + 1] = 0;
+static void FreeThreadData_ApiRenamePlayer(void *data_raw)
+{
+  struct ApiRenamePlayerThreadData *data = data_raw;
 
-    xpos++;
-  }
-  else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
+  checked_free(data->player_name);
+  checked_free(data->player_uuid);
+  checked_free(data);
+}
+
+static boolean SetRequest_ApiRenamePlayer(struct HttpRequest *request,
+                                         void *data_raw)
+{
+  struct ApiRenamePlayerThreadData *data = data_raw;
+  char *player_name_raw = data->player_name;
+  char *player_uuid_raw = data->player_uuid;
+
+  request->hostname = setup.api_server_hostname;
+  request->port     = API_SERVER_PORT;
+  request->method   = API_SERVER_METHOD;
+  request->uri      = API_SERVER_URI_RENAME;
+
+  char *player_name = getEscapedJSON(player_name_raw);
+  char *player_uuid = getEscapedJSON(player_uuid_raw);
+
+  snprintf(request->body, MAX_HTTP_BODY_SIZE,
+          "{\n"
+          "%s"
+          "  \"game_version\":         \"%s\",\n"
+          "  \"game_platform\":        \"%s\",\n"
+          "  \"name\":                 \"%s\",\n"
+          "  \"uuid\":                 \"%s\"\n"
+          "}\n",
+          getPasswordJSON(setup.api_server_password),
+          getProgramRealVersionString(),
+          getProgramPlatformString(),
+          player_name,
+          player_uuid);
+
+  checked_free(player_name);
+  checked_free(player_uuid);
+
+  ConvertHttpRequestBodyToServerEncoding(request);
+
+  return TRUE;
+}
+
+static void HandleResponse_ApiRenamePlayer(struct HttpResponse *response,
+                                          void *data_raw)
+{
+  // nothing to do here
+}
+
+#if defined(PLATFORM_EMSCRIPTEN)
+static void Emscripten_ApiRenamePlayer_Loaded(unsigned handle, void *data_raw,
+                                             void *buffer, unsigned int size)
+{
+  struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
+
+  if (response != NULL)
   {
-    xpos--;
+    HandleResponse_ApiRenamePlayer(response, data_raw);
 
-    setup.player_name[xpos] = 0;
+    checked_free(response);
   }
-  else if (key == KSYM_Return && xpos > 0)
+  else
   {
-    SaveSetup();
+    Error("server response too large to handle (%d bytes)", size);
+  }
 
-    is_active = FALSE;
+  FreeThreadData_ApiRenamePlayer(data_raw);
+}
 
-    SetGameStatus(GAME_MODE_MAIN);
-  }
-  else if (key == KSYM_Escape)
-  {
-    strcpy(setup.player_name, last_player_name);
+static void Emscripten_ApiRenamePlayer_Failed(unsigned handle, void *data_raw,
+                                             int code, const char *status)
+{
+  Error("server failed to handle request: %d %s", code, status);
 
-    is_active = FALSE;
+  FreeThreadData_ApiRenamePlayer(data_raw);
+}
 
-    SetGameStatus(GAME_MODE_MAIN);
-  }
+static void Emscripten_ApiRenamePlayer_Progress(unsigned handle, void *data_raw,
+                                               int bytes, int size)
+{
+  // nothing to do here
+}
 
-  if (is_active)
+static void Emscripten_ApiRenamePlayer_HttpRequest(struct HttpRequest *request,
+                                                  void *data_raw)
+{
+  if (!SetRequest_ApiRenamePlayer(request, data_raw))
   {
-    pos->width = (strlen(setup.player_name) + 1) * font_width;
-    startx = mSX + ALIGNED_TEXT_XPOS(pos);
+    FreeThreadData_ApiRenamePlayer(data_raw);
 
-    DrawText(startx, starty, setup.player_name, font_active_nr);
-    DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
+    return;
   }
-  else
+
+  emscripten_async_wget2_data(request->uri,
+                             request->method,
+                             request->body,
+                             data_raw,
+                             TRUE,
+                             Emscripten_ApiRenamePlayer_Loaded,
+                             Emscripten_ApiRenamePlayer_Failed,
+                             Emscripten_ApiRenamePlayer_Progress);
+}
+
+#else
+
+static void ApiRenamePlayer_HttpRequestExt(struct HttpRequest *request,
+                                          struct HttpResponse *response,
+                                          void *data_raw)
+{
+  if (!SetRequest_ApiRenamePlayer(request, data_raw))
+    return;
+
+  if (!DoHttpRequest(request, response))
   {
-    pos->width = strlen(setup.player_name) * font_width;
-    startx = mSX + ALIGNED_TEXT_XPOS(pos);
+    Error("HTTP request failed: %s", GetHttpError());
 
-    DrawText(startx, starty, setup.player_name, font_nr);
+    return;
+  }
 
-    StopTextInput();
+  if (!HTTP_SUCCESS(response->status_code))
+  {
+    Error("server failed to handle request: %d %s",
+         response->status_code,
+         response->status_text);
+
+    return;
   }
+
+  HandleResponse_ApiRenamePlayer(response, data_raw);
 }
 
+static void ApiRenamePlayer_HttpRequest(struct HttpRequest *request,
+                                   struct HttpResponse *response,
+                                   void *data_raw)
+{
+  ApiRenamePlayer_HttpRequestExt(request, response, data_raw);
 
-// ============================================================================
-// tree menu functions
-// ============================================================================
+  FreeThreadData_ApiRenamePlayer(data_raw);
+}
+#endif
 
-static int getAlignXOffsetFromTreeInfo(TreeInfo *ti)
+static int ApiRenamePlayerThread(void *data_raw)
 {
-  if (game_status != GAME_MODE_SETUP ||
-      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
-    return 0;
+  struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
+  struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
 
-  int max_text_size = 0;
-  TreeInfo *node;
+  program.api_thread_count++;
 
-  for (node = getTreeInfoFirstGroupEntry(ti); node != NULL; node = node->next)
-    max_text_size = MAX(max_text_size, strlen(node->name));
+#if defined(PLATFORM_EMSCRIPTEN)
+  Emscripten_ApiRenamePlayer_HttpRequest(request, data_raw);
+#else
+  ApiRenamePlayer_HttpRequest(request, response, data_raw);
+#endif
 
-  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 button_width = SC_MENUBUTTON_XSIZE;
-  int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
-  int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
-  int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].align;
-  int x = ALIGNED_XPOS(0, screen_width, align) * -1;
-  int align_xoffset_raw = ALIGNED_XPOS(x, button_width + text_width, align);
-  int align_xoffset = MAX(0, align_xoffset_raw);
+  program.api_thread_count--;
 
-  return align_xoffset;
+  checked_free(request);
+  checked_free(response);
+
+  return 0;
 }
 
-static int getAlignYOffsetFromTreeInfo(TreeInfo *ti)
+static void ApiRenamePlayerAsThread(void)
 {
-  if (game_status != GAME_MODE_SETUP ||
-      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
-    return 0;
-
-  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 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;
-  int y = ALIGNED_YPOS(0, page_height, align) * -1;
-  int align_yoffset_raw = ALIGNED_YPOS(y, text_height, align);
-  int align_yoffset = MAX(0, align_yoffset_raw);
+  struct ApiRenamePlayerThreadData *data = CreateThreadData_ApiRenamePlayer();
 
-  return align_yoffset;
+  ExecuteAsThread(ApiRenamePlayerThread,
+                 "ApiRenamePlayer", data,
+                 "rename player on server");
 }
 
-static void DrawChooseTree(TreeInfo **ti_ptr)
+
+// ============================================================================
+// reset player UUID API functions
+// ============================================================================
+
+struct ApiResetUUIDThreadData
 {
-  int fade_mask = REDRAW_FIELD;
+  char *player_name;
+  char *player_uuid_old;
+  char *player_uuid_new;
+};
 
-  if (CheckFadeAll())
-    fade_mask = REDRAW_ALL;
+static void *CreateThreadData_ApiResetUUID(char *uuid_new)
+{
+  struct ApiResetUUIDThreadData *data =
+    checked_malloc(sizeof(struct ApiResetUUIDThreadData));
 
-  if (strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
-  {
-    SetGameStatus(GAME_MODE_MAIN);
+  data->player_name     = getStringCopy(setup.player_name);
+  data->player_uuid_old = getStringCopy(setup.player_uuid);
+  data->player_uuid_new = getStringCopy(uuid_new);
 
-    DrawMainMenu();
+  return data;
+}
 
-    return;
-  }
+static void FreeThreadData_ApiResetUUID(void *data_raw)
+{
+  struct ApiResetUUIDThreadData *data = data_raw;
 
-  UnmapAllGadgets();
+  checked_free(data->player_name);
+  checked_free(data->player_uuid_old);
+  checked_free(data->player_uuid_new);
+  checked_free(data);
+}
 
-  FreeScreenGadgets();
-  CreateScreenGadgets();
+static boolean SetRequest_ApiResetUUID(struct HttpRequest *request,
+                                      void *data_raw)
+{
+  struct ApiResetUUIDThreadData *data = data_raw;
+  char *player_name_raw = data->player_name;
+  char *player_uuid_old_raw = data->player_uuid_old;
+  char *player_uuid_new_raw = data->player_uuid_new;
 
-  FadeOut(fade_mask);
+  request->hostname = setup.api_server_hostname;
+  request->port     = API_SERVER_PORT;
+  request->method   = API_SERVER_METHOD;
+  request->uri      = API_SERVER_URI_RESETUUID;
 
-  // needed if different viewport properties defined for choosing level (set)
-  ChangeViewportPropertiesIfNeeded();
+  char *player_name = getEscapedJSON(player_name_raw);
+  char *player_uuid_old = getEscapedJSON(player_uuid_old_raw);
+  char *player_uuid_new = getEscapedJSON(player_uuid_new_raw);
 
-  if (game_status == GAME_MODE_LEVELNR)
-    SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
-  else if (game_status == GAME_MODE_LEVELS)
-    SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
+  snprintf(request->body, MAX_HTTP_BODY_SIZE,
+          "{\n"
+          "%s"
+          "  \"game_version\":         \"%s\",\n"
+          "  \"game_platform\":        \"%s\",\n"
+          "  \"name\":                 \"%s\",\n"
+          "  \"uuid_old\":             \"%s\",\n"
+          "  \"uuid_new\":             \"%s\"\n"
+          "}\n",
+          getPasswordJSON(setup.api_server_password),
+          getProgramRealVersionString(),
+          getProgramPlatformString(),
+          player_name,
+          player_uuid_old,
+          player_uuid_new);
 
-  ClearField();
+  checked_free(player_name);
+  checked_free(player_uuid_old);
+  checked_free(player_uuid_new);
 
-  OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+  ConvertHttpRequestBodyToServerEncoding(request);
 
-  HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
-  MapScreenTreeGadgets(*ti_ptr);
+  return TRUE;
+}
 
-  DrawMaskedBorder(fade_mask);
+static void HandleResponse_ApiResetUUID(struct HttpResponse *response,
+                                       void *data_raw)
+{
+  struct ApiResetUUIDThreadData *data = data_raw;
 
-  FadeIn(fade_mask);
+  // upgrade player UUID in server setup file
+  setup.player_uuid = getStringCopy(data->player_uuid_new);
+  setup.player_version = 2;
+
+  SaveSetup_ServerSetup();
 }
 
-static void drawChooseTreeList(int first_entry, int num_page_entries,
-                              TreeInfo *ti)
+#if defined(PLATFORM_EMSCRIPTEN)
+static void Emscripten_ApiResetUUID_Loaded(unsigned handle, void *data_raw,
+                                          void *buffer, unsigned int size)
 {
+  struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
+
+  if (response != NULL)
+  {
+    HandleResponse_ApiResetUUID(response, data_raw);
+
+    checked_free(response);
+  }
+  else
+  {
+    Error("server response too large to handle (%d bytes)", size);
+  }
+
+  FreeThreadData_ApiResetUUID(data_raw);
+}
+
+static void Emscripten_ApiResetUUID_Failed(unsigned handle, void *data_raw,
+                                          int code, const char *status)
+{
+  Error("server failed to handle request: %d %s", code, status);
+
+  FreeThreadData_ApiResetUUID(data_raw);
+}
+
+static void Emscripten_ApiResetUUID_Progress(unsigned handle, void *data_raw,
+                                            int bytes, int size)
+{
+  // nothing to do here
+}
+
+static void Emscripten_ApiResetUUID_HttpRequest(struct HttpRequest *request,
+                                               void *data_raw)
+{
+  if (!SetRequest_ApiResetUUID(request, data_raw))
+  {
+    FreeThreadData_ApiResetUUID(data_raw);
+
+    return;
+  }
+
+  emscripten_async_wget2_data(request->uri,
+                             request->method,
+                             request->body,
+                             data_raw,
+                             TRUE,
+                             Emscripten_ApiResetUUID_Loaded,
+                             Emscripten_ApiResetUUID_Failed,
+                             Emscripten_ApiResetUUID_Progress);
+}
+
+#else
+
+static void ApiResetUUID_HttpRequestExt(struct HttpRequest *request,
+                                       struct HttpResponse *response,
+                                       void *data_raw)
+{
+  if (!SetRequest_ApiResetUUID(request, data_raw))
+    return;
+
+  if (!DoHttpRequest(request, response))
+  {
+    Error("HTTP request failed: %s", GetHttpError());
+
+    return;
+  }
+
+  if (!HTTP_SUCCESS(response->status_code))
+  {
+    Error("server failed to handle request: %d %s",
+         response->status_code,
+         response->status_text);
+
+    return;
+  }
+
+  HandleResponse_ApiResetUUID(response, data_raw);
+}
+
+static void ApiResetUUID_HttpRequest(struct HttpRequest *request,
+                                    struct HttpResponse *response,
+                                    void *data_raw)
+{
+  ApiResetUUID_HttpRequestExt(request, response, data_raw);
+
+  FreeThreadData_ApiResetUUID(data_raw);
+}
+#endif
+
+static int ApiResetUUIDThread(void *data_raw)
+{
+  struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
+  struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
+
+  program.api_thread_count++;
+
+#if defined(PLATFORM_EMSCRIPTEN)
+  Emscripten_ApiResetUUID_HttpRequest(request, data_raw);
+#else
+  ApiResetUUID_HttpRequest(request, response, data_raw);
+#endif
+
+  program.api_thread_count--;
+
+  checked_free(request);
+  checked_free(response);
+
+  return 0;
+}
+
+static void ApiResetUUIDAsThread(char *uuid_new)
+{
+  struct ApiResetUUIDThreadData *data = CreateThreadData_ApiResetUUID(uuid_new);
+
+  ExecuteAsThread(ApiResetUUIDThread,
+                 "ApiResetUUID", data,
+                 "reset UUID on server");
+}
+
+
+// ============================================================================
+// type name functions
+// ============================================================================
+
+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)
+{
+  struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
+
+  *pos = *mci->pos_input;
+
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
+  {
+    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;
+
+    strcpy(name, type_name_node->name);
+
+    pos->x = xpos * 32;
+    pos->y = ypos * 32;
+    pos->width = MAX_PLAYER_NAME_LEN * 32;
+  }
+  else
+  {
+    type_name_nr = user.nr;
+
+    strcpy(name, setup.player_name);
+  }
+
+  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)
+  {
+    TreeInfo *node = type_name_node;
+
+    // change name of edited user in local menu tree structure
+    setString(&node->name, name);
+    setString(&node->name_sorting, name);
+
+    node->color = getPlayerNameColor(name);
+    pos->font = MENU_CHOOSE_TREE_FONT(node->color);
+  }
+}
+
+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();
+
+    // temporarily change active user to edited user
+    user.nr = type_name_nr;
+
+    if (create_user &&
+        Request("Use current setup values for the new player?", REQ_ASK))
+    {
+      // use current setup values for new user, but create new player UUID
+      setup.player_uuid = getStringCopy(getUUID());
+    }
+    else
+    {
+      // load setup for existing user (or start with defaults for new user)
+      LoadSetup();
+    }
+  }
+
+  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();
+
+  // change name of edited user on score server
+  ApiRenamePlayerAsThread();
+
+  if (game_status == GAME_MODE_PSEUDO_TYPENAMES || reset_setup)
+  {
+    if (reset_setup)
+    {
+      if (Request("Reset setup values for this player?", REQ_ASK))
+      {
+       // remove setup config file
+       unlink(setup_filename);
+
+       // set player name to default player name
+       LoadSetup();
+
+       // 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();
+  }
+}
+
+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)
+  {
+    getTypeNameValues(name, pos, &xpos);
+
+    int sx = mSX + ALIGNED_TEXT_XPOS(pos);
+    int sy = mSY + ALIGNED_TEXT_YPOS(pos);
+
+    StartTextInput(sx, sy, pos->width, pos->height);
+  }
+  else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
+  {
+    name[xpos] = key_char;
+    name[xpos + 1] = 0;
+
+    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
+// ============================================================================
+
+static int getAlignXOffsetFromTreeInfo(TreeInfo *ti)
+{
+  if (game_status != GAME_MODE_SETUP ||
+      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
+    return 0;
+
+  int max_text_size = 0;
+  TreeInfo *node;
+
+  for (node = getTreeInfoFirstGroupEntry(ti); node != NULL; node = node->next)
+    max_text_size = MAX(max_text_size, strlen(node->name));
+
   int num_entries = numTreeInfoInGroup(ti);
   boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
+  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);
-  int font_nr = FONT_TEXT_1;
-  int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
-  int i;
-  char *title_string = NULL;
+  int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].align;
+  int x = ALIGNED_XPOS(0, screen_width, align) * -1;
+  int align_xoffset_raw = ALIGNED_XPOS(x, button_width + text_width, align);
+  int align_xoffset = MAX(0, align_xoffset_raw);
+
+  return align_xoffset;
+}
+
+static int getAlignYOffsetFromTreeInfo(TreeInfo *ti)
+{
+  if (game_status != GAME_MODE_SETUP ||
+      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
+    return 0;
+
+  int num_entries = numTreeInfoInGroup(ti);
+  int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
+  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;
+  int y = ALIGNED_YPOS(0, page_height, align) * -1;
+  int align_yoffset_raw = ALIGNED_YPOS(y, text_height, align);
+  int align_yoffset = MAX(0, align_yoffset_raw);
+
+  return align_yoffset;
+}
+
+static void DrawChooseTree(TreeInfo **ti_ptr)
+{
+  int fade_mask = REDRAW_FIELD;
+  boolean restart_music = (game_status != game_status_last_screen &&
+                          game_status_last_screen != GAME_MODE_SCOREINFO);
+
+  if (CheckFadeAll())
+    fade_mask = REDRAW_ALL;
+
+  if (*ti_ptr != NULL && strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
+  {
+    if (game_status == GAME_MODE_SETUP)
+    {
+      execSetupArtwork();
+    }
+    else       // GAME_MODE_LEVELS
+    {
+      SetGameStatus(GAME_MODE_MAIN);
+
+      DrawMainMenu();
+    }
+
+    return;
+  }
+
+  UnmapAllGadgets();
+
+  FreeScreenGadgets();
+  CreateScreenGadgets();
+
+  if (restart_music)
+    FadeMenuSoundsAndMusic();
+
+  FadeOut(fade_mask);
+
+  // needed if different viewport properties defined for this screen
+  ChangeViewportPropertiesIfNeeded();
+
+  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);
+  else if (game_status == GAME_MODE_SCORES)
+    SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
+
+  ClearField();
+
+  OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+
+  // map gadgets for high score screen
+  if (game_status == GAME_MODE_SCORES)
+    MapScreenMenuGadgets(SCREEN_MASK_SCORES);
+
+  MapScreenTreeGadgets(*ti_ptr);
+
+  HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
+
+  DrawMaskedBorder(fade_mask);
+
+  if (restart_music)
+    PlayMenuSoundsAndMusic();
+
+  FadeIn(fade_mask);
+}
+
+static int getChooseTreeFont(TreeInfo *node, boolean active)
+{
+  if (game_status == GAME_MODE_SCORES)
+    return (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
+  else
+    return MENU_CHOOSE_TREE_FONT(MENU_CHOOSE_TREE_COLOR(node, active));
+}
+
+static void drawChooseTreeText(TreeInfo *ti, int y, boolean active)
+{
+  int num_entries = numTreeInfoInGroup(ti);
+  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 first_entry = ti->cl_first;
+  int entry_pos = first_entry + y;
+  TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
+  TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos);
+  int font_nr = getChooseTreeFont(node, active);
+  int font_xoffset = getFontDrawOffsetX(font_nr);
+  int xpos = MENU_SCREEN_START_XPOS;
+  int ypos = MENU_SCREEN_START_YPOS + y;
+  int startdx = xpos * 32;
+  int startdy = ypos * 32;
+  int startx = amSX + startdx;
+  int starty = amSY + startdy;
+  int startx_text = startx + font_xoffset;
+  int endx_text = amSX + screen_width;
+  int max_text_size = endx_text - startx_text;
+  int max_buffer_len = max_text_size / getFontWidth(font_nr);
+  char buffer[max_buffer_len + 1];
+
+  if (game_status == GAME_MODE_SCORES && !node->parent_link)
+  {
+    int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
+    int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
+    int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
+    int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
+    int font_size_1 = getFontWidth(font_nr1);
+    int font_size_3 = getFontWidth(font_nr3);
+    int font_size_4 = getFontWidth(font_nr4);
+    int text_size_1 = 4 * font_size_1;
+    int text_size_4 = 5 * font_size_4;
+    int border = amSX - SX + getFontDrawOffsetX(font_nr1);
+    int dx1 = 0;
+    int dx3 = text_size_1;
+    int dx4 = SXSIZE - 2 * startdx - 2 * border - text_size_4;
+    int num_dots = (dx4 - dx3) / font_size_3;
+    int startx1 = startx + dx1;
+    int startx3 = startx + dx3;
+    int startx4 = startx + dx4;
+    int pos = node->pos;
+    char *pos_text = getHallOfFameRankText(pos, 3);
+    int i;
+
+    DrawText(startx1, starty, pos_text, font_nr1);
+
+    for (i = 0; i < num_dots; i++)
+      DrawText(startx3 + i * font_size_3, starty, ".", font_nr3);
+
+    if (!strEqual(scores.entry[pos].name, EMPTY_PLAYER_NAME))
+      DrawText(startx3, starty, scores.entry[pos].name, font_nr2);
+
+    DrawText(startx4, starty, getHallOfFameScoreText(pos, 5), font_nr4);
+  }
+  else
+  {
+    strncpy(buffer, node->name, max_buffer_len);
+    buffer[max_buffer_len] = '\0';
+
+    DrawText(startx, starty, buffer, font_nr);
+  }
+}
+
+static void drawChooseTreeHeadExt(int type, char *title_string)
+{
   int yoffset_sets = MENU_TITLE1_YPOS;
   int yoffset_setup = 16;
-  int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ||
-                ti->type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup);
-
-  title_string = ti->infotext;
+  int yoffset = (type == TREE_TYPE_SCORE_ENTRY ||
+                type == TREE_TYPE_LEVEL_DIR ||
+                type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup);
 
   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
+}
+
+static void drawChooseTreeHead(TreeInfo *ti)
+{
+  drawChooseTreeHeadExt(ti->type, ti->infotext);
+}
+
+static void drawChooseTreeList(TreeInfo *ti)
+{
+  int first_entry = ti->cl_first;
+  int num_entries = numTreeInfoInGroup(ti);
+  int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
+  int i;
 
   clearMenuListArea();
 
@@ -4131,23 +4839,11 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
   {
     TreeInfo *node, *node_first;
     int entry_pos = first_entry + i;
-    int xpos = MENU_SCREEN_START_XPOS;
-    int ypos = MENU_SCREEN_START_YPOS + i;
-    int startx = amSX + xpos * 32;
-    int starty = amSY + ypos * 32;
-    int startx_text = startx + font_xoffset;
-    int endx_text = amSX + screen_width;
-    int max_text_size = endx_text - startx_text;
-    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);
+    drawChooseTreeText(ti, i, FALSE);
 
     if (node->parent_link)
       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
@@ -4155,26 +4851,37 @@ 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_SCORES && node->pos == scores.last_added)
+      initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
+
+    if (game_status == GAME_MODE_NAMES)
+      drawChooseTreeEdit(i, FALSE);
   }
 
   redraw_mask |= REDRAW_FIELD;
 }
 
-static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
+static void drawChooseTreeInfo(TreeInfo *ti)
 {
-  TreeInfo *node, *node_first;
-  int x, last_redraw_mask = redraw_mask;
+  int entry_pos = ti->cl_first + ti->cl_cursor;
+  int last_redraw_mask = redraw_mask;
   int ypos = MENU_TITLE2_YPOS;
   int font_nr = FONT_TITLE_2;
+  int x;
 
   if (ti->type == TREE_TYPE_LEVEL_NR)
     DrawTextFCentered(ypos, font_nr, leveldir_current->name);
 
+  if (ti->type == TREE_TYPE_SCORE_ENTRY)
+    DrawTextFCentered(ypos, font_nr, "HighScores of Level %d",
+                     scores.last_level_nr);
+
   if (ti->type != TREE_TYPE_LEVEL_DIR)
     return;
 
-  node_first = getTreeInfoFirstGroupEntry(ti);
-  node = getTreeInfoFromPos(node_first, entry_pos);
+  TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
+  TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos);
 
   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
 
@@ -4189,36 +4896,163 @@ static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
                      node->levels, (node->levels > 1 ? "levels" : "level"),
                      node->class_desc);
 
-  // let BackToFront() redraw only what is needed
-  redraw_mask = last_redraw_mask;
-  for (x = 0; x < SCR_FIELDX; x++)
-    MarkTileDirty(x, 1);
+  // let BackToFront() redraw only what is needed
+  redraw_mask = last_redraw_mask;
+  for (x = 0; x < SCR_FIELDX; x++)
+    MarkTileDirty(x, 1);
+}
+
+static void drawChooseTreeCursorAndText(TreeInfo *ti, boolean active)
+{
+  drawChooseTreeCursor(ti->cl_cursor, active);
+  drawChooseTreeText(ti, ti->cl_cursor, active);
+}
+
+static void drawChooseTreeScreen(TreeInfo *ti)
+{
+  drawChooseTreeHead(ti);
+  drawChooseTreeList(ti);
+  drawChooseTreeInfo(ti);
+  drawChooseTreeCursorAndText(ti, TRUE);
+
+  AdjustChooseTreeScrollbar(ti, SCREEN_CTRL_ID_SCROLL_VERTICAL);
+
+  // scroll bar and buttons may just have been added after reloading scores
+  if (game_status == GAME_MODE_SCORES)
+    MapScreenTreeGadgets(ti);
+}
+
+static void drawChooseTreeScreen_Scores_NotAvailable(void)
+{
+  // dirty workaround to use spacing definitions from info screen
+  info_mode = INFO_MODE_TITLE;
+
+  char *text_info = "HighScores of Level %d";
+  char *text_title = "Score information:";
+  char *text_error = "No scores for this level.";
+  char *text_foot = "Press any key or button for main menu";
+  int font_info = FONT_TITLE_2;
+  int font_title = FONT_INITIAL_3;
+  int font_error = FONT_INITIAL_4;
+  int font_foot  = FONT_INITIAL_2;
+  int spacing_title = menu.headline1_spacing_info[INFO_MODE_TITLE];
+  int ystep_title = getMenuTextStep(spacing_title, font_title);
+  int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1;
+  int ystart2 = ystart1 + ystep_title;
+  int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
+  int ystart0 = MENU_TITLE2_YPOS;
+
+  drawChooseTreeHeadExt(TREE_TYPE_SCORE_ENTRY, INFOTEXT_SCORE_ENTRY);
+  DrawTextFCentered(ystart0, font_info, text_info, scores.last_level_nr);
+
+  DrawTextSCentered(ystart1, font_title, text_title);
+  DrawTextSCentered(ystart2, font_error, text_error);
+
+  DrawTextSCentered(ybottom, font_foot, text_foot);
+}
+
+static TreeInfo *setHallOfFameActiveEntry(TreeInfo **ti_ptr)
+{
+  int score_pos = scores.last_added;
+
+  if (game_status_last_screen == GAME_MODE_SCOREINFO)
+    score_pos = scores.last_entry_nr;
+
+  // set current tree entry to last added score entry
+  *ti_ptr = getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos));
+
+  // if that fails, set current tree entry to first entry (back link)
+  if (*ti_ptr == NULL)
+    *ti_ptr = score_entries->node_group;
+
+  int num_entries = numTreeInfoInGroup(*ti_ptr);
+  int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
+  int pos_score = getPosFromTreeInfo(*ti_ptr);
+  int pos_first_raw = pos_score - (num_page_entries + 1) / 2 + 1;
+  int pos_first = MIN(MAX(0, pos_first_raw), num_entries - num_page_entries);
+
+  (*ti_ptr)->cl_first = pos_first;
+  (*ti_ptr)->cl_cursor = pos_score - pos_first;
+
+  return *ti_ptr;
 }
 
 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 y = (ti != NULL ? ti->cl_cursor : 0);
   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
   int num_entries = numTreeInfoInGroup(ti);
   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
   boolean position_set_by_scrollbar = (dx == 999);
+  boolean button_action = (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE);
+  boolean button_is_valid = (mx >= 0 && my >= 0);
+  boolean button_screen_clicked = (button_action && button_is_valid);
+
+  if (game_status == GAME_MODE_SCORES)
+  {
+    if (server_scores.updated)
+    {
+      // reload scores, using updated server score cache file
+      LoadLocalAndServerScore(scores.last_level_nr, FALSE);
+
+      server_scores.updated = FALSE;
+
+      DrawHallOfFame_setScoreEntries();
+
+      if (score_entries != NULL)
+      {
+       ti = setHallOfFameActiveEntry(ti_ptr);
+
+       if (button != MB_MENU_INITIALIZE)
+         drawChooseTreeScreen(ti);
+      }
+    }
+
+    if (score_entries == NULL)
+    {
+      if (button == MB_MENU_INITIALIZE)
+      {
+       drawChooseTreeScreen_Scores_NotAvailable();
+      }
+      else if (button_screen_clicked)
+      {
+       PlaySound(SND_MENU_ITEM_SELECTING);
+
+       SetGameStatus(GAME_MODE_MAIN);
+
+       DrawMainMenu();
+      }
+
+      return;
+    }
+  }
 
   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);
 
-    if (ti->cl_first == -1)
+    if (game_status == GAME_MODE_SCORES)
+    {
+      ti = setHallOfFameActiveEntry(ti_ptr);
+    }
+    else if (ti->cl_first == -1)
     {
       // only on initialization
       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
       ti->cl_cursor = entry_pos - ti->cl_first;
+
     }
     else if (ti->cl_cursor >= num_page_entries ||
             (num_entries > num_page_entries &&
@@ -4231,19 +5065,15 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
     if (position_set_by_scrollbar)
       ti->cl_first = dy;
-    else
-      AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
-                               ti->cl_first, ti);
 
-    drawChooseTreeList(ti->cl_first, num_page_entries, ti);
-    drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
-    drawChooseTreeCursor(ti->cl_cursor, TRUE);
+    drawChooseTreeScreen(ti);
 
     return;
   }
   else if (button == MB_MENU_LEAVE)
   {
-    FadeSetLeaveMenu();
+    if (game_status != GAME_MODE_SCORES)
+      FadeSetLeaveMenu();
 
     PlaySound(SND_MENU_ITEM_SELECTING);
 
@@ -4254,7 +5084,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
     }
     else if (game_status == GAME_MODE_SETUP)
     {
-      if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+      if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
+         setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
          setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
          setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
        execSetupGame();
@@ -4300,6 +5131,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
   {
@@ -4341,14 +5175,7 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
       }
 
       if (redraw)
-      {
-       drawChooseTreeList(ti->cl_first, num_page_entries, ti);
-       drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
-       drawChooseTreeCursor(ti->cl_cursor, TRUE);
-
-       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
-                                 ti->cl_first, ti);
-      }
+       drawChooseTreeScreen(ti);
 
       return;
     }
@@ -4357,7 +5184,13 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
     y = ti->cl_cursor + dy;
   }
 
-  if (dx == 1)
+  if (game_status == GAME_MODE_SCORES && ABS(dx) == 1)
+  {
+    HandleHallOfFame_SelectLevel(1, dx);
+
+    return;
+  }
+  else if (dx == 1)
   {
     TreeInfo *node_first, *node_cursor;
     int entry_pos = ti->cl_first + y;
@@ -4393,26 +5226,35 @@ 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);
 
-       drawChooseTreeCursor(ti->cl_cursor, FALSE);
-       drawChooseTreeCursor(y, TRUE);
-       drawChooseTreeInfo(ti->cl_first + y, ti);
+       drawChooseTreeCursorAndText(ti, FALSE);
 
        ti->cl_cursor = y;
+
+       drawChooseTreeCursorAndText(ti, TRUE);
+
+       drawChooseTreeInfo(ti);
       }
       else if (dx < 0)
       {
        if (game_status == GAME_MODE_SETUP)
        {
-         if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+         if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
+             setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
              setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
              setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
            execSetupGame();
@@ -4460,14 +5302,16 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
       }
       else if (node_cursor->parent_link)
       {
-       FadeSetLeaveMenu();
+       if (game_status != GAME_MODE_SCORES)
+         FadeSetLeaveMenu();
 
        *ti_ptr = node_cursor->node_parent;
        DrawChooseTree(ti_ptr);
       }
       else
       {
-       FadeSetEnterMenu();
+       if (game_status != GAME_MODE_SCORES)
+         FadeSetEnterMenu();
 
        node_cursor->cl_first = ti->cl_first;
        node_cursor->cl_cursor = ti->cl_cursor;
@@ -4484,7 +5328,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
        if (game_status == GAME_MODE_SETUP)
        {
-         if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+         if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
+             setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
              setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
              setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
            execSetupGame();
@@ -4517,6 +5362,67 @@ 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_LEVELS)
+         {
+           // store level set if chosen from "last played level set" menu
+           StoreLastPlayedLevels(leveldir_current);
+         }
+         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();
+
+           // update list of last played level sets
+           UpdateLastPlayedLevels_TreeInfo();
+
+           TapeErase();
+
+           ToggleFullscreenIfNeeded();
+           ChangeWindowScalingIfNeeded();
+
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_GRAPHICS);
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_SOUNDS);
+           ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_MUSIC);
+         }
+         else if (game_status == GAME_MODE_SCORES)
+         {
+           if (game_status_last_screen == GAME_MODE_PLAYING &&
+               setup.auto_play_next_level && setup.increment_levels &&
+               scores.last_level_nr < leveldir_current->last_level &&
+               !network_playing)
+           {
+             StartGameActions(network.enabled, setup.autorecord,
+                              level.random_seed);
+             return;
+           }
+           else
+           {
+             SetGameStatus(GAME_MODE_SCOREINFO);
+
+             DrawScoreInfo(node_cursor->pos);
+
+             return;
+           }
+         }
 
          SetGameStatus(GAME_MODE_MAIN);
 
@@ -4525,15 +5431,64 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
       }
     }
   }
+
+  if (game_status == GAME_MODE_SCORES)
+    PlayMenuSoundIfLoop();
 }
 
-void DrawChooseLevelSet(void)
+void DrawChoosePlayerName(void)
 {
-  FadeMenuSoundsAndMusic();
+  int i;
 
-  DrawChooseTree(&leveldir_current);
+  if (player_name != NULL)
+  {
+    freeTreeInfo(player_name);
 
-  PlayMenuSoundsAndMusic();
+    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);
+}
+
+void HandleChoosePlayerName(int mx, int my, int dx, int dy, int button)
+{
+  HandleChooseTree(mx, my, dx, dy, button, &player_name_current);
+}
+
+void DrawChooseLevelSet(void)
+{
+  DrawChooseTree(&leveldir_current);
 }
 
 void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
@@ -4545,8 +5500,6 @@ void DrawChooseLevelNr(void)
 {
   int i;
 
-  FadeMenuSoundsAndMusic();
-
   if (level_number != NULL)
   {
     freeTreeInfo(level_number);
@@ -4592,8 +5545,6 @@ void DrawChooseLevelNr(void)
     level_number_current = level_number;
 
   DrawChooseTree(&level_number_current);
-
-  PlayMenuSoundsAndMusic();
 }
 
 void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
@@ -4601,15 +5552,68 @@ void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
   HandleChooseTree(mx, my, dx, dy, button, &level_number_current);
 }
 
-void DrawHallOfFame(int level_nr, int highlight_position)
+static void DrawHallOfFame_setScoreEntries(void)
 {
-  int fade_mask = REDRAW_FIELD;
+  int max_empty_entries = 10;  // at least show "top ten" list, if empty
+  int max_visible_entries = NUM_MENU_ENTRIES_ON_SCREEN - 1;   // w/o back link
+  int min_score_entries = MIN(max_empty_entries, max_visible_entries);
+  int score_pos = (scores.last_added >= 0 ? scores.last_added : 0);
+  int i;
 
-  if (CheckFadeAll())
-    fade_mask = REDRAW_ALL;
+  if (score_entries != NULL)
+  {
+    freeTreeInfo(score_entries);
 
-  UnmapAllGadgets();
-  FadeMenuSoundsAndMusic();
+    score_entries = NULL;
+  }
+
+  for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+  {
+    // do not add empty score entries if off-screen
+    if (scores.entry[i].score == 0 &&
+       scores.entry[i].time == 0 &&
+       i >= min_score_entries)
+      break;
+
+    TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_SCORE_ENTRY);
+    char identifier[32], name[64];
+    int value = i;
+
+    ti->node_top = &score_entries;
+    ti->sort_priority = 10000 + value;
+    ti->color = FC_YELLOW;
+    ti->pos = i;
+
+    snprintf(identifier, sizeof(identifier), "%d", value);
+    snprintf(name, sizeof(name), "%03d.", value + 1);
+
+    setString(&ti->identifier, identifier);
+    setString(&ti->name, name);
+    setString(&ti->name_sorting, name);
+
+    pushTreeInfo(&score_entries, ti);
+  }
+
+  // sort score entries to start with highest score entry
+  sortTreeInfo(&score_entries);
+
+  // add top tree node to create back link to main menu
+  score_entries = addTopTreeInfoNode(score_entries);
+
+  // set current score entry to last added or highest score entry
+  score_entry_current =
+    getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos));
+
+  // if that fails, set current score entry to first valid score entry
+  if (score_entry_current == NULL)
+    score_entry_current = getFirstValidTreeInfoEntry(score_entries);
+
+  // ("score_entries" and "score_entry_current" may be NULL here)
+}
+
+void DrawHallOfFame(int level_nr)
+{
+  scores.last_level_nr = level_nr;
 
   // (this is needed when called from GameEnd() after winning a game)
   KeyboardAutoRepeatOn();
@@ -4618,138 +5622,292 @@ void DrawHallOfFame(int level_nr, int highlight_position)
   SetDrawDeactivationMask(REDRAW_NONE);
   SetDrawBackgroundMask(REDRAW_FIELD);
 
-  if (highlight_position < 0) 
-    LoadScore(level_nr);
-  else
+  LoadLocalAndServerScore(level_nr, TRUE);
+
+  DrawHallOfFame_setScoreEntries();
+
+  if (scores.last_added >= 0)
     SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW);
 
   FadeSetEnterScreen();
 
-  FadeOut(fade_mask);
+  DrawChooseTree(&score_entry_current);
+}
 
-  // needed if different viewport properties defined for scores
-  ChangeViewportPropertiesIfNeeded();
+static char *getHallOfFameRankText(int nr, int size)
+{
+  static char rank_text[10];
+  boolean forced = (scores.force_last_added && nr == scores.last_added);
+  char *rank_text_raw = (forced ? "???" : int2str(nr + 1, size));
+
+  sprintf(rank_text, "%s%s", rank_text_raw, (size > 0 || !forced ? "." : ""));
+
+  return rank_text;
+}
+
+static char *getHallOfFameTimeText(int nr)
+{
+  static char score_text[10];
+  int time_seconds = scores.entry[nr].time / FRAMES_PER_SECOND;
+  int mm = (time_seconds / 60) % 60;
+  int ss = (time_seconds % 60);
+
+  sprintf(score_text, "%02d:%02d", mm, ss);    // show playing time
+
+  return score_text;
+}
+
+static char *getHallOfFameScoreText(int nr, int size)
+{
+  if (!level.rate_time_over_score)
+    return int2str(scores.entry[nr].score, size);      // show normal score
+  else if (level.use_step_counter)
+    return int2str(scores.entry[nr].time, size);       // show number of steps
+  else
+    return getHallOfFameTimeText(nr);                  // show playing time
+}
+
+static void HandleHallOfFame_SelectLevel(int step, int direction)
+{
+  int old_level_nr = scores.last_level_nr;
+  int new_level_nr = old_level_nr + step * direction;
+
+  if (new_level_nr < leveldir_current->first_level)
+    new_level_nr = leveldir_current->first_level;
+  if (new_level_nr > leveldir_current->last_level)
+    new_level_nr = leveldir_current->last_level;
+
+  if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
+    new_level_nr = leveldir_current->handicap_level;
+
+  if (new_level_nr != old_level_nr)
+  {
+    PlaySound(SND_MENU_ITEM_SELECTING);
+
+    scores.last_level_nr = level_nr = new_level_nr;
+    scores.last_entry_nr = 0;
+
+    LoadLevel(level_nr);
+    LoadLocalAndServerScore(level_nr, TRUE);
+
+    DrawHallOfFame_setScoreEntries();
+
+    if (game_status == GAME_MODE_SCORES)
+    {
+      // force remapping optional gadgets (especially scroll bar)
+      UnmapScreenTreeGadgets();
+
+      // redraw complete high score screen, as sub-title has changed
+      ClearField();
+
+      // redraw level selection buttons (which have just been erased)
+      RedrawScreenMenuGadgets(SCREEN_MASK_SCORES);
+
+      HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, &score_entry_current);
+    }
+    else
+    {
+      DrawScoreInfo_Content(scores.last_entry_nr);
+    }
+
+    SaveLevelSetup_SeriesInfo();
+  }
+}
+
+void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
+{
+  HandleChooseTree(mx, my, dx, dy, button, &score_entry_current);
+}
+
+static void DrawScoreInfo_Content(int entry_nr)
+{
+  struct ScoreEntry *entry = &scores.entry[entry_nr];
+  char *pos_text = getHallOfFameRankText(entry_nr, 0);
+  int font_title = MENU_INFO_FONT_TITLE;
+  int font_head  = MENU_INFO_FONT_HEAD;
+  int font_text  = MENU_INFO_FONT_TEXT;
+  int font_foot  = MENU_INFO_FONT_FOOT;
+  int spacing_title = menu.headline1_spacing[GAME_MODE_SCOREINFO];
+  int spacing_para  = menu.paragraph_spacing[GAME_MODE_SCOREINFO];
+  int spacing_line  = menu.line_spacing[GAME_MODE_SCOREINFO];
+  int xstep = getFontWidth(font_text);
+  int ystep_title = getMenuTextStep(spacing_title, font_title);
+  int ystep_para  = getMenuTextStep(spacing_para,  font_text);
+  int ystep_line  = getMenuTextStep(spacing_line,  font_text);
+  int ystart  = mSY - SY + menu.top_spacing[GAME_MODE_SCOREINFO];
+  int ybottom = mSY - SY + SYSIZE - menu.bottom_spacing[GAME_MODE_SCOREINFO];
+  int xstart1 = mSX - SX + 2 * xstep;
+  int xstart2 = mSX - SX + 13 * xstep;
+  int button_x = SX + xstart1;
+  int button_y1, button_y2;
+  int font_width = getFontWidth(font_text);
+  int font_height = getFontHeight(font_text);
+  int pad_left = xstart2;
+  int pad_right = MENU_SCREEN_INFO_SPACE_RIGHT;
+  int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width;
+  int max_lines_per_text = 5;
+  int lines;
+
+  ClearField();
+
+  // redraw score selection buttons (which have just been erased)
+  RedrawScreenMenuGadgets(SCREEN_MASK_SCORES);
+
+  if (score_entries == NULL)
+  {
+    drawChooseTreeScreen_Scores_NotAvailable();
+
+    return;
+  }
+
+  drawChooseTreeHead(score_entries);
+  drawChooseTreeInfo(score_entries);
+
+  DrawTextSCentered(ystart, font_title, "Score Information:");
+  ystart += ystep_title;
+
+  DrawTextF(xstart1, ystart, font_head, "Level Set");
+  lines = DrawTextBufferS(xstart2, ystart, leveldir_current->name, font_text,
+                         max_chars_per_line, -1, max_lines_per_text, 0, -1,
+                         TRUE, FALSE, FALSE);
+  ystart += ystep_line + (lines > 0 ? lines - 1 : 0) * font_height;
+
+  DrawTextF(xstart1, ystart, font_head, "Level");
+  lines = DrawTextBufferS(xstart2, ystart, level.name, font_text,
+                         max_chars_per_line, -1, max_lines_per_text, 0, -1,
+                         TRUE, FALSE, FALSE);
+  ystart += ystep_para + (lines > 0 ? lines - 1 : 0) * font_height;
+
+  button_y1 = SY + ystart;
+  ystart += graphic_info[IMG_MENU_BUTTON_PREV_SCORE].height;
+
+  DrawTextF(xstart1, ystart, font_head, "Rank");
+  DrawTextF(xstart2, ystart, font_text, pos_text);
+  ystart += ystep_line;
+
+  DrawTextF(xstart1, ystart, font_head, "Player");
+  DrawTextF(xstart2, ystart, font_text, entry->name);
+  ystart += ystep_line;
+
+  if (level.use_step_counter)
+  {
+    DrawTextF(xstart1, ystart, font_head, "Steps");
+    DrawTextF(xstart2, ystart, font_text, int2str(entry->time, 5));
+    ystart += ystep_line;
+  }
+  else
+  {
+    DrawTextF(xstart1, ystart, font_head, "Time");
+    DrawTextF(xstart2, ystart, font_text, getHallOfFameTimeText(entry_nr));
+    ystart += ystep_line;
+  }
+
+  if (!level.rate_time_over_score || entry->score > 0)
+  {
+    DrawTextF(xstart1, ystart, font_head, "Score");
+    DrawTextF(xstart2, ystart, font_text, int2str(entry->score, 5));
+    ystart += ystep_line;
+  }
+
+  ystart += ystep_line;
+
+  DrawTextF(xstart1, ystart, font_head, "Tape Date");
+  DrawTextF(xstart2, ystart, font_text, entry->tape_date);
+  ystart += ystep_line;
+
+  DrawTextF(xstart1, ystart, font_head, "Platform");
+  DrawTextF(xstart2, ystart, font_text, entry->platform);
+  ystart += ystep_line;
 
-  PlayMenuSoundsAndMusic();
+  DrawTextF(xstart1, ystart, font_head, "Version");
+  DrawTextF(xstart2, ystart, font_text, entry->version);
+  ystart += ystep_line;
 
-  OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+  DrawTextF(xstart1, ystart, font_head, "Country");
+  lines = DrawTextBufferS(xstart2, ystart, entry->country_name, font_text,
+                         max_chars_per_line, -1, max_lines_per_text, 0, -1,
+                         TRUE, FALSE, FALSE);
+  ystart += ystep_line;
 
-  HandleHallOfFame(level_nr, highlight_position, 0, 0, MB_MENU_INITIALIZE);
+  button_y2 = SY + ystart;
 
-  DrawMaskedBorder(fade_mask);
+  DrawTextSCentered(ybottom, font_foot, "Press any key or button to go back");
 
-  FadeIn(fade_mask);
+  AdjustScoreInfoButtons(button_x, button_y1, button_y2);
 }
 
-static void drawHallOfFameList(int level_nr, int first_entry,
-                              int highlight_position)
+static void DrawScoreInfo(int entry_nr)
 {
-  int i, j;
-
-  SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
-  ClearField();
-
-  DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
-  DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
-                   "HighScores of Level %d", level_nr);
+  scores.last_entry_nr = entry_nr;
 
-  for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
-  {
-    int entry = first_entry + i;
-    boolean active = (entry == highlight_position);
-    int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
-    int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
-    int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
-    int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
-    int dxoff = getFontDrawOffsetX(font_nr1);
-    int dx1 = 3 * getFontWidth(font_nr1);
-    int dx2 = dx1 + getFontWidth(font_nr1);
-    int dx3 = SXSIZE - 2 * (mSX - SX + dxoff) - 5 * getFontWidth(font_nr4);
-    int num_dots = (dx3 - dx2) / getFontWidth(font_nr3);
-    int sy = mSY + 64 + i * 32;
+  SetMainBackgroundImageIfDefined(IMG_BACKGROUND_SCOREINFO);
 
-    DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
-    DrawText(mSX + dx1, sy, ".", font_nr1);
+  UnmapAllGadgets();
 
-    for (j = 0; j < num_dots; j++)
-      DrawText(mSX + dx2 + j * getFontWidth(font_nr3), sy, ".", font_nr3);
+  FadeOut(REDRAW_FIELD);
 
-    if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
-      DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
+  DrawScoreInfo_Content(entry_nr);
 
-    DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
-  }
+  // map gadgets for score info screen
+  MapScreenMenuGadgets(SCREEN_MASK_SCORES_INFO);
 
-  redraw_mask |= REDRAW_FIELD;
+  FadeIn(REDRAW_FIELD);
 }
 
-void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
+static void HandleScoreInfo_SelectScore(int step, int direction)
 {
-  static int level_nr = 0;
-  static int first_entry = 0;
-  static int highlight_position = 0;
-  int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+  int old_entry_nr = scores.last_entry_nr;
+  int new_entry_nr = old_entry_nr + step * direction;
+  int num_nodes = numTreeInfoInGroup(score_entry_current);
+  int num_entries = num_nodes - 1;     // score nodes only, without back link
 
-  if (button == MB_MENU_INITIALIZE)
-  {
-    level_nr = mx;
-    first_entry = 0;
-    highlight_position = my;
+  if (new_entry_nr < 0)
+    new_entry_nr = 0;
+  if (new_entry_nr > num_entries - 1)
+    new_entry_nr = num_entries - 1;
 
-    drawHallOfFameList(level_nr, first_entry, highlight_position);
+  if (new_entry_nr != old_entry_nr)
+  {
+    scores.last_entry_nr = new_entry_nr;
 
-    return;
+    DrawScoreInfo_Content(new_entry_nr);
   }
+}
 
-  if (ABS(dy) == SCROLL_PAGE)          // handle scrolling one page
-    step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
+void HandleScoreInfo(int mx, int my, int dx, int dy, int button)
+{
+  boolean button_action = (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE);
+  boolean button_is_valid = (mx >= 0 && my >= 0);
+  boolean button_screen_clicked = (button_action && button_is_valid);
 
-  if (dy < 0)
+  if (server_scores.updated)
   {
-    if (first_entry > 0)
-    {
-      first_entry -= step;
-      if (first_entry < 0)
-       first_entry = 0;
+    // reload scores, using updated server score cache file
+    LoadLocalAndServerScore(scores.last_level_nr, FALSE);
 
-      drawHallOfFameList(level_nr, first_entry, highlight_position);
-    }
-  }
-  else if (dy > 0)
-  {
-    if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
-    {
-      first_entry += step;
-      if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
-       first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
+    server_scores.updated = FALSE;
 
-      drawHallOfFameList(level_nr, first_entry, highlight_position);
-    }
+    DrawHallOfFame_setScoreEntries();
+
+    DrawScoreInfo_Content(scores.last_entry_nr);
   }
-  else if (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE)
+
+  if (button_screen_clicked)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
-    FadeSound(SND_BACKGROUND_SCORES);
-
-    if (button == MB_MENU_CHOICE &&
-       game_status_last_screen == GAME_MODE_PLAYING &&
-       setup.auto_play_next_level && setup.increment_levels &&
-       level_nr < leveldir_current->last_level &&
-       !network_playing)
-    {
-      StartGameActions(network.enabled, setup.autorecord, level.random_seed);
-    }
-    else
-    {
-      SetGameStatus(GAME_MODE_MAIN);
+    SetGameStatus(GAME_MODE_SCORES);
 
-      DrawMainMenu();
-    }
+    DrawHallOfFame(level_nr);
+  }
+  else if (dx)
+  {
+    HandleHallOfFame_SelectLevel(1, SIGN(dx) * (ABS(dx) > 1 ? 10 : 1));
+  }
+  else if (dy)
+  {
+    HandleScoreInfo_SelectScore(1, SIGN(dy) * (ABS(dy) > 1 ? 10 : 1));
   }
-
-  if (game_status == GAME_MODE_SCORES)
-    PlayMenuSoundIfLoop();
 }
 
 
@@ -4768,6 +5926,7 @@ static char *vsync_mode_text;
 static char *scroll_delay_text;
 static char *snapshot_mode_text;
 static char *game_speed_text;
+static char *scores_type_text;
 static char *network_server_text;
 static char *graphics_set_name;
 static char *sounds_set_name;
@@ -4788,6 +5947,56 @@ static void execSetupMain(void)
   DrawSetupScreen();
 }
 
+static void execSetupGame_setScoresType(void)
+{
+  if (scores_types == NULL)
+  {
+    int i;
+
+    for (i = 0; scores_types_list[i].value != NULL; i++)
+    {
+      TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
+      char identifier[32], name[32];
+      char *value = scores_types_list[i].value;
+      char *text = scores_types_list[i].text;
+
+      ti->node_top = &scores_types;
+      ti->sort_priority = i;
+
+      sprintf(identifier, "%s", value);
+      sprintf(name, "%s", text);
+
+      setString(&ti->identifier, identifier);
+      setString(&ti->name, name);
+      setString(&ti->name_sorting, name);
+      setString(&ti->infotext, STR_SETUP_CHOOSE_SCORES_TYPE);
+
+      pushTreeInfo(&scores_types, ti);
+    }
+
+    // sort scores type values to start with lowest scores type value
+    sortTreeInfo(&scores_types);
+
+    // set current scores type value to configured scores type value
+    scores_type_current =
+      getTreeInfoFromIdentifier(scores_types, setup.scores_in_highscore_list);
+
+    // if that fails, set current scores type to reliable default value
+    if (scores_type_current == NULL)
+      scores_type_current =
+       getTreeInfoFromIdentifier(scores_types, STR_SCORES_TYPE_DEFAULT);
+
+    // if that also fails, set current scores type to first available value
+    if (scores_type_current == NULL)
+      scores_type_current = scores_types;
+  }
+
+  setup.scores_in_highscore_list = scores_type_current->identifier;
+
+  // needed for displaying scores type text instead of identifier
+  scores_type_text = scores_type_current->name;
+}
+
 static void execSetupGame_setGameSpeeds(boolean update_value)
 {
   if (setup.game_speed_extended)
@@ -4981,11 +6190,15 @@ static void execSetupGame(void)
   boolean check_vsync_mode = (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED);
 
   execSetupGame_setGameSpeeds(FALSE);
+  execSetupGame_setScoresType();
   execSetupGame_setScrollDelays();
   execSetupGame_setSnapshotModes();
 
   execSetupGame_setNetworkServerText();
 
+  if (!setup.provide_uploading_tapes)
+    setHideSetupEntry(execOfferUploadTapes);
+
   setup_mode = SETUP_MODE_GAME;
 
   DrawSetupScreen();
@@ -4995,6 +6208,13 @@ static void execSetupGame(void)
     DisableVsyncIfNeeded();
 }
 
+static void execSetupChooseScoresType(void)
+{
+  setup_mode = SETUP_MODE_CHOOSE_SCORES_TYPE;
+
+  DrawSetupScreen();
+}
+
 static void execSetupChooseGameSpeed(void)
 {
   setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED;
@@ -5016,6 +6236,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;
@@ -5273,13 +6500,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;
 
@@ -5290,7 +6525,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))
@@ -5299,8 +6534,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)
@@ -5902,11 +7152,28 @@ static void execSetupTouch(void)
 
 static void execSetupArtwork(void)
 {
+  static ArtworkDirTree *gfx_last_valid = NULL;
+  static ArtworkDirTree *snd_last_valid = NULL;
+  static ArtworkDirTree *mus_last_valid = NULL;
+
+  // current artwork directory may be invalid (level group, parent link)
+  if (!validLevelSeries(artwork.gfx_current))
+    artwork.gfx_current = getFirstValidTreeInfoEntry(gfx_last_valid);
+  if (!validLevelSeries(artwork.snd_current))
+    artwork.snd_current = getFirstValidTreeInfoEntry(snd_last_valid);
+  if (!validLevelSeries(artwork.mus_current))
+    artwork.mus_current = getFirstValidTreeInfoEntry(mus_last_valid);
+
+  // store valid artwork directory information
+  gfx_last_valid = artwork.gfx_current;
+  snd_last_valid = artwork.snd_current;
+  mus_last_valid = artwork.mus_current;
+
 #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;
@@ -6024,6 +7291,11 @@ static void execGadgetNetworkServer(void)
   ClickOnGadget(gi, MB_LEFTBUTTON);
 }
 
+static void execOfferUploadTapes(void)
+{
+  OfferUploadTapes();
+}
+
 static void ToggleNetworkModeIfNeeded(void)
 {
   int font_title = FONT_TITLE_1;
@@ -6082,6 +7354,17 @@ static void ToggleGameSpeedsListIfNeeded(void)
   DrawSetupScreen();
 }
 
+static void ToggleUseApiServerIfNeeded(void)
+{
+  if (runtime.use_api_server == setup.use_api_server)
+    return;
+
+  runtime.use_api_server = setup.use_api_server;
+
+  if (runtime.use_api_server)
+    CheckApiServerTasks();
+}
+
 static void ModifyGameSpeedIfNeeded(void)
 {
   if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) ||
@@ -6129,6 +7412,9 @@ static struct
   void *related_value;
 } hide_related_entry_list[] =
 {
+  { &setup.scores_in_highscore_list,   execSetupChooseScoresType       },
+  { &setup.scores_in_highscore_list,   &scores_type_text               },
+
   { &setup.game_frame_delay,           execSetupChooseGameSpeed        },
   { &setup.game_frame_delay,           &game_speed_text                },
 
@@ -6193,6 +7479,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                  },
@@ -6218,6 +7505,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         },
@@ -6239,15 +7527,24 @@ 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.use_api_server,  "Use Highscore Server:" },
+  { TYPE_ENTER_LIST,   execSetupChooseScoresType,"Scores in Highscore List:" },
+  { TYPE_STRING,       &scores_type_text,      ""                      },
+  { TYPE_ENTER_LIST,   execOfferUploadTapes,   "Upload Tapes to Server" },
+  { 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:"             },
   { TYPE_SWITCH,       &setup.skip_levels,     "Skip Unsolved Levels:" },
   { TYPE_SWITCH,       &setup.increment_levels,"Increment Solved Levels:" },
   { TYPE_SWITCH,       &setup.auto_play_next_level,"Auto-play Next Level:" },
-  { TYPE_SWITCH,       &setup.skip_scores_after_game,"Skip Scores After Game:" },
+  { TYPE_SWITCH,       &setup.count_score_after_game,"Count Score After Game:" },
+  { TYPE_SWITCH,       &setup.show_scores_after_game,"Show Scores After Game:" },
   { TYPE_YES_NO,       &setup.ask_on_game_over, "Ask on Game Over:"    },
+  { TYPE_YES_NO,       &setup.ask_on_quit_game, "Ask on Quit Game:"    },
+  { TYPE_YES_NO,       &setup.ask_on_quit_program, "Ask on Quit Program:" },
   { TYPE_SWITCH,       &setup.autorecord,      "Auto-Record Tapes:"    },
+  { TYPE_SWITCH,       &setup.auto_pause_on_start, "Start Game in Pause Mode:" },
   { TYPE_ENTER_LIST,   execSetupChooseGameSpeed, "Game Speed:"         },
   { TYPE_STRING,       &game_speed_text,       ""                      },
   { TYPE_SWITCH,       &setup.game_speed_extended, "Game Speed Extended List:" },
@@ -6255,10 +7552,26 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_ENTER_LIST,   execSetupChooseScrollDelay, "Scroll Delay:"     },
   { TYPE_STRING,       &scroll_delay_text,     ""                      },
 #endif
-  { TYPE_SWITCH,       &setup.forced_scroll_delay,"Scroll Delay in EM engine:" },
   { TYPE_ENTER_LIST, execSetupChooseSnapshotMode,"Game Engine Snapshot Mode:" },
   { TYPE_STRING,       &snapshot_mode_text,    ""                      },
-  { TYPE_SWITCH,       &setup.show_snapshot_buttons,"Show Snapshot Buttons:" },
+  { TYPE_SWITCH,       &setup.show_load_save_buttons,"Show Load/Save Buttons:" },
+  { TYPE_SWITCH,       &setup.show_undo_redo_buttons,"Show Undo/Redo Buttons:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 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"                  },
 
@@ -6294,6 +7607,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                    }
@@ -6321,9 +7636,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"                  },
 
@@ -6478,6 +7792,10 @@ static struct TokenInfo setup_info_shortcuts_1[] =
   { TYPE_KEY,          &setup.shortcut.save_game, ""                   },
   { TYPE_KEYTEXT,      NULL,           "Quick Load Game from Tape:",   },
   { TYPE_KEY,          &setup.shortcut.load_game, ""                   },
+  { TYPE_KEYTEXT,      NULL,           "Restart Game:",                },
+  { TYPE_KEY,          &setup.shortcut.restart_game, ""                },
+  { TYPE_KEYTEXT,      NULL,           "Replay & Pause Before End:",   },
+  { TYPE_KEY,          &setup.shortcut.pause_before_end, ""            },
   { TYPE_KEYTEXT,      NULL,           "Start Game & Toggle Pause:",   },
   { TYPE_KEY,          &setup.shortcut.toggle_pause, ""                },
   { TYPE_EMPTY,                NULL,                   ""                      },
@@ -6686,14 +8004,18 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
   if (scrollbar_needed && xpos > MENU_SCREEN_START_XPOS)
   {
     int max_menu_text_length = 26;     // maximum text length for classic menu
-    int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
+    int font_xoffset = getFontDrawOffsetX(font_nr);
     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
     int text_font_nr = getMenuTextFont(FONT_MENU_2);
-    int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
+    int text_font_xoffset = getFontDrawOffsetX(text_font_nr);
     int text_width = max_menu_text_length * getFontWidth(text_font_nr);
 
     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;
 
@@ -6708,11 +8030,11 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
                                    MENU_SCREEN_START_XPOS);
     int max_menu_text_length_medium = max_menu_text_length_big * 2;
     int check_font_nr = FONT_OPTION_ON; // known font that needs correction
-    int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
-    int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
+    int font1_xoffset = getFontDrawOffsetX(font_nr);
+    int font2_xoffset = getFontDrawOffsetX(check_font_nr);
     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
     int text_font_nr = getMenuTextFont(FONT_MENU_2);
-    int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
+    int text_font_xoffset = getFontDrawOffsetX(text_font_nr);
     int text_width = max_menu_text_length_medium * getFontWidth(text_font_nr);
     boolean correct_font_draw_xoffset = FALSE;
 
@@ -6728,7 +8050,7 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
     // (this can happen for extreme/wrong values for font draw offset)
     if (correct_font_draw_xoffset)
     {
-      font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
+      font_draw_xoffset_old = getFontDrawOffsetX(font_nr);
       font_draw_xoffset_modified = TRUE;
 
       if (type & TYPE_KEY)
@@ -6810,12 +8132,16 @@ 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)
     ToggleNetworkModeIfNeeded();
 
+  // API server mode may have changed at this point
+  if (si->value == &setup.use_api_server)
+    ToggleUseApiServerIfNeeded();
+
   // game speed list may have changed at this point
   if (si->value == &setup.game_speed_extended)
     ToggleGameSpeedsListIfNeeded();
@@ -6893,6 +8219,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;
@@ -7475,7 +8806,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];
@@ -7538,9 +8869,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
@@ -7797,7 +9128,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
@@ -7956,6 +9287,8 @@ static boolean ConfigureVirtualButtonsMain(void)
 
   overlay.grid_button_highlight = grid_button[step_nr];
 
+  UnmapAllGadgets();
+
   FadeSetEnterMenu();
   FadeOut(REDRAW_FIELD);
 
@@ -8224,6 +9557,8 @@ void DrawSetupScreen(void)
 
   if (setup_mode == SETUP_MODE_INPUT)
     DrawSetupScreen_Input();
+  else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE)
+    DrawChooseTree(&scores_type_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
     DrawChooseTree(&game_speed_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
@@ -8306,6 +9641,8 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
 {
   if (setup_mode == SETUP_MODE_INPUT)
     HandleSetupScreen_Input(mx, my, dx, dy, button);
+  else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE)
+    HandleChooseTree(mx, my, dx, dy, button, &scores_type_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
     HandleChooseTree(mx, my, dx, dy, button, &game_speed_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
@@ -8367,10 +9704,10 @@ void HandleGameActions(void)
   if (game_status != GAME_MODE_PLAYING)
     return;
 
-  GameActions();       // main game loop
+  GameActions();               // main game loop
 
   if (tape.auto_play && !tape.playing)
-    AutoPlayTape();    // continue automatically playing next tape
+    AutoPlayTapesContinue();   // continue automatically playing next tape
 }
 
 
@@ -8380,6 +9717,7 @@ static struct
 {
   int gfx_unpressed, gfx_pressed;
   struct MenuPosInfo *pos;
+  boolean *check_value;
   int gadget_id;
   int screen_mask;
   unsigned int event_mask;
@@ -8389,7 +9727,7 @@ static struct
 {
   {
     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,
@@ -8397,15 +9735,47 @@ static struct
   },
   {
     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,
     FALSE, "next level"
   },
+  {
+    IMG_MENU_BUTTON_PREV_LEVEL2, IMG_MENU_BUTTON_PREV_LEVEL2_ACTIVE,
+    &menu.scores.button.prev_level, NULL,
+    SCREEN_CTRL_ID_PREV_LEVEL2,
+    SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    FALSE, "previous level"
+  },
+  {
+    IMG_MENU_BUTTON_NEXT_LEVEL2, IMG_MENU_BUTTON_NEXT_LEVEL2_ACTIVE,
+    &menu.scores.button.next_level, NULL,
+    SCREEN_CTRL_ID_NEXT_LEVEL2,
+    SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    FALSE, "next level"
+  },
+  {
+    IMG_MENU_BUTTON_PREV_SCORE, IMG_MENU_BUTTON_PREV_SCORE_ACTIVE,
+    &menu.scores.button.prev_score, NULL,
+    SCREEN_CTRL_ID_PREV_SCORE,
+    SCREEN_MASK_SCORES_INFO,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    FALSE, "previous score"
+  },
+  {
+    IMG_MENU_BUTTON_NEXT_SCORE, IMG_MENU_BUTTON_NEXT_SCORE_ACTIVE,
+    &menu.scores.button.next_score, NULL,
+    SCREEN_CTRL_ID_NEXT_SCORE,
+    SCREEN_MASK_SCORES_INFO,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    FALSE, "next score"
+  },
   {
     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,
@@ -8413,7 +9783,7 @@ static struct
   },
   {
     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,
@@ -8421,7 +9791,7 @@ static struct
   },
   {
     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,
@@ -8429,7 +9799,7 @@ static struct
   },
   {
     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,
@@ -8437,7 +9807,7 @@ static struct
   },
   {
     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,
@@ -8445,7 +9815,7 @@ static struct
   },
   {
     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,
@@ -8453,15 +9823,23 @@ static struct
   },
   {
     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,
     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,
+    &menu.setup.button.touch_back, NULL,
     SCREEN_CTRL_ID_TOUCH_PREV_PAGE,
     SCREEN_MASK_TOUCH,
     GD_EVENT_RELEASED,
@@ -8469,7 +9847,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_NEXT, IMG_MENU_BUTTON_TOUCH_NEXT,
-    &menu.setup.button.touch_next,
+    &menu.setup.button.touch_next, NULL,
     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE,
     SCREEN_MASK_TOUCH,
     GD_EVENT_RELEASED,
@@ -8477,7 +9855,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_BACK2, IMG_MENU_BUTTON_TOUCH_BACK2,
-    &menu.setup.button.touch_back2,
+    &menu.setup.button.touch_back2, NULL,
     SCREEN_CTRL_ID_TOUCH_PREV_PAGE2,
     SCREEN_MASK_TOUCH2,
     GD_EVENT_RELEASED,
@@ -8485,7 +9863,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_NEXT2, IMG_MENU_BUTTON_TOUCH_NEXT2,
-    &menu.setup.button.touch_next2,
+    &menu.setup.button.touch_next2, NULL,
     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2,
     SCREEN_MASK_TOUCH2,
     GD_EVENT_RELEASED,
@@ -8564,12 +9942,18 @@ static void CreateScreenMenubuttons(void)
   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
   {
     struct MenuPosInfo *pos = menubutton_info[i].pos;
+    int screen_mask = menubutton_info[i].screen_mask;
     boolean is_touch_button = menubutton_info[i].is_touch_button;
+    boolean is_check_button = menubutton_info[i].check_value != NULL;
+    boolean is_score_button = (screen_mask & SCREEN_MASK_SCORES_INFO);
     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;
 
@@ -8587,6 +9971,10 @@ 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)
     {
@@ -8594,6 +9982,33 @@ static void CreateScreenMenubuttons(void)
       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;
+    }
+
+    if (is_score_button)
+    {
+      // if x/y set to -1, dynamically place buttons next to title text
+      int title_width = getTextWidth(INFOTEXT_SCORE_ENTRY, FONT_TITLE_1);
+
+      if (pos->x == -1)
+       x = (id == SCREEN_CTRL_ID_PREV_LEVEL2 ?
+            SX + (SXSIZE - title_width) / 2 - width * 3 / 2 :
+            id == SCREEN_CTRL_ID_NEXT_LEVEL2 ?
+            SX + (SXSIZE + title_width) / 2 + width / 2 : 0);
+
+      if (pos->y == -1)
+       y = (id == SCREEN_CTRL_ID_PREV_LEVEL2 ||
+            id == SCREEN_CTRL_ID_NEXT_LEVEL2 ? mSY + MENU_TITLE1_YPOS : 0);
+    }
+
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
                      GDI_IMAGE_ID, gfx_unpressed,
@@ -8602,10 +10017,13 @@ 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,
@@ -8613,7 +10031,7 @@ static void CreateScreenMenubuttons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8681,7 +10099,7 @@ static void CreateScreenScrollbuttons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8762,7 +10180,7 @@ static void CreateScreenScrollbars(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8808,7 +10226,7 @@ static void CreateScreenTextInputGadgets(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8832,6 +10250,15 @@ void FreeScreenGadgets(void)
     FreeGadget(screen_gadget[i]);
 }
 
+static void RedrawScreenMenuGadgets(int screen_mask)
+{
+  int i;
+
+  for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
+    if (screen_mask & menubutton_info[i].screen_mask)
+      RedrawGadget(screen_gadget[menubutton_info[i].gadget_id]);
+}
+
 static void MapScreenMenuGadgets(int screen_mask)
 {
   int i;
@@ -8882,11 +10309,41 @@ static void MapScreenGadgets(int num_entries)
     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
 }
 
+static void UnmapScreenGadgets()
+{
+  int i;
+
+  for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
+    UnmapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
+
+  for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
+    UnmapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
+}
+
 static void MapScreenTreeGadgets(TreeInfo *ti)
 {
   MapScreenGadgets(numTreeInfoInGroup(ti));
 }
 
+static void UnmapScreenTreeGadgets(void)
+{
+  UnmapScreenGadgets();
+}
+
+static void AdjustScoreInfoButtons(int x, int y1, int y2)
+{
+  struct GadgetInfo *gi_1 = screen_gadget[SCREEN_CTRL_ID_PREV_SCORE];
+  struct GadgetInfo *gi_2 = screen_gadget[SCREEN_CTRL_ID_NEXT_SCORE];
+  struct MenuPosInfo *pos_1 = menubutton_info[SCREEN_CTRL_ID_PREV_SCORE].pos;
+  struct MenuPosInfo *pos_2 = menubutton_info[SCREEN_CTRL_ID_NEXT_SCORE].pos;
+
+  if (pos_1->x == -1 && pos_1->y == -1)
+    ModifyGadget(gi_1, GDI_X, x, GDI_Y, y1, GDI_END);
+
+  if (pos_2->x == -1 && pos_2->y == -1)
+    ModifyGadget(gi_2, GDI_X, x, GDI_Y, y2, GDI_END);
+}
+
 static void HandleScreenGadgets(struct GadgetInfo *gi)
 {
   int id = gi->custom_id;
@@ -8905,6 +10362,22 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT);
       break;
 
+    case SCREEN_CTRL_ID_PREV_LEVEL2:
+      HandleHallOfFame_SelectLevel(step, -1);
+      break;
+
+    case SCREEN_CTRL_ID_NEXT_LEVEL2:
+      HandleHallOfFame_SelectLevel(step, +1);
+      break;
+
+    case SCREEN_CTRL_ID_PREV_SCORE:
+      HandleScoreInfo_SelectScore(step, -1);
+      break;
+
+    case SCREEN_CTRL_ID_NEXT_SCORE:
+      HandleScoreInfo_SelectScore(step, +1);
+      break;
+
     case SCREEN_CTRL_ID_FIRST_LEVEL:
       HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
       break;
@@ -8935,6 +10408,11 @@ 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:
@@ -8943,7 +10421,9 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       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);
@@ -8951,10 +10431,14 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
        HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
       else if (game_status == GAME_MODE_INFO)
        HandleInfoScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
+      else if (game_status == GAME_MODE_SCORES)
+       HandleHallOfFame(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
       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);
@@ -8962,10 +10446,14 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
        HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
       else if (game_status == GAME_MODE_INFO)
        HandleInfoScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
+      else if (game_status == GAME_MODE_SCORES)
+       HandleHallOfFame(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
       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);
@@ -8973,6 +10461,8 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
        HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
       else if (game_status == GAME_MODE_INFO)
        HandleInfoScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
+      else if (game_status == GAME_MODE_SCORES)
+       HandleHallOfFame(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
       break;
 
     case SCREEN_CTRL_ID_NETWORK_SERVER:
@@ -9119,3 +10609,139 @@ void DrawScreenAfterAddingSet(char *tree_subdir_new, int tree_type)
     }
   }
 }
+
+static int UploadTapes(void)
+{
+  SetGameStatus(GAME_MODE_LOADING);
+
+  FadeSetEnterScreen();
+  FadeOut(REDRAW_ALL);
+
+  ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+  FadeIn(REDRAW_ALL);
+
+  DrawInitTextHead("Uploading tapes");
+
+  global.autoplay_mode = AUTOPLAY_MODE_UPLOAD;
+  global.autoplay_leveldir = "ALL";
+  global.autoplay_all = TRUE;
+
+  int num_tapes_uploaded = AutoPlayTapes();
+
+  global.autoplay_mode = AUTOPLAY_MODE_NONE;
+  global.autoplay_leveldir = NULL;
+  global.autoplay_all = FALSE;
+
+  SetGameStatus(GAME_MODE_MAIN);
+
+  DrawMainMenu();
+
+  return num_tapes_uploaded;
+}
+
+static boolean OfferUploadTapes(void)
+{
+  if (!Request(setup.has_remaining_tapes ?
+              "Upload missing tapes to the high score server now?" :
+              "Upload all your tapes to the high score server now?", REQ_ASK))
+    return FALSE;
+
+  int num_tapes_uploaded = UploadTapes();
+  char message[100];
+
+  if (num_tapes_uploaded < 0)
+  {
+    num_tapes_uploaded = -num_tapes_uploaded - 1;
+
+    if (num_tapes_uploaded == 0)
+      sprintf(message, "Upload failed! No tapes uploaded!");
+    else if (num_tapes_uploaded == 1)
+      sprintf(message, "Upload failed! Only 1 tape uploaded!");
+    else
+      sprintf(message, "Upload failed! Only %d tapes uploaded!",
+             num_tapes_uploaded);
+
+    Request(message, REQ_CONFIRM);
+
+    // if uploading tapes failed, add tape upload entry to setup menu
+    setup.provide_uploading_tapes = TRUE;
+    setup.has_remaining_tapes = TRUE;
+
+    SaveSetup_ServerSetup();
+
+    return FALSE;
+  }
+
+  if (num_tapes_uploaded == 0)
+    sprintf(message, "No tapes uploaded!");
+  else if (num_tapes_uploaded == 1)
+    sprintf(message, "1 tape uploaded!");
+  else
+    sprintf(message, "%d tapes uploaded!", num_tapes_uploaded);
+
+  Request(message, REQ_CONFIRM);
+
+  if (num_tapes_uploaded > 0)
+    Request("New scores will be visible after a few minutes!", REQ_CONFIRM);
+
+  // after all tapes have been uploaded, remove entry from setup menu
+  setup.provide_uploading_tapes = FALSE;
+  setup.has_remaining_tapes = FALSE;
+
+  SaveSetup_ServerSetup();
+
+  return TRUE;
+}
+
+static void CheckUploadTapes(void)
+{
+  if (!setup.ask_for_uploading_tapes)
+    return;
+
+  // after asking for uploading tapes, do not ask again
+  setup.ask_for_uploading_tapes = FALSE;
+  setup.ask_for_remaining_tapes = FALSE;
+
+  if (directoryExists(getTapeDir(NULL)))
+  {
+    boolean tapes_uploaded = OfferUploadTapes();
+
+    if (!tapes_uploaded)
+    {
+      Request(setup.has_remaining_tapes ?
+             "You can upload missing tapes from the setup menu later!" :
+             "You can upload your tapes from the setup menu later!",
+             REQ_CONFIRM);
+    }
+  }
+  else
+  {
+    // if tapes directory does not exist yet, never offer uploading all tapes
+    setup.provide_uploading_tapes = FALSE;
+  }
+
+  SaveSetup_ServerSetup();
+}
+
+static void UpgradePlayerUUID(void)
+{
+  ApiResetUUIDAsThread(getUUID());
+}
+
+static void CheckUpgradePlayerUUID(void)
+{
+  if (setup.player_version > 1)
+    return;
+
+  UpgradePlayerUUID();
+}
+
+void CheckApiServerTasks(void)
+{
+  // check if the player's UUID has to be upgraded
+  CheckUpgradePlayerUUID();
+
+  // check if there are any tapes to be uploaded
+  CheckUploadTapes();
+}