added setup option to always play native BD caves with random colors
[rocksndiamonds.git] / src / screens.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // screens.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "screens.h"
15 #include "events.h"
16 #include "game.h"
17 #include "tools.h"
18 #include "editor.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "anim.h"
22 #include "network.h"
23 #include "init.h"
24 #include "config.h"
25 #include "api.h"
26
27
28 #define DEBUG_JOYSTICKS                         0
29
30
31 // screens on the info screen
32 #define INFO_MODE_MAIN                          0
33 #define INFO_MODE_TITLE                         1
34 #define INFO_MODE_ELEMENTS                      2
35 #define INFO_MODE_MUSIC                         3
36 #define INFO_MODE_CREDITS                       4
37 #define INFO_MODE_PROGRAM                       5
38 #define INFO_MODE_VERSION                       6
39 #define INFO_MODE_LEVELSET                      7
40
41 #define MAX_INFO_MODES                          8
42
43 // screens on the setup screen
44 // (must match GFX_SPECIAL_ARG_SETUP_* values as defined in src/main.h)
45 // (should also match corresponding entries in src/conf_gfx.c)
46 #define SETUP_MODE_MAIN                         0
47 #define SETUP_MODE_GAME                         1
48 #define SETUP_MODE_ENGINES                      2
49 #define SETUP_MODE_EDITOR                       3
50 #define SETUP_MODE_GRAPHICS                     4
51 #define SETUP_MODE_SOUND                        5
52 #define SETUP_MODE_ARTWORK                      6
53 #define SETUP_MODE_INPUT                        7
54 #define SETUP_MODE_TOUCH                        8
55 #define SETUP_MODE_SHORTCUTS                    9
56 #define SETUP_MODE_SHORTCUTS_1                  10
57 #define SETUP_MODE_SHORTCUTS_2                  11
58 #define SETUP_MODE_SHORTCUTS_3                  12
59 #define SETUP_MODE_SHORTCUTS_4                  13
60 #define SETUP_MODE_SHORTCUTS_5                  14
61
62 // sub-screens on the setup screen (generic)
63 #define SETUP_MODE_CHOOSE_ARTWORK               15
64 #define SETUP_MODE_CHOOSE_OTHER                 16
65
66 // sub-screens on the setup screen (specific)
67 #define SETUP_MODE_CHOOSE_SCORES_TYPE           17
68 #define SETUP_MODE_CHOOSE_GAME_SPEED            18
69 #define SETUP_MODE_CHOOSE_SCROLL_DELAY          19
70 #define SETUP_MODE_CHOOSE_SNAPSHOT_MODE         20
71 #define SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE      21
72 #define SETUP_MODE_CHOOSE_BD_PALETTE_C64        22
73 #define SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV     23
74 #define SETUP_MODE_CHOOSE_BD_PALETTE_ATARI      24
75 #define SETUP_MODE_CHOOSE_BD_COLOR_TYPE         25
76 #define SETUP_MODE_CHOOSE_WINDOW_SIZE           26
77 #define SETUP_MODE_CHOOSE_SCALING_TYPE          27
78 #define SETUP_MODE_CHOOSE_RENDERING             28
79 #define SETUP_MODE_CHOOSE_VSYNC                 29
80 #define SETUP_MODE_CHOOSE_GRAPHICS              30
81 #define SETUP_MODE_CHOOSE_SOUNDS                31
82 #define SETUP_MODE_CHOOSE_MUSIC                 32
83 #define SETUP_MODE_CHOOSE_VOLUME_SIMPLE         33
84 #define SETUP_MODE_CHOOSE_VOLUME_LOOPS          34
85 #define SETUP_MODE_CHOOSE_VOLUME_MUSIC          35
86 #define SETUP_MODE_CHOOSE_TOUCH_CONTROL         36
87 #define SETUP_MODE_CHOOSE_MOVE_DISTANCE         37
88 #define SETUP_MODE_CHOOSE_DROP_DISTANCE         38
89 #define SETUP_MODE_CHOOSE_TRANSPARENCY          39
90 #define SETUP_MODE_CHOOSE_GRID_XSIZE_0          40
91 #define SETUP_MODE_CHOOSE_GRID_YSIZE_0          41
92 #define SETUP_MODE_CHOOSE_GRID_XSIZE_1          42
93 #define SETUP_MODE_CHOOSE_GRID_YSIZE_1          43
94 #define SETUP_MODE_CONFIG_VIRT_BUTTONS          44
95
96 #define MAX_SETUP_MODES                         45
97
98 #define MAX_MENU_MODES                          MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
99
100 // info screen titles
101 #define STR_INFO_MAIN                           "Info Screen"
102 #define STR_INFO_TITLE                          "Title Screen"
103 #define STR_INFO_ELEMENTS                       "Game Elements"
104 #define STR_INFO_MUSIC                          "Music Info"
105 #define STR_INFO_CREDITS                        "Credits"
106 #define STR_INFO_PROGRAM                        "Program Info"
107 #define STR_INFO_VERSION                        "Version Info"
108 #define STR_INFO_LEVELSET                       "Level Set Info"
109 #define STR_INFO_EXIT                           "Exit"
110
111 // setup screen titles
112 #define STR_SETUP_MAIN                          "Setup"
113 #define STR_SETUP_GAME                          "Game & Menu"
114 #define STR_SETUP_ENGINES                       "Game Engines"
115 #define STR_SETUP_EDITOR                        "Editor"
116 #define STR_SETUP_GRAPHICS                      "Graphics"
117 #define STR_SETUP_SOUND                         "Sound & Music"
118 #define STR_SETUP_ARTWORK                       "Custom Artwork"
119 #define STR_SETUP_INPUT                         "Input Devices"
120 #define STR_SETUP_TOUCH                         "Touch Controls"
121 #define STR_SETUP_SHORTCUTS                     "Key Shortcuts"
122 #define STR_SETUP_EXIT                          "Exit"
123 #define STR_SETUP_SAVE_AND_EXIT                 "Save and Exit"
124
125 #define STR_SETUP_CHOOSE_SCORES_TYPE            "Scores Type"
126 #define STR_SETUP_CHOOSE_GAME_SPEED             "Game Speed"
127 #define STR_SETUP_CHOOSE_SCROLL_DELAY           "Scroll Delay"
128 #define STR_SETUP_CHOOSE_SNAPSHOT_MODE          "Snapshot Mode"
129 #define STR_SETUP_CHOOSE_GAME_ENGINE_TYPE       "Game Engine"
130 #define STR_SETUP_CHOOSE_BD_PALETTE_C64         "Palette (C64)"
131 #define STR_SETUP_CHOOSE_BD_PALETTE_C64DTV      "Palette (C64DTV)"
132 #define STR_SETUP_CHOOSE_BD_PALETTE_ATARI       "Palette (Atari)"
133 #define STR_SETUP_CHOOSE_BD_COLOR_TYPE          "Color Type"
134 #define STR_SETUP_CHOOSE_WINDOW_SIZE            "Window Scaling"
135 #define STR_SETUP_CHOOSE_SCALING_TYPE           "Anti-Aliasing"
136 #define STR_SETUP_CHOOSE_RENDERING              "Rendering Mode"
137 #define STR_SETUP_CHOOSE_VSYNC                  "VSync Mode"
138 #define STR_SETUP_CHOOSE_VOLUME_SIMPLE          "Sound Volume"
139 #define STR_SETUP_CHOOSE_VOLUME_LOOPS           "Loops Volume"
140 #define STR_SETUP_CHOOSE_VOLUME_MUSIC           "Music Volume"
141 #define STR_SETUP_CHOOSE_TOUCH_CONTROL          "Control Type"
142 #define STR_SETUP_CHOOSE_MOVE_DISTANCE          "Move Distance"
143 #define STR_SETUP_CHOOSE_DROP_DISTANCE          "Drop Distance"
144 #define STR_SETUP_CHOOSE_TRANSPARENCY           "Transparency"
145 #define STR_SETUP_CHOOSE_GRID_XSIZE_0           "Horiz. Buttons"
146 #define STR_SETUP_CHOOSE_GRID_YSIZE_0           "Vert. Buttons"
147 #define STR_SETUP_CHOOSE_GRID_XSIZE_1           "Horiz. Buttons"
148 #define STR_SETUP_CHOOSE_GRID_YSIZE_1           "Vert. Buttons"
149
150 // other screen text constants
151 #define STR_CHOOSE_TREE_EDIT                    "Edit"
152 #define MENU_CHOOSE_TREE_FONT(x)                (FONT_TEXT_1 + (x))
153 #define MENU_CHOOSE_TREE_COLOR(ti, a)           TREE_COLOR(ti, a)
154
155 #define TEXT_MAIN_MENU                          "Press any key or button for main menu"
156 #define TEXT_INFO_MENU                          "Press any key or button for info menu"
157 #define TEXT_NEXT_PAGE                          "Press any key or button for next page"
158 #define TEXT_NEXT_MENU                          (info_screens_from_main ?               \
159                                                  TEXT_MAIN_MENU : TEXT_INFO_MENU)
160
161 // for input setup functions
162 #define SETUPINPUT_SCREEN_POS_START             0
163 #define SETUPINPUT_SCREEN_POS_EMPTY1            3
164 #define SETUPINPUT_SCREEN_POS_EMPTY2            12
165 #define SETUPINPUT_SCREEN_POS_END               13
166
167 #define MENU_SETUP_FONT_TITLE                   FONT_TEXT_1
168 #define MENU_SETUP_FONT_TEXT                    FONT_TITLE_2
169
170 #define MAX_SETUP_TEXT_INPUT_LEN                28
171
172 // for various menu stuff
173 #define MENU_SCREEN_START_XPOS                  1
174 #define MENU_SCREEN_START_YPOS                  2
175 #define MENU_SCREEN_VALUE_XPOS                  (SCR_FIELDX - 3)
176 #define MENU_SCREEN_TEXT2_XPOS                  (SCR_FIELDX - 2)
177 #define MENU_SCREEN_MAX_XPOS                    (SCR_FIELDX - 1)
178 #define MENU_TITLE1_YPOS                        8
179 #define MENU_TITLE2_YPOS                        46
180 #define MENU_INFO_FONT_TITLE                    FONT_TEXT_1
181 #define MENU_INFO_FONT_HEAD                     FONT_TEXT_2
182 #define MENU_INFO_FONT_TEXT                     FONT_TEXT_3
183 #define MENU_INFO_FONT_FOOT                     FONT_TEXT_4
184 #define MENU_INFO_SPACE_HEAD                    (menu.headline2_spacing_info[info_mode])
185 #define MENU_SCREEN_INFO_SPACE_LEFT             (menu.left_spacing_info[info_mode])
186 #define MENU_SCREEN_INFO_SPACE_MIDDLE           (menu.middle_spacing_info[info_mode])
187 #define MENU_SCREEN_INFO_SPACE_RIGHT            (menu.right_spacing_info[info_mode])
188 #define MENU_SCREEN_INFO_SPACE_TOP              (menu.top_spacing_info[info_mode])
189 #define MENU_SCREEN_INFO_SPACE_BOTTOM           (menu.bottom_spacing_info[info_mode])
190 #define MENU_SCREEN_INFO_SPACE_LINE             (menu.line_spacing_info[info_mode])
191 #define MENU_SCREEN_INFO_SPACE_EXTRA            (menu.extra_spacing_info[info_mode])
192 #define MENU_SCREEN_INFO_TILE_SIZE_RAW          (menu.tile_size_info[info_mode])
193 #define MENU_SCREEN_INFO_TILE_SIZE              (MENU_SCREEN_INFO_TILE_SIZE_RAW > 0 ?           \
194                                                  MENU_SCREEN_INFO_TILE_SIZE_RAW : TILEY)
195 #define MENU_SCREEN_INFO_ENTRY_SIZE_RAW         (menu.list_entry_size_info[info_mode])
196 #define MENU_SCREEN_INFO_ENTRY_SIZE             (MAX(MENU_SCREEN_INFO_ENTRY_SIZE_RAW,           \
197                                                      MENU_SCREEN_INFO_TILE_SIZE))
198 #define MENU_SCREEN_INFO_YSTART                 MENU_SCREEN_INFO_SPACE_TOP
199 #define MENU_SCREEN_INFO_YSTEP                  (MENU_SCREEN_INFO_ENTRY_SIZE +                  \
200                                                  MENU_SCREEN_INFO_SPACE_EXTRA)
201 #define MENU_SCREEN_INFO_YBOTTOM                (SYSIZE - MENU_SCREEN_INFO_SPACE_BOTTOM)
202 #define MENU_SCREEN_INFO_YSIZE                  (MENU_SCREEN_INFO_YBOTTOM -                     \
203                                                  MENU_SCREEN_INFO_YSTART - TILEY / 2)
204 #define MAX_INFO_ELEMENTS_IN_ARRAY              128
205 #define MAX_INFO_ELEMENTS_ON_SCREEN             (SYSIZE / TILEY)
206 #define MAX_INFO_ELEMENTS                       MIN(MAX_INFO_ELEMENTS_IN_ARRAY,                 \
207                                                     MAX_INFO_ELEMENTS_ON_SCREEN)
208 #define STD_INFO_ELEMENTS_ON_SCREEN             10
209 #define DYN_INFO_ELEMENTS_ON_SCREEN             (MENU_SCREEN_INFO_YSIZE / MENU_SCREEN_INFO_YSTEP)
210 #define DEFAULT_INFO_ELEMENTS                   MIN(STD_INFO_ELEMENTS_ON_SCREEN,                \
211                                                     DYN_INFO_ELEMENTS_ON_SCREEN)
212 #define NUM_INFO_ELEMENTS_FROM_CONF                                     \
213                                 (menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] > 0 ?       \
214                                  menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] :           \
215                                  DEFAULT_INFO_ELEMENTS)
216 #define NUM_INFO_ELEMENTS_ON_SCREEN             MIN(NUM_INFO_ELEMENTS_FROM_CONF, MAX_INFO_ELEMENTS)
217 #define MAX_MENU_ENTRIES_ON_SCREEN              (SCR_FIELDY - MENU_SCREEN_START_YPOS)
218 #define MAX_MENU_TEXT_LENGTH_BIG                13
219 #define MAX_MENU_TEXT_LENGTH_MEDIUM             (MAX_MENU_TEXT_LENGTH_BIG * 2)
220
221 // screen gadget identifiers
222 #define SCREEN_CTRL_ID_PREV_LEVEL               0
223 #define SCREEN_CTRL_ID_NEXT_LEVEL               1
224 #define SCREEN_CTRL_ID_PREV_LEVEL2              2
225 #define SCREEN_CTRL_ID_NEXT_LEVEL2              3
226 #define SCREEN_CTRL_ID_PREV_SCORE               4
227 #define SCREEN_CTRL_ID_NEXT_SCORE               5
228 #define SCREEN_CTRL_ID_PLAY_TAPE                6
229 #define SCREEN_CTRL_ID_FIRST_LEVEL              7
230 #define SCREEN_CTRL_ID_LAST_LEVEL               8
231 #define SCREEN_CTRL_ID_LEVEL_NUMBER             9
232 #define SCREEN_CTRL_ID_PREV_PLAYER              10
233 #define SCREEN_CTRL_ID_NEXT_PLAYER              11
234 #define SCREEN_CTRL_ID_INSERT_SOLUTION          12
235 #define SCREEN_CTRL_ID_PLAY_SOLUTION            13
236 #define SCREEN_CTRL_ID_LEVELSET_INFO            14
237 #define SCREEN_CTRL_ID_SWITCH_ECS_AGA           15
238 #define SCREEN_CTRL_ID_TOUCH_PREV_PAGE          16
239 #define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE          17
240 #define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2         18
241 #define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2         19
242
243 #define NUM_SCREEN_MENUBUTTONS                  20
244
245 #define SCREEN_CTRL_ID_SCROLL_UP                20
246 #define SCREEN_CTRL_ID_SCROLL_DOWN              21
247 #define SCREEN_CTRL_ID_SCROLL_VERTICAL          22
248 #define SCREEN_CTRL_ID_NETWORK_SERVER           23
249
250 #define NUM_SCREEN_GADGETS                      24
251
252 #define NUM_SCREEN_SCROLLBUTTONS                2
253 #define NUM_SCREEN_SCROLLBARS                   1
254 #define NUM_SCREEN_TEXTINPUT                    1
255
256 #define SCREEN_MASK_MAIN                        (1 << 0)
257 #define SCREEN_MASK_MAIN_HAS_SOLUTION           (1 << 1)
258 #define SCREEN_MASK_MAIN_HAS_SET_INFO           (1 << 2)
259 #define SCREEN_MASK_INPUT                       (1 << 3)
260 #define SCREEN_MASK_TOUCH                       (1 << 4)
261 #define SCREEN_MASK_TOUCH2                      (1 << 5)
262 #define SCREEN_MASK_SCORES                      (1 << 6)
263 #define SCREEN_MASK_SCORES_INFO                 (1 << 7)
264
265 // graphic position and size values for buttons and scrollbars
266 #define SC_MENUBUTTON_XSIZE                     TILEX
267 #define SC_MENUBUTTON_YSIZE                     TILEY
268
269 #define SC_SCROLLBUTTON_XSIZE                   TILEX
270 #define SC_SCROLLBUTTON_YSIZE                   TILEY
271
272 #define SC_SCROLLBAR_XPOS                       (SXSIZE - SC_SCROLLBUTTON_XSIZE)
273
274 #define SC_SCROLL_VERTICAL_XSIZE                SC_SCROLLBUTTON_XSIZE
275 #define SC_SCROLL_VERTICAL_YSIZE                ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
276                                                  SC_SCROLLBUTTON_YSIZE)
277
278 #define SC_SCROLL_UP_XPOS                       SC_SCROLLBAR_XPOS
279 #define SC_SCROLL_UP_YPOS                       (2 * SC_SCROLLBUTTON_YSIZE)
280
281 #define SC_SCROLL_VERTICAL_XPOS                 SC_SCROLLBAR_XPOS
282 #define SC_SCROLL_VERTICAL_YPOS                 (SC_SCROLL_UP_YPOS + \
283                                                  SC_SCROLLBUTTON_YSIZE)
284
285 #define SC_SCROLL_DOWN_XPOS                     SC_SCROLLBAR_XPOS
286 #define SC_SCROLL_DOWN_YPOS                     (SC_SCROLL_VERTICAL_YPOS + \
287                                                  SC_SCROLL_VERTICAL_YSIZE)
288
289 #define SC_BORDER_SIZE                          14
290
291
292 // forward declarations of internal functions
293 static void HandleScreenGadgets(struct GadgetInfo *);
294 static void HandleSetupScreen_Generic(int, int, int, int, int);
295 static void HandleSetupScreen_Input(int, int, int, int, int);
296 static void CustomizeKeyboard(int);
297 static void ConfigureJoystick(int);
298 static void ConfigureVirtualButtons(void);
299 static void execSetupGame(void);
300 static void execSetupEngines(void);
301 static void execSetupEditor(void);
302 static void execSetupGraphics(void);
303 static void execSetupSound(void);
304 static void execSetupTouch(void);
305 static void execSetupArtwork(void);
306 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
307
308 static void DrawChoosePlayerName(void);
309 static void DrawChooseLevelSet(void);
310 static void DrawChooseLevelNr(void);
311 static void DrawScoreInfo(int);
312 static void DrawScoreInfo_Content(int);
313 static void DrawInfoScreen(void);
314 static void DrawSetupScreen(void);
315 static void DrawTypeName(void);
316
317 static void DrawInfoScreen_NotAvailable(char *, char *);
318 static void DrawInfoScreen_HelpAnim(int, int, boolean);
319 static void DrawInfoScreen_HelpText(int, int, int, int);
320 static void HandleInfoScreen_Main(int, int, int, int, int);
321 static void HandleInfoScreen_TitleScreen(int, int, int);
322 static void HandleInfoScreen_Elements(int, int, int);
323 static void HandleInfoScreen_Music(int, int, int);
324 static void HandleInfoScreen_Version(int);
325 static void HandleInfoScreen_Generic(int, int, int);
326
327 static void ModifyGameSpeedIfNeeded(void);
328 static void DisableVsyncIfNeeded(void);
329
330 static void RedrawScreenMenuGadgets(int);
331 static void MapScreenMenuGadgets(int);
332 static void UnmapScreenMenuGadgets(int);
333 static void MapScreenGadgets(int);
334 static void UnmapScreenGadgets(void);
335 static void MapScreenTreeGadgets(TreeInfo *);
336 static void UnmapScreenTreeGadgets(void);
337
338 static void UpdateScreenMenuGadgets(int, boolean);
339 static void AdjustScoreInfoButtons_SelectScore(int, int, int);
340 static void AdjustScoreInfoButtons_PlayTape(int, int, boolean);
341
342 static boolean OfferUploadTapes(void);
343 static void execOfferUploadTapes(void);
344
345 static void DrawHallOfFame_setScoreEntries(void);
346 static void HandleHallOfFame_SelectLevel(int, int);
347 static char *getHallOfFameRankText(int, int);
348 static char *getHallOfFameScoreText(int, int);
349 static char *getInfoScreenTitle_Generic(void);
350 static int getInfoScreenBackgroundImage_Generic(void);
351 static int getInfoScreenBackgroundSound_Generic(void);
352 static int getInfoScreenBackgroundMusic_Generic(void);
353
354 static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *);
355
356 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
357
358 static int info_mode = INFO_MODE_MAIN;
359 static int setup_mode = SETUP_MODE_MAIN;
360
361 static boolean info_screens_from_main = FALSE;
362
363 static TreeInfo *window_sizes = NULL;
364 static TreeInfo *window_size_current = NULL;
365
366 static TreeInfo *scaling_types = NULL;
367 static TreeInfo *scaling_type_current = NULL;
368
369 static TreeInfo *rendering_modes = NULL;
370 static TreeInfo *rendering_mode_current = NULL;
371
372 static TreeInfo *vsync_modes = NULL;
373 static TreeInfo *vsync_mode_current = NULL;
374
375 static TreeInfo *scroll_delays = NULL;
376 static TreeInfo *scroll_delay_current = NULL;
377
378 static TreeInfo *snapshot_modes = NULL;
379 static TreeInfo *snapshot_mode_current = NULL;
380
381 static TreeInfo *game_engine_types = NULL;
382 static TreeInfo *game_engine_type_current = NULL;
383
384 static TreeInfo *bd_palettes_c64 = NULL;
385 static TreeInfo *bd_palette_c64_current = NULL;
386
387 static TreeInfo *bd_palettes_c64dtv = NULL;
388 static TreeInfo *bd_palette_c64dtv_current = NULL;
389
390 static TreeInfo *bd_palettes_atari = NULL;
391 static TreeInfo *bd_palette_atari_current = NULL;
392
393 static TreeInfo *bd_color_types = NULL;
394 static TreeInfo *bd_color_type_current = NULL;
395
396 static TreeInfo *scores_types = NULL;
397 static TreeInfo *scores_type_current = NULL;
398
399 static TreeInfo *game_speeds_normal = NULL;
400 static TreeInfo *game_speeds_extended = NULL;
401 static TreeInfo *game_speeds = NULL;
402 static TreeInfo *game_speed_current = NULL;
403
404 static TreeInfo *volumes_simple = NULL;
405 static TreeInfo *volume_simple_current = NULL;
406
407 static TreeInfo *volumes_loops = NULL;
408 static TreeInfo *volume_loops_current = NULL;
409
410 static TreeInfo *volumes_music = NULL;
411 static TreeInfo *volume_music_current = NULL;
412
413 static TreeInfo *touch_controls = NULL;
414 static TreeInfo *touch_control_current = NULL;
415
416 static TreeInfo *move_distances = NULL;
417 static TreeInfo *move_distance_current = NULL;
418
419 static TreeInfo *drop_distances = NULL;
420 static TreeInfo *drop_distance_current = NULL;
421
422 static TreeInfo *transparencies = NULL;
423 static TreeInfo *transparency_current = NULL;
424
425 static TreeInfo *grid_sizes[2][2] = { { NULL, NULL }, { NULL, NULL } };
426 static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } };
427
428 static TreeInfo *player_name = NULL;
429 static TreeInfo *player_name_current = NULL;
430
431 static TreeInfo *level_number = NULL;
432 static TreeInfo *level_number_current = NULL;
433
434 static TreeInfo *score_entries = NULL;
435 static TreeInfo *score_entry_current = NULL;
436
437 static struct ValueTextInfo window_sizes_list[] =
438 {
439   { 50,                                 "50 %"                          },
440   { 80,                                 "80 %"                          },
441   { 90,                                 "90 %"                          },
442   { 100,                                "100 % (Default)"               },
443   { 110,                                "110 %"                         },
444   { 120,                                "120 %"                         },
445   { 130,                                "130 %"                         },
446   { 140,                                "140 %"                         },
447   { 150,                                "150 %"                         },
448   { 200,                                "200 %"                         },
449   { 250,                                "250 %"                         },
450   { 300,                                "300 %"                         },
451
452   { -1,                                 NULL                            },
453 };
454
455 static struct StringValueTextInfo scaling_types_list[] =
456 {
457   { SCALING_QUALITY_NEAREST,             "Off"                          },
458   { SCALING_QUALITY_LINEAR,              "Linear"                       },
459   { SCALING_QUALITY_BEST,                "Anisotropic"                  },
460
461   { NULL,                                NULL                           },
462 };
463
464 static struct StringValueTextInfo rendering_modes_list[] =
465 {
466   { STR_SPECIAL_RENDERING_OFF,          "Off (May show artifacts, fast)"},
467   { STR_SPECIAL_RENDERING_BITMAP,       "Bitmap/Texture mode (slower)"  },
468 #if DEBUG
469   // this mode may work under certain conditions, but does not work on Windows
470   { STR_SPECIAL_RENDERING_TARGET,       "Target Texture mode (slower)"  },
471 #endif
472   { STR_SPECIAL_RENDERING_DOUBLE,       "Double Texture mode (slower)"  },
473
474   { NULL,                                NULL                           },
475 };
476
477 static struct StringValueTextInfo vsync_modes_list[] =
478 {
479   { STR_VSYNC_MODE_OFF,                 "Off"                           },
480   { STR_VSYNC_MODE_NORMAL,              "Normal"                        },
481   { STR_VSYNC_MODE_ADAPTIVE,            "Adaptive"                      },
482
483   { NULL,                               NULL                            },
484 };
485
486 static struct StringValueTextInfo scores_types_list[] =
487 {
488   { STR_SCORES_TYPE_LOCAL_ONLY,         "Local scores only"             },
489   { STR_SCORES_TYPE_SERVER_ONLY,        "Server scores only"            },
490   { STR_SCORES_TYPE_LOCAL_AND_SERVER,   "Local and server scores"       },
491
492   { NULL,                               NULL                            },
493 };
494
495 static struct ValueTextInfo game_speeds_list_normal[] =
496 {
497   { 30,                                 "Very Slow"                     },
498   { 25,                                 "Slow"                          },
499   { 20,                                 "Normal"                        },
500   { 15,                                 "Fast"                          },
501   { 10,                                 "Very Fast"                     },
502
503   { -1,                                 NULL                            },
504 };
505
506 static struct ValueTextInfo game_speeds_list_extended[] =
507 {
508   { 1000,                               "1 fps (Extremely Slow)"        },
509   { 500,                                "2 fps"                         },
510   { 200,                                "5 fps"                         },
511   { 100,                                "10 fps"                        },
512   { 50,                                 "20 fps"                        },
513   { 29,                                 "35 fps (Original Supaplex)"    },
514   { 25,                                 "40 fps"                        },
515   { 20,                                 "50 fps (=== Normal Speed ===)" },
516   { 16,                                 "60 fps (60 Hz VSync Speed)"    },
517   { 14,                                 "70 fps (Maximum Supaplex)"     },
518   { 10,                                 "100 fps"                       },
519   { 5,                                  "200 fps"                       },
520   { 2,                                  "500 fps"                       },
521   { 1,                                  "1000 fps (Extremely Fast)"     },
522
523   { -1,                                 NULL                            },
524 };
525
526 static struct ValueTextInfo *game_speeds_list;
527
528 static struct ValueTextInfo scroll_delays_list[] =
529 {
530   { 0,                                  "0 Tiles (No Scroll Delay)"     },
531   { 1,                                  "1 Tile"                        },
532   { 2,                                  "2 Tiles"                       },
533   { 3,                                  "3 Tiles (Default)"             },
534   { 4,                                  "4 Tiles"                       },
535   { 5,                                  "5 Tiles"                       },
536   { 6,                                  "6 Tiles"                       },
537   { 7,                                  "7 Tiles"                       },
538   { 8,                                  "8 Tiles (Maximum Scroll Delay)"},
539
540   { -1,                                 NULL                            },
541 };
542
543 static struct StringValueTextInfo snapshot_modes_list[] =
544 {
545   { STR_SNAPSHOT_MODE_OFF,              "Off"                           },
546   { STR_SNAPSHOT_MODE_EVERY_STEP,       "Every Step"                    },
547   { STR_SNAPSHOT_MODE_EVERY_MOVE,       "Every Move"                    },
548   { STR_SNAPSHOT_MODE_EVERY_COLLECT,    "Every Collect"                 },
549
550   { NULL,                               NULL                            },
551 };
552
553 static struct ValueTextInfo game_engine_types_list[] =
554 {
555   { GAME_ENGINE_TYPE_RND,               "Rocks'n'Diamonds"              },
556   { GAME_ENGINE_TYPE_BD,                "Boulder Dash"                  },
557   { GAME_ENGINE_TYPE_EM,                "Emerald Mine"                  },
558   { GAME_ENGINE_TYPE_SP,                "Supaplex"                      },
559   { GAME_ENGINE_TYPE_MM,                "Mirror Magic"                  },
560
561   { -1,                                 NULL                            }
562 };
563
564 static struct ValueTextInfo bd_palettes_c64_list[] =
565 {
566   { GD_PALETTE_C64_VICE_NEW,            "Vice new"                      },
567   { GD_PALETTE_C64_VICE_OLD,            "Vice old"                      },
568   { GD_PALETTE_C64_VIDE_DEFAULT,        "Vice default"                  },
569   { GD_PALETTE_C64_C64HQ,               "C64HQ"                         },
570   { GD_PALETTE_C64_C64S,                "C64S"                          },
571   { GD_PALETTE_C64_CCS64,               "CCS64"                         },
572   { GD_PALETTE_C64_FRODO,               "Frodo"                         },
573   { GD_PALETTE_C64_GODOT,               "GoDot"                         },
574   { GD_PALETTE_C64_PC64,                "PC64"                          },
575   { GD_PALETTE_C64_RTADASH,             "RTADash"                       },
576
577   { -1,                                 NULL                            },
578 };
579
580 static struct ValueTextInfo bd_palettes_c64dtv_list[] =
581 {
582   { GD_PALETTE_C64DTV_SPIFF,            "Spiff"                         },
583   { GD_PALETTE_C64DTV_MURRAY,           "Murray"                        },
584
585   { -1,                                 NULL                            },
586 };
587
588 static struct ValueTextInfo bd_palettes_atari_list[] =
589 {
590   { GD_PALETTE_ATARI_BUILTIN,           "BuiltIn"                       },
591   { GD_PALETTE_ATARI_BUILTIN_CONTRAST,  "BuiltIn contrast"              },
592   { GD_PALETTE_ATARI_DEFAULT,           "Default"                       },
593   { GD_PALETTE_ATARI_JAKUB,             "Jakub"                         },
594   { GD_PALETTE_ATARI_JAKUB_CONTRAST,    "Jakub contrast"                },
595   { GD_PALETTE_ATARI_REAL,              "Real"                          },
596   { GD_PALETTE_ATARI_REAL_CONTRAST,     "Real contrast"                 },
597   { GD_PALETTE_ATARI_XFORMER,           "XFormer"                       },
598
599   { -1,                                 NULL                            },
600 };
601
602 static struct ValueTextInfo bd_color_types_list[] =
603 {
604   { GD_COLOR_TYPE_RGB,                  "RGB colors"                    },
605   { GD_COLOR_TYPE_C64,                  "C64 colors"                    },
606   { GD_COLOR_TYPE_C64DTV,               "C64DTV colors"                 },
607   { GD_COLOR_TYPE_ATARI,                "Atari colors"                  },
608
609   { -1,                                 NULL                            },
610 };
611
612 static struct ValueTextInfo volumes_list[] =
613 {
614   { 0,                                  "0 %"                           },
615   { 1,                                  "1 %"                           },
616   { 2,                                  "2 %"                           },
617   { 5,                                  "5 %"                           },
618   { 10,                                 "10 %"                          },
619   { 20,                                 "20 %"                          },
620   { 30,                                 "30 %"                          },
621   { 40,                                 "40 %"                          },
622   { 50,                                 "50 %"                          },
623   { 60,                                 "60 %"                          },
624   { 70,                                 "70 %"                          },
625   { 80,                                 "80 %"                          },
626   { 90,                                 "90 %"                          },
627   { 100,                                "100 %"                         },
628
629   { -1,                                 NULL                            },
630 };
631
632 static struct StringValueTextInfo touch_controls_list[] =
633 {
634   { TOUCH_CONTROL_OFF,                  "Off"                           },
635   { TOUCH_CONTROL_VIRTUAL_BUTTONS,      "Virtual Buttons"               },
636   { TOUCH_CONTROL_WIPE_GESTURES,        "Wipe Gestures"                 },
637   { TOUCH_CONTROL_FOLLOW_FINGER,        "Follow Finger"                 },
638
639   { NULL,                               NULL                            },
640 };
641
642 static struct ValueTextInfo distances_list[] =
643 {
644   { 1,                                  "1 %"                           },
645   { 2,                                  "2 %"                           },
646   { 3,                                  "3 %"                           },
647   { 4,                                  "4 %"                           },
648   { 5,                                  "5 %"                           },
649   { 10,                                 "10 %"                          },
650   { 15,                                 "15 %"                          },
651   { 20,                                 "20 %"                          },
652   { 25,                                 "25 %"                          },
653
654   { -1,                                 NULL                            },
655 };
656
657 static struct ValueTextInfo transparencies_list[] =
658 {
659   { 0,                                  "0 % (Opaque)"                  },
660   { 10,                                 "10 %"                          },
661   { 20,                                 "20 %"                          },
662   { 30,                                 "30 %"                          },
663   { 40,                                 "40 %"                          },
664   { 50,                                 "50 %"                          },
665   { 60,                                 "60 %"                          },
666   { 70,                                 "70 %"                          },
667   { 80,                                 "80 %"                          },
668   { 90,                                 "90 %"                          },
669   { 100,                                "100 % (Invisible)"             },
670
671   { -1,                                 NULL                            },
672 };
673
674 static struct ValueTextInfo grid_sizes_list[] =
675 {
676   { 3,                                  "3"                             },
677   { 4,                                  "4"                             },
678   { 5,                                  "5"                             },
679   { 6,                                  "6"                             },
680   { 7,                                  "7"                             },
681   { 8,                                  "8"                             },
682   { 9,                                  "9"                             },
683   { 10,                                 "10"                            },
684   { 11,                                 "11"                            },
685   { 12,                                 "12"                            },
686   { 13,                                 "13"                            },
687   { 14,                                 "14"                            },
688   { 15,                                 "15"                            },
689   { 16,                                 "16"                            },
690   { 17,                                 "17"                            },
691   { 18,                                 "18"                            },
692   { 19,                                 "19"                            },
693   { 20,                                 "20"                            },
694   { 21,                                 "21"                            },
695   { 22,                                 "22"                            },
696   { 23,                                 "23"                            },
697   { 24,                                 "24"                            },
698   { 25,                                 "25"                            },
699   { 26,                                 "26"                            },
700   { 27,                                 "27"                            },
701   { 28,                                 "28"                            },
702   { 29,                                 "29"                            },
703   { 30,                                 "30"                            },
704   { 31,                                 "31"                            },
705   { 32,                                 "32"                            },
706
707   { -1,                                 NULL                            },
708 };
709
710 static int align_xoffset = 0;
711 static int align_yoffset = 0;
712
713 #define DRAW_MODE(s)            ((s) >= GAME_MODE_MAIN &&                       \
714                                  (s) <= GAME_MODE_SETUP ? (s) :                 \
715                                  (s) == GAME_MODE_PSEUDO_TYPENAME ?             \
716                                  GAME_MODE_MAIN :                               \
717                                  (s) == GAME_MODE_PSEUDO_TYPENAMES ?            \
718                                  GAME_MODE_NAMES : GAME_MODE_DEFAULT)
719
720 // (there are no draw offset definitions needed for INFO_MODE_TITLE)
721 #define DRAW_MODE_INFO(i)       ((i) >= INFO_MODE_TITLE &&                      \
722                                  (i) <= INFO_MODE_LEVELSET ? (i) :              \
723                                  INFO_MODE_MAIN)
724
725 #define DRAW_MODE_SETUP(i)      ((i) >= SETUP_MODE_MAIN &&                      \
726                                  (i) <= SETUP_MODE_SHORTCUTS_5 ? (i) :          \
727                                  (i) >= SETUP_MODE_CHOOSE_GRAPHICS &&           \
728                                  (i) <= SETUP_MODE_CHOOSE_MUSIC ?               \
729                                  SETUP_MODE_CHOOSE_ARTWORK :                    \
730                                  SETUP_MODE_CHOOSE_OTHER)
731
732 #define DRAW_XOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?          \
733                                  menu.draw_xoffset[GAME_MODE_INFO] :            \
734                                  menu.draw_xoffset_info[DRAW_MODE_INFO(i)])
735 #define DRAW_YOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?          \
736                                  menu.draw_yoffset[GAME_MODE_INFO] :            \
737                                  menu.draw_yoffset_info[DRAW_MODE_INFO(i)])
738 #define EXTRA_SPACING_INFO(i)   (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?          \
739                                  menu.extra_spacing[GAME_MODE_INFO] :           \
740                                  menu.extra_spacing_info[DRAW_MODE_INFO(i)])
741
742 #define DRAW_XOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ?        \
743                                  menu.draw_xoffset[GAME_MODE_SETUP] :           \
744                                  menu.draw_xoffset_setup[DRAW_MODE_SETUP(i)])
745 #define DRAW_YOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ?        \
746                                  menu.draw_yoffset[GAME_MODE_SETUP] :           \
747                                  menu.draw_yoffset_setup[DRAW_MODE_SETUP(i)])
748 #define EXTRA_SPACING_SETUP(i)  (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ?        \
749                                  menu.extra_spacing[GAME_MODE_SETUP] :          \
750                                  menu.extra_spacing_setup[DRAW_MODE_SETUP(i)])
751
752 #define EXTRA_SPACING_SCORES(i) (EXTRA_SPACING_INFO(i))
753
754 #define EXTRA_SPACING_SCOREINFO(i) (menu.extra_spacing[GAME_MODE_SCOREINFO])
755
756 #define DRAW_XOFFSET(s)         ((s) == GAME_MODE_INFO  ? DRAW_XOFFSET_INFO(info_mode) :        \
757                                  (s) == GAME_MODE_SETUP ? DRAW_XOFFSET_SETUP(setup_mode) :      \
758                                  menu.draw_xoffset[DRAW_MODE(s)])
759
760 #define DRAW_YOFFSET(s)         ((s) == GAME_MODE_INFO  ? DRAW_YOFFSET_INFO(info_mode) :        \
761                                  (s) == GAME_MODE_SETUP ? DRAW_YOFFSET_SETUP(setup_mode) :      \
762                                  menu.draw_yoffset[DRAW_MODE(s)])
763
764 #define EXTRA_SPACING(s)        ((s) == GAME_MODE_INFO   ? EXTRA_SPACING_INFO(info_mode) :      \
765                                  (s) == GAME_MODE_SETUP  ? EXTRA_SPACING_SETUP(setup_mode) :    \
766                                  (s) == GAME_MODE_SCORES ? EXTRA_SPACING_SCORES(info_mode) :    \
767                                  menu.extra_spacing[DRAW_MODE(s)])
768
769 #define mSX                     (SX + DRAW_XOFFSET(game_status))
770 #define mSY                     (SY + DRAW_YOFFSET(game_status))
771
772 #define amSX                    (mSX + align_xoffset)
773 #define amSY                    (mSY + align_yoffset)
774
775 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
776                                     menu.list_size[game_status] :       \
777                                     MAX_MENU_ENTRIES_ON_SCREEN)
778
779 #define IN_VIS_MENU(x, y)       IN_FIELD(x, y, SCR_FIELDX, NUM_MENU_ENTRIES_ON_SCREEN)
780
781
782 // title display and control definitions
783
784 #define MAX_NUM_TITLE_SCREENS   (2 * MAX_NUM_TITLE_IMAGES + 2 * MAX_NUM_TITLE_MESSAGES)
785
786 #define NO_DIRECT_LEVEL_SELECT  (-1)
787
788
789 static int num_title_screens = 0;
790
791 struct TitleControlInfo
792 {
793   boolean is_image;
794   boolean initial;
795   boolean first;
796   int local_nr;
797   int sort_priority;
798 };
799
800 struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS];
801
802
803 // main menu display and control definitions
804
805 #define MAIN_CONTROL_NAME                       0
806 #define MAIN_CONTROL_LEVELS                     1
807 #define MAIN_CONTROL_SCORES                     2
808 #define MAIN_CONTROL_EDITOR                     3
809 #define MAIN_CONTROL_INFO                       4
810 #define MAIN_CONTROL_GAME                       5
811 #define MAIN_CONTROL_SETUP                      6
812 #define MAIN_CONTROL_QUIT                       7
813 #define MAIN_CONTROL_PREV_LEVEL                 8
814 #define MAIN_CONTROL_NEXT_LEVEL                 9
815 #define MAIN_CONTROL_FIRST_LEVEL                10
816 #define MAIN_CONTROL_LAST_LEVEL                 11
817 #define MAIN_CONTROL_LEVEL_NUMBER               12
818 #define MAIN_CONTROL_LEVEL_INFO_1               13
819 #define MAIN_CONTROL_LEVEL_INFO_2               14
820 #define MAIN_CONTROL_LEVEL_NAME                 15
821 #define MAIN_CONTROL_LEVEL_AUTHOR               16
822 #define MAIN_CONTROL_LEVEL_YEAR                 17
823 #define MAIN_CONTROL_LEVEL_IMPORTED_FROM        18
824 #define MAIN_CONTROL_LEVEL_IMPORTED_BY          19
825 #define MAIN_CONTROL_LEVEL_TESTED_BY            20
826 #define MAIN_CONTROL_TITLE_1                    21
827 #define MAIN_CONTROL_TITLE_2                    22
828 #define MAIN_CONTROL_TITLE_3                    23
829
830 static char str_main_text_name[10];
831 static char str_main_text_first_level[10];
832 static char str_main_text_last_level[10];
833 static char str_main_text_level_number[10];
834
835 static char network_server_hostname[MAX_SETUP_TEXT_INPUT_LEN + 1];
836
837 static char *main_text_name                     = str_main_text_name;
838 static char *main_text_first_level              = str_main_text_first_level;
839 static char *main_text_last_level               = str_main_text_last_level;
840 static char *main_text_level_number             = str_main_text_level_number;
841 static char *main_text_levels                   = "Levelset";
842 static char *main_text_scores                   = "Hall Of Fame";
843 static char *main_text_editor                   = "Level Creator";
844 static char *main_text_info                     = "Info Screen";
845 static char *main_text_game                     = "Start Game";
846 static char *main_text_setup                    = "Setup";
847 static char *main_text_quit                     = "Quit";
848 static char *main_text_level_name               = level.name;
849 static char *main_text_level_author             = level.author;
850 static char *main_text_level_year               = NULL;
851 static char *main_text_level_imported_from      = NULL;
852 static char *main_text_level_imported_by        = NULL;
853 static char *main_text_level_tested_by          = NULL;
854 static char *main_text_title_1                  = NULL;
855 static char *main_text_title_2                  = NULL;
856 static char *main_text_title_3                  = NULL;
857
858 extern char debug_xsn_mode[];
859
860 struct MainControlInfo
861 {
862   int nr;
863
864   struct MenuPosInfo *pos_button;
865   int button_graphic;
866
867   struct TextPosInfo *pos_text;
868   char **text;
869
870   struct TextPosInfo *pos_input;
871   char **input;
872 };
873
874 static struct MainControlInfo main_controls[] =
875 {
876   {
877     MAIN_CONTROL_NAME,
878     &menu.main.button.name,             IMG_MENU_BUTTON_NAME,
879     &menu.main.text.name,               &main_text_name,
880     &menu.main.input.name,              &setup.player_name,
881   },
882   {
883     MAIN_CONTROL_LEVELS,
884     &menu.main.button.levels,           IMG_MENU_BUTTON_LEVELS,
885     &menu.main.text.levels,             &main_text_levels,
886     NULL,                               NULL,
887   },
888   {
889     MAIN_CONTROL_SCORES,
890     &menu.main.button.scores,           IMG_MENU_BUTTON_SCORES,
891     &menu.main.text.scores,             &main_text_scores,
892     NULL,                               NULL,
893   },
894   {
895     MAIN_CONTROL_EDITOR,
896     &menu.main.button.editor,           IMG_MENU_BUTTON_EDITOR,
897     &menu.main.text.editor,             &main_text_editor,
898     NULL,                               NULL,
899   },
900   {
901     MAIN_CONTROL_INFO,
902     &menu.main.button.info,             IMG_MENU_BUTTON_INFO,
903     &menu.main.text.info,               &main_text_info,
904     NULL,                               NULL,
905   },
906   {
907     MAIN_CONTROL_GAME,
908     &menu.main.button.game,             IMG_MENU_BUTTON_GAME,
909     &menu.main.text.game,               &main_text_game,
910     NULL,                               NULL,
911   },
912   {
913     MAIN_CONTROL_SETUP,
914     &menu.main.button.setup,            IMG_MENU_BUTTON_SETUP,
915     &menu.main.text.setup,              &main_text_setup,
916     NULL,                               NULL,
917   },
918   {
919     MAIN_CONTROL_QUIT,
920     &menu.main.button.quit,             IMG_MENU_BUTTON_QUIT,
921     &menu.main.text.quit,               &main_text_quit,
922     NULL,                               NULL,
923   },
924   {
925     MAIN_CONTROL_PREV_LEVEL,
926     NULL,                               -1,
927     NULL,                               NULL,
928     NULL,                               NULL,
929   },
930   {
931     MAIN_CONTROL_NEXT_LEVEL,
932     NULL,                               -1,
933     NULL,                               NULL,
934     NULL,                               NULL,
935   },
936   {
937     MAIN_CONTROL_FIRST_LEVEL,
938     NULL,                               -1,
939     &menu.main.text.first_level,        &main_text_first_level,
940     NULL,                               NULL,
941   },
942   {
943     MAIN_CONTROL_LAST_LEVEL,
944     NULL,                               -1,
945     &menu.main.text.last_level,         &main_text_last_level,
946     NULL,                               NULL,
947   },
948   {
949     MAIN_CONTROL_LEVEL_NUMBER,
950     NULL,                               -1,
951     &menu.main.text.level_number,       &main_text_level_number,
952     NULL,                               NULL,
953   },
954   {
955     MAIN_CONTROL_LEVEL_INFO_1,
956     NULL,                               -1,
957     &menu.main.text.level_info_1,       NULL,
958     NULL,                               NULL,
959   },
960   {
961     MAIN_CONTROL_LEVEL_INFO_2,
962     NULL,                               -1,
963     &menu.main.text.level_info_2,       NULL,
964     NULL,                               NULL,
965   },
966   {
967     MAIN_CONTROL_LEVEL_NAME,
968     NULL,                               -1,
969     &menu.main.text.level_name,         &main_text_level_name,
970     NULL,                               NULL,
971   },
972   {
973     MAIN_CONTROL_LEVEL_AUTHOR,
974     NULL,                               -1,
975     &menu.main.text.level_author,       &main_text_level_author,
976     NULL,                               NULL,
977   },
978   {
979     MAIN_CONTROL_LEVEL_YEAR,
980     NULL,                               -1,
981     &menu.main.text.level_year,         &main_text_level_year,
982     NULL,                               NULL,
983   },
984   {
985     MAIN_CONTROL_LEVEL_IMPORTED_FROM,
986     NULL,                               -1,
987     &menu.main.text.level_imported_from, &main_text_level_imported_from,
988     NULL,                               NULL,
989   },
990   {
991     MAIN_CONTROL_LEVEL_IMPORTED_BY,
992     NULL,                               -1,
993     &menu.main.text.level_imported_by,  &main_text_level_imported_by,
994     NULL,                               NULL,
995   },
996   {
997     MAIN_CONTROL_LEVEL_TESTED_BY,
998     NULL,                               -1,
999     &menu.main.text.level_tested_by,    &main_text_level_tested_by,
1000     NULL,                               NULL,
1001   },
1002   {
1003     MAIN_CONTROL_TITLE_1,
1004     NULL,                               -1,
1005     &menu.main.text.title_1,            &main_text_title_1,
1006     NULL,                               NULL,
1007   },
1008   {
1009     MAIN_CONTROL_TITLE_2,
1010     NULL,                               -1,
1011     &menu.main.text.title_2,            &main_text_title_2,
1012     NULL,                               NULL,
1013   },
1014   {
1015     MAIN_CONTROL_TITLE_3,
1016     NULL,                               -1,
1017     &menu.main.text.title_3,            &main_text_title_3,
1018     NULL,                               NULL,
1019   },
1020
1021   {
1022     -1,
1023     NULL,                               -1,
1024     NULL,                               NULL,
1025     NULL,                               NULL,
1026   }
1027 };
1028
1029
1030 static boolean hasLevelSetInfo(void)
1031 {
1032   return (getLevelSetInfoFilename(0) != NULL);
1033 }
1034
1035 static int getTitleScreenGraphic(int nr, boolean initial)
1036 {
1037   return (initial ? IMG_TITLESCREEN_INITIAL_1 : IMG_TITLESCREEN_1) + nr;
1038 }
1039
1040 static struct TitleMessageInfo *getTitleMessageInfo(int nr, boolean initial)
1041 {
1042   return (initial ? &titlemessage_initial[nr] : &titlemessage[nr]);
1043 }
1044
1045 #if 0
1046 static int getTitleScreenGameMode(boolean initial)
1047 {
1048   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
1049 }
1050 #endif
1051
1052 static int getTitleMessageGameMode(boolean initial)
1053 {
1054   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
1055 }
1056
1057 static int getTitleAnimMode(struct TitleControlInfo *tci)
1058 {
1059   int base = (tci->initial ? GAME_MODE_TITLE_INITIAL_1 : GAME_MODE_TITLE_1);
1060
1061   return base + tci->local_nr;
1062 }
1063
1064 #if 0
1065 static int getTitleScreenBackground(boolean initial)
1066 {
1067   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
1068 }
1069 #endif
1070
1071 #if 0
1072 static int getTitleMessageBackground(int nr, boolean initial)
1073 {
1074   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
1075 }
1076 #endif
1077
1078 static int getTitleBackground(int nr, boolean initial, boolean is_image)
1079 {
1080   int base = (is_image ?
1081               (initial ? IMG_BACKGROUND_TITLESCREEN_INITIAL_1 :
1082                          IMG_BACKGROUND_TITLESCREEN_1) :
1083               (initial ? IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
1084                          IMG_BACKGROUND_TITLEMESSAGE_1));
1085   int graphic_global = (initial ? IMG_BACKGROUND_TITLE_INITIAL :
1086                                   IMG_BACKGROUND_TITLE);
1087   int graphic_local = base + nr;
1088
1089   if (graphic_info[graphic_local].bitmap != NULL)
1090     return graphic_local;
1091
1092   if (graphic_info[graphic_global].bitmap != NULL)
1093     return graphic_global;
1094
1095   return IMG_UNDEFINED;
1096 }
1097
1098 static int getTitleSound(struct TitleControlInfo *tci)
1099 {
1100   boolean is_image = tci->is_image;
1101   int initial = tci->initial;
1102   int nr = tci->local_nr;
1103   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
1104   int base = (is_image ?
1105               (initial ? SND_BACKGROUND_TITLESCREEN_INITIAL_1 :
1106                          SND_BACKGROUND_TITLESCREEN_1) :
1107               (initial ? SND_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
1108                          SND_BACKGROUND_TITLEMESSAGE_1));
1109   int sound_global = menu.sound[mode];
1110   int sound_local = base + nr;
1111
1112 #if 0
1113   Debug("screens:getTitleSound", "%d, %d, %d: %d ['%s'], %d ['%s']",
1114         nr, initial, is_image,
1115         sound_global, getSoundListEntry(sound_global)->filename,
1116         sound_local, getSoundListEntry(sound_local)->filename);
1117 #endif
1118
1119   if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME))
1120     return sound_local;
1121
1122   if (!strEqual(getSoundListEntry(sound_global)->filename, UNDEFINED_FILENAME))
1123     return sound_global;
1124
1125   return SND_UNDEFINED;
1126 }
1127
1128 static int getTitleMusic(struct TitleControlInfo *tci)
1129 {
1130   boolean is_image = tci->is_image;
1131   int initial = tci->initial;
1132   int nr = tci->local_nr;
1133   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
1134   int base = (is_image ?
1135               (initial ? MUS_BACKGROUND_TITLESCREEN_INITIAL_1 :
1136                          MUS_BACKGROUND_TITLESCREEN_1) :
1137               (initial ? MUS_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
1138                          MUS_BACKGROUND_TITLEMESSAGE_1));
1139   int music_global = menu.music[mode];
1140   int music_local = base + nr;
1141
1142 #if 0
1143   Debug("screens:getTitleMusic", "%d, %d, %d: %d ['%s'], %d ['%s']",
1144         nr, initial, is_image,
1145         music_global, getMusicListEntry(music_global)->filename,
1146         music_local, getMusicListEntry(music_local)->filename);
1147 #endif
1148
1149   if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME))
1150     return music_local;
1151
1152   if (!strEqual(getMusicListEntry(music_global)->filename, UNDEFINED_FILENAME))
1153     return music_global;
1154
1155   return MUS_UNDEFINED;
1156 }
1157
1158 static struct TitleFadingInfo getTitleFading(struct TitleControlInfo *tci)
1159 {
1160   boolean is_image = tci->is_image;
1161   boolean initial = tci->initial;
1162   boolean first = tci->first;
1163   int nr = tci->local_nr;
1164   struct TitleMessageInfo tmi;
1165   struct TitleFadingInfo ti;
1166
1167   tmi = (is_image ? (initial ? (first ?
1168                                 titlescreen_initial_first[nr] :
1169                                 titlescreen_initial[nr])
1170                              : (first ?
1171                                 titlescreen_first[nr] :
1172                                 titlescreen[nr]))
1173                   : (initial ? (first ?
1174                                 titlemessage_initial_first[nr] :
1175                                 titlemessage_initial[nr])
1176                              : (first ?
1177                                 titlemessage_first[nr] :
1178                                 titlemessage[nr])));
1179
1180   ti.fade_mode  = tmi.fade_mode;
1181   ti.fade_delay = tmi.fade_delay;
1182   ti.post_delay = tmi.post_delay;
1183   ti.auto_delay = tmi.auto_delay;
1184   ti.auto_delay_unit = tmi.auto_delay_unit;
1185
1186   return ti;
1187 }
1188
1189 static int compareTitleControlInfo(const void *object1, const void *object2)
1190 {
1191   const struct TitleControlInfo *tci1 = (struct TitleControlInfo *)object1;
1192   const struct TitleControlInfo *tci2 = (struct TitleControlInfo *)object2;
1193   int compare_result;
1194
1195   if (tci1->initial != tci2->initial)
1196     compare_result = (tci1->initial ? -1 : +1);
1197   else if (tci1->sort_priority != tci2->sort_priority)
1198     compare_result = tci1->sort_priority - tci2->sort_priority;
1199   else if (tci1->is_image != tci2->is_image)
1200     compare_result = (tci1->is_image ? -1 : +1);
1201   else
1202     compare_result = tci1->local_nr - tci2->local_nr;
1203
1204   return compare_result;
1205 }
1206
1207 static boolean CheckTitleScreen_BD(int nr, boolean initial)
1208 {
1209   // only show BD style title screen for native BD level sets
1210   if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
1211     return FALSE;
1212
1213   // only show BD style title screen for first title screen of a level set
1214   if (initial || nr != 0)
1215     return FALSE;
1216
1217   int graphic = getTitleScreenGraphic(nr, initial);
1218   Bitmap *bitmap = graphic_info[graphic].bitmap;
1219
1220   // only show BD style title screen if no other title screen defined
1221   if (bitmap != NULL)
1222     return FALSE;
1223
1224   // check if BD style title screen defined
1225   return (GetTitleScreenBitmaps_BD() != NULL);
1226 }
1227
1228 static void InitializeTitleControlsExt_AddTitleInfo(boolean is_image,
1229                                                     boolean initial,
1230                                                     int nr, int sort_priority)
1231 {
1232   title_controls[num_title_screens].is_image = is_image;
1233   title_controls[num_title_screens].initial = initial;
1234   title_controls[num_title_screens].local_nr = nr;
1235   title_controls[num_title_screens].sort_priority = sort_priority;
1236
1237   title_controls[num_title_screens].first = FALSE;      // will be set later
1238
1239   num_title_screens++;
1240 }
1241
1242 static void InitializeTitleControls_CheckTitleInfo(boolean initial)
1243 {
1244   int i;
1245
1246   for (i = 0; i < MAX_NUM_TITLE_IMAGES; i++)
1247   {
1248     int graphic = getTitleScreenGraphic(i, initial);
1249     Bitmap *bitmap = graphic_info[graphic].bitmap;
1250     int sort_priority = graphic_info[graphic].sort_priority;
1251     boolean has_title_screen = (bitmap != NULL);
1252
1253     // check for optional title screen of native BD style level set
1254     if (CheckTitleScreen_BD(i, initial))
1255       has_title_screen = TRUE;
1256
1257     if (has_title_screen)
1258       InitializeTitleControlsExt_AddTitleInfo(TRUE, initial, i, sort_priority);
1259   }
1260
1261   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
1262   {
1263     struct TitleMessageInfo *tmi = getTitleMessageInfo(i, initial);
1264     char *filename = getLevelSetTitleMessageFilename(i, initial);
1265     int sort_priority = tmi->sort_priority;
1266
1267     if (filename != NULL)
1268       InitializeTitleControlsExt_AddTitleInfo(FALSE, initial, i, sort_priority);
1269   }
1270 }
1271
1272 static void InitializeTitleControls(boolean show_title_initial)
1273 {
1274   num_title_screens = 0;
1275
1276   // 1st step: initialize title screens for game start (only when starting)
1277   if (show_title_initial)
1278     InitializeTitleControls_CheckTitleInfo(TRUE);
1279
1280   // 2nd step: initialize title screens for current level set
1281   InitializeTitleControls_CheckTitleInfo(FALSE);
1282
1283   // sort title screens according to sort_priority and title number
1284   qsort(title_controls, num_title_screens, sizeof(struct TitleControlInfo),
1285         compareTitleControlInfo);
1286
1287   // mark first title screen
1288   title_controls[0].first = TRUE;
1289 }
1290
1291 static boolean visibleMenuPos(struct MenuPosInfo *pos)
1292 {
1293   return (pos != NULL && pos->x != -1 && pos->y != -1);
1294 }
1295
1296 static boolean visibleTextPos(struct TextPosInfo *pos)
1297 {
1298   return (pos != NULL && pos->x != -1 && pos->y != -1);
1299 }
1300
1301 static void InitializeMainControls(void)
1302 {
1303   boolean local_team_mode = (!network.enabled && setup.team_mode);
1304   int i;
1305
1306   // set main control text values to dynamically determined values
1307   sprintf(main_text_name,         "%s",   local_team_mode ? "Team:" : "Name:");
1308
1309   strcpy(main_text_first_level,  int2str(leveldir_current->first_level,
1310                                          menu.main.text.first_level.size));
1311   strcpy(main_text_last_level,   int2str(leveldir_current->last_level,
1312                                          menu.main.text.last_level.size));
1313   strcpy(main_text_level_number, int2str(level_nr,
1314                                          menu.main.text.level_number.size));
1315
1316   main_text_level_year          = leveldir_current->year;
1317   main_text_level_imported_from = leveldir_current->imported_from;
1318   main_text_level_imported_by   = leveldir_current->imported_by;
1319   main_text_level_tested_by     = leveldir_current->tested_by;
1320
1321   main_text_title_1 = getConfigProgramTitleString();
1322   main_text_title_2 = getConfigProgramCopyrightString();
1323   main_text_title_3 = getConfigProgramCompanyString();
1324
1325   // set main control screen positions to dynamically determined values
1326   for (i = 0; main_controls[i].nr != -1; i++)
1327   {
1328     struct MainControlInfo *mci = &main_controls[i];
1329     int nr                         = mci->nr;
1330     struct MenuPosInfo *pos_button = mci->pos_button;
1331     struct TextPosInfo *pos_text   = mci->pos_text;
1332     struct TextPosInfo *pos_input  = mci->pos_input;
1333     char *text                     = (mci->text  ? *mci->text  : NULL);
1334     char *input                    = (mci->input ? *mci->input : NULL);
1335     int button_graphic             = mci->button_graphic;
1336     int font_text                  = (pos_text  ? pos_text->font  : -1);
1337     int font_input                 = (pos_input ? pos_input->font : -1);
1338
1339     int font_text_width   = (font_text  != -1 ? getFontWidth(font_text)   : 0);
1340     int font_text_height  = (font_text  != -1 ? getFontHeight(font_text)  : 0);
1341     int font_input_width  = (font_input != -1 ? getFontWidth(font_input)  : 0);
1342     int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0);
1343     int text_chars  = (text  != NULL ? strlen(text)  : 0);
1344     int input_chars = (input != NULL ? strlen(input) : 0);
1345
1346     int button_width =
1347       (button_graphic != -1 ? graphic_info[button_graphic].width  : 0);
1348     int button_height =
1349       (button_graphic != -1 ? graphic_info[button_graphic].height : 0);
1350     int text_width   = font_text_width * text_chars;
1351     int text_height  = font_text_height;
1352     int input_width  = font_input_width * input_chars;
1353     int input_height = font_input_height;
1354
1355     if (nr == MAIN_CONTROL_NAME)
1356     {
1357       menu.main.input.name.width  = input_width;
1358       menu.main.input.name.height = input_height;
1359     }
1360
1361     if (pos_button != NULL)             // (x/y may be -1/-1 here)
1362     {
1363       pos_button->width  = button_width;
1364       pos_button->height = button_height;
1365     }
1366
1367     if (pos_text != NULL)               // (x/y may be -1/-1 here)
1368     {
1369       // calculate text size -- needed for text alignment
1370       boolean calculate_text_size = (text != NULL);
1371
1372       if (pos_text->width == -1 || calculate_text_size)
1373         pos_text->width = text_width;
1374       if (pos_text->height == -1 || calculate_text_size)
1375         pos_text->height = text_height;
1376
1377       if (visibleMenuPos(pos_button))
1378       {
1379         if (pos_text->x == -1)
1380           pos_text->x = pos_button->x + pos_button->width;
1381         if (pos_text->y == -1)
1382           pos_text->y =
1383             pos_button->y + (pos_button->height - pos_text->height) / 2;
1384       }
1385     }
1386
1387     if (pos_input != NULL)              // (x/y may be -1/-1 here)
1388     {
1389       if (visibleTextPos(pos_text))
1390       {
1391         if (pos_input->x == -1)
1392           pos_input->x = pos_text->x + pos_text->width;
1393         if (pos_input->y == -1)
1394           pos_input->y = pos_text->y;
1395       }
1396
1397       if (pos_input->width == -1)
1398         pos_input->width = input_width;
1399       if (pos_input->height == -1)
1400         pos_input->height = input_height;
1401     }
1402   }
1403 }
1404
1405 static void DrawPressedGraphicThruMask(int dst_x, int dst_y,
1406                                        int graphic, boolean pressed)
1407 {
1408   struct GraphicInfo *g = &graphic_info[graphic];
1409   Bitmap *src_bitmap;
1410   int src_x, src_y;
1411   int xoffset = (pressed ? g->pressed_xoffset : 0);
1412   int yoffset = (pressed ? g->pressed_yoffset : 0);
1413
1414   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1415
1416   BlitBitmapMasked(src_bitmap, drawto, src_x + xoffset, src_y + yoffset,
1417                    g->width, g->height, dst_x, dst_y);
1418 }
1419
1420 static void DrawCursorAndText_Main_Ext(int nr, boolean active_text,
1421                                        boolean active_input,
1422                                        boolean pressed_button)
1423 {
1424   int i;
1425
1426   for (i = 0; main_controls[i].nr != -1; i++)
1427   {
1428     struct MainControlInfo *mci = &main_controls[i];
1429
1430     if (mci->nr == nr || nr == -1)
1431     {
1432       struct MenuPosInfo *pos_button = mci->pos_button;
1433       struct TextPosInfo *pos_text   = mci->pos_text;
1434       struct TextPosInfo *pos_input  = mci->pos_input;
1435       char *text                     = (mci->text  ? *mci->text  : NULL);
1436       char *input                    = (mci->input ? *mci->input : NULL);
1437       int button_graphic             = mci->button_graphic;
1438       int font_text                  = (pos_text  ? pos_text->font  : -1);
1439       int font_input                 = (pos_input ? pos_input->font : -1);
1440
1441       if (active_text)
1442       {
1443         button_graphic = BUTTON_ACTIVE(button_graphic);
1444         font_text = FONT_ACTIVE(font_text);
1445       }
1446
1447       if (active_input)
1448       {
1449         font_input = FONT_ACTIVE(font_input);
1450       }
1451
1452       if (visibleMenuPos(pos_button))
1453       {
1454         struct MenuPosInfo *pos = pos_button;
1455         int x = mSX + pos->x;
1456         int y = mSY + pos->y;
1457
1458         DrawBackgroundForGraphic(x, y, pos->width, pos->height, button_graphic);
1459         DrawPressedGraphicThruMask(x, y, button_graphic, pressed_button);
1460       }
1461
1462       if (visibleTextPos(pos_text) && text != NULL)
1463       {
1464         struct TextPosInfo *pos = pos_text;
1465         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1466         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1467
1468 #if 1
1469         // (check why/if this is needed)
1470         DrawBackgroundForFont(x, y, pos->width, pos->height, font_text);
1471 #endif
1472         DrawText(x, y, text, font_text);
1473       }
1474
1475       if (visibleTextPos(pos_input) && input != NULL)
1476       {
1477         struct TextPosInfo *pos = pos_input;
1478         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1479         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1480
1481 #if 1
1482         // (check why/if this is needed)
1483         DrawBackgroundForFont(x, y, pos->width, pos->height, font_input);
1484 #endif
1485         DrawText(x, y, input, font_input);
1486       }
1487     }
1488   }
1489 }
1490
1491 static void DrawCursorAndText_Main(int nr, boolean active_text,
1492                                    boolean pressed_button)
1493 {
1494   DrawCursorAndText_Main_Ext(nr, active_text, FALSE, pressed_button);
1495 }
1496
1497 #if 0
1498 static void DrawCursorAndText_Main_Input(int nr, boolean active_text,
1499                                          boolean pressed_button)
1500 {
1501   DrawCursorAndText_Main_Ext(nr, active_text, TRUE, pressed_button);
1502 }
1503 #endif
1504
1505 static struct MainControlInfo *getMainControlInfo(int nr)
1506 {
1507   int i;
1508
1509   for (i = 0; main_controls[i].nr != -1; i++)
1510     if (main_controls[i].nr == nr)
1511       return &main_controls[i];
1512
1513   return NULL;
1514 }
1515
1516 static boolean insideMenuPosRect(struct MenuPosInfo *rect, int x, int y)
1517 {
1518   if (rect == NULL)
1519     return FALSE;
1520
1521   int rect_x = ALIGNED_TEXT_XPOS(rect);
1522   int rect_y = ALIGNED_TEXT_YPOS(rect);
1523
1524   return (x >= rect_x && x < rect_x + rect->width &&
1525           y >= rect_y && y < rect_y + rect->height);
1526 }
1527
1528 static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
1529 {
1530   if (rect == NULL)
1531     return FALSE;
1532
1533   int rect_x = ALIGNED_TEXT_XPOS(rect);
1534   int rect_y = ALIGNED_TEXT_YPOS(rect);
1535
1536 #if 0
1537   Debug("screens:insideTextPosRect",
1538         "(%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d",
1539         x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
1540         (x >= rect_x && x < rect_x + rect->width &&
1541          y >= rect_y && y < rect_y + rect->height));
1542 #endif
1543
1544   return (x >= rect_x && x < rect_x + rect->width &&
1545           y >= rect_y && y < rect_y + rect->height);
1546 }
1547
1548 static boolean insidePreviewRect(struct PreviewInfo *preview, int x, int y)
1549 {
1550   int rect_width  = preview->xsize * preview->tile_size;
1551   int rect_height = preview->ysize * preview->tile_size;
1552   int rect_x = ALIGNED_XPOS(preview->x, rect_width,  preview->align);
1553   int rect_y = ALIGNED_YPOS(preview->y, rect_height, preview->valign);
1554
1555   return (x >= rect_x && x < rect_x + rect_width &&
1556           y >= rect_y && y < rect_y + rect_height);
1557 }
1558
1559 static void AdjustScrollbar(int id, int items_max, int items_visible,
1560                             int item_position)
1561 {
1562   struct GadgetInfo *gi = screen_gadget[id];
1563
1564   if (item_position > items_max - items_visible)
1565     item_position = items_max - items_visible;
1566
1567   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
1568                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
1569                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
1570 }
1571
1572 static void AdjustChooseTreeScrollbar(TreeInfo *ti, int id)
1573 {
1574   AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN,
1575                   ti->cl_first);
1576 }
1577
1578 static void clearMenuListArea(void)
1579 {
1580   int scrollbar_xpos = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
1581
1582   // correct scrollbar position if placed outside menu (playfield) area
1583   if (scrollbar_xpos > SX + SC_SCROLLBAR_XPOS)
1584     scrollbar_xpos = SX + SC_SCROLLBAR_XPOS;
1585
1586   // clear menu list area, but not title or scrollbar
1587   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
1588                  scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32);
1589
1590   // special compatibility handling for "Snake Bite" graphics set
1591   if (strPrefix(leveldir_current->identifier, "snake_bite"))
1592     ClearRectangle(drawto, mSX, mSY + MENU_SCREEN_START_YPOS * 32,
1593                    scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32);
1594 }
1595
1596 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
1597 {
1598   static int cursor_array[MAX_LEV_FIELDY];
1599   int x = amSX + TILEX * xpos;
1600   int y = amSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
1601
1602   if (xpos == 0)
1603   {
1604     if (graphic != -1)
1605       cursor_array[ypos] = graphic;
1606     else
1607       graphic = cursor_array[ypos];
1608   }
1609
1610   if (active)
1611     graphic = BUTTON_ACTIVE(graphic);
1612
1613   DrawBackgroundForGraphic(x, y, TILEX, TILEY, graphic);
1614   DrawFixedGraphicThruMaskExt(drawto, x, y, graphic, 0);
1615 }
1616
1617 static void initCursor(int ypos, int graphic)
1618 {
1619   drawCursorExt(0, ypos, FALSE, graphic);
1620 }
1621
1622 static void drawCursor(int ypos, boolean active)
1623 {
1624   drawCursorExt(0, ypos, active, -1);
1625 }
1626
1627 static void drawCursorXY(int xpos, int ypos, int graphic)
1628 {
1629   drawCursorExt(xpos, ypos, FALSE, graphic);
1630 }
1631
1632 static void drawChooseTreeCursor(int ypos, boolean active)
1633 {
1634   drawCursorExt(0, ypos, active, -1);
1635 }
1636
1637 static int getChooseTreeEditFont(boolean active)
1638 {
1639   return (active ? FONT_MENU_2_ACTIVE : FONT_MENU_2);
1640 }
1641
1642 static int getChooseTreeEditXPos(int pos)
1643 {
1644   boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
1645   int xoffset = (has_scrollbar ? -1 : 0);
1646   int xpos = MENU_SCREEN_TEXT2_XPOS + xoffset;
1647   int sx = amSX + xpos * TILEX;
1648   int font_nr = getChooseTreeEditFont(FALSE);
1649   int width = getTextWidth(STR_CHOOSE_TREE_EDIT, font_nr);
1650
1651   return (pos == POS_RIGHT ? sx + width - 1 : sx);
1652 }
1653
1654 static int getChooseTreeEditYPos(int ypos_raw)
1655 {
1656   int ypos = MENU_SCREEN_START_YPOS + ypos_raw;
1657   int sy = amSY + ypos * TILEY;
1658
1659   return sy;
1660 }
1661
1662 static int getChooseTreeEditXPosReal(int pos)
1663 {
1664   int xpos = getChooseTreeEditXPos(pos);
1665   int font_nr = getChooseTreeEditFont(FALSE);
1666   int font_xoffset = getFontDrawOffsetX(font_nr);
1667
1668   return xpos + font_xoffset;
1669 }
1670
1671 static void drawChooseTreeEdit(int ypos_raw, boolean active)
1672 {
1673   int sx = getChooseTreeEditXPos(POS_LEFT);
1674   int sy = getChooseTreeEditYPos(ypos_raw);
1675   int font_nr = getChooseTreeEditFont(active);
1676
1677   DrawText(sx, sy, STR_CHOOSE_TREE_EDIT, font_nr);
1678 }
1679
1680 static void DrawInfoScreen_Headline(int screen_nr, int num_screens,
1681                                     int use_global_screens)
1682 {
1683   char *info_text_title_1 = getInfoScreenTitle_Generic();
1684   char info_text_title_2[MAX_LINE_LEN + 1];
1685
1686   if (num_screens > 1)
1687   {
1688     sprintf(info_text_title_2, "Page %d of %d", screen_nr + 1, num_screens);
1689   }
1690   else
1691   {
1692     char *text_format = (use_global_screens ? "for %s" : "for \"%s\"");
1693     int text_format_len = strlen(text_format) - strlen("%s");
1694     int max_text_len = SXSIZE / getFontWidth(FONT_TITLE_2);
1695     int max_name_len = max_text_len - text_format_len;
1696     char name_cut[max_name_len];
1697     char *name_full = (use_global_screens ? getProgramTitleString() :
1698                        leveldir_current->name);
1699
1700     snprintf(name_cut, max_name_len, "%s", name_full);
1701     snprintf(info_text_title_2, max_text_len, text_format, name_cut);
1702   }
1703
1704   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, info_text_title_1);
1705   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, info_text_title_2);
1706 }
1707
1708 static void DrawTitleScreenImage(int nr, boolean initial)
1709 {
1710   static int frame_counter = 0;
1711   int graphic = getTitleScreenGraphic(nr, initial);
1712   Bitmap *bitmap = graphic_info[graphic].bitmap;
1713   Bitmap *bitmap_background = NULL;
1714   int draw_masked = graphic_info[graphic].draw_masked;
1715   int width  = graphic_info[graphic].width;
1716   int height = graphic_info[graphic].height;
1717   int src_x = graphic_info[graphic].src_x;
1718   int src_y = graphic_info[graphic].src_y;
1719   int dst_x, dst_y;
1720
1721   // check for optional title screen of native BD style level set
1722   if (CheckTitleScreen_BD(nr, initial))
1723   {
1724     Bitmap **title_screen_bitmaps = GetTitleScreenBitmaps_BD();
1725
1726     bitmap            = title_screen_bitmaps[0];
1727     bitmap_background = title_screen_bitmaps[1];
1728
1729     if (bitmap != NULL)
1730     {
1731       width  = bitmap->width;
1732       height = bitmap->height;
1733       src_x = 0;
1734       src_y = 0;
1735     }
1736   }
1737
1738   if (bitmap == NULL)
1739     return;
1740
1741   if (width > WIN_XSIZE)
1742   {
1743     // image width too large for window => center image horizontally
1744     src_x = (width - WIN_XSIZE) / 2;
1745     width = WIN_XSIZE;
1746   }
1747
1748   if (height > WIN_YSIZE)
1749   {
1750     // image height too large for window => center image vertically
1751     src_y = (height - WIN_YSIZE) / 2;
1752     height = WIN_YSIZE;
1753   }
1754
1755   // always display title screens centered
1756   dst_x = (WIN_XSIZE - width) / 2;
1757   dst_y = (WIN_YSIZE - height) / 2;
1758
1759   SetDrawBackgroundMask(REDRAW_ALL);
1760   SetWindowBackgroundImage(getTitleBackground(nr, initial, TRUE));
1761
1762   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1763
1764   boolean draw_masked_final = (DrawingOnBackground(dst_x, dst_y) && draw_masked);
1765
1766   if (bitmap_background != NULL)
1767   {
1768     int size = bitmap_background->height - bitmap->height;
1769     int offset = frame_counter++ % size;
1770
1771     BlitBitmap(bitmap_background, drawto, src_x, src_y + offset, width, height, dst_x, dst_y);
1772
1773     draw_masked_final = TRUE;
1774   }
1775
1776   if (draw_masked_final)
1777     BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1778   else
1779     BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1780
1781   redraw_mask = REDRAW_ALL;
1782 }
1783
1784 static void DrawTitleScreenMessage(int nr, boolean initial)
1785 {
1786   char *filename = getLevelSetTitleMessageFilename(nr, initial);
1787   struct TitleMessageInfo *tmi = getTitleMessageInfo(nr, initial);
1788
1789   if (filename == NULL)
1790     return;
1791
1792   // force TITLE font on title message screen
1793   SetFontStatus(getTitleMessageGameMode(initial));
1794
1795   // if chars *and* width set to "-1", automatically determine width
1796   if (tmi->chars == -1 && tmi->width == -1)
1797     tmi->width = viewport.window[game_status].width;
1798
1799   // if lines *and* height set to "-1", automatically determine height
1800   if (tmi->lines == -1 && tmi->height == -1)
1801     tmi->height = viewport.window[game_status].height;
1802
1803   // if chars set to "-1", automatically determine by text and font width
1804   if (tmi->chars == -1)
1805     tmi->chars = tmi->width / getFontWidth(tmi->font);
1806   else
1807     tmi->width = tmi->chars * getFontWidth(tmi->font);
1808
1809   // if lines set to "-1", automatically determine by text and font height
1810   if (tmi->lines == -1)
1811     tmi->lines = tmi->height / getFontHeight(tmi->font);
1812   else
1813     tmi->height = tmi->lines * getFontHeight(tmi->font);
1814
1815   // if x set to "-1", automatically determine by width and alignment
1816   if (tmi->x == -1)
1817     tmi->x = -1 * ALIGNED_XPOS(0, tmi->width, tmi->align);
1818
1819   // if y set to "-1", automatically determine by height and alignment
1820   if (tmi->y == -1)
1821     tmi->y = -1 * ALIGNED_YPOS(0, tmi->height, tmi->valign);
1822
1823   SetDrawBackgroundMask(REDRAW_ALL);
1824   SetWindowBackgroundImage(getTitleBackground(nr, initial, FALSE));
1825
1826   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1827
1828   DrawTextFile(ALIGNED_TEXT_XPOS(tmi), ALIGNED_TEXT_YPOS(tmi),
1829                filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
1830                tmi->autowrap, tmi->centered, tmi->parse_comments);
1831
1832   ResetFontStatus();
1833 }
1834
1835 static void DrawTitleScreen(void)
1836 {
1837   KeyboardAutoRepeatOff();
1838
1839   HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE);
1840 }
1841
1842 static boolean CheckTitleScreen(boolean levelset_has_changed)
1843 {
1844   static boolean show_title_initial = TRUE;
1845   boolean show_titlescreen = FALSE;
1846
1847   // needed to be able to skip title screen, if no image or message defined
1848   InitializeTitleControls(show_title_initial);
1849
1850   if (setup.show_titlescreen && (show_title_initial || levelset_has_changed))
1851     show_titlescreen = TRUE;
1852
1853   // show initial title images and messages only once at program start
1854   show_title_initial = FALSE;
1855
1856   return (show_titlescreen && num_title_screens > 0);
1857 }
1858
1859 void DrawMainMenu(void)
1860 {
1861   static LevelDirTree *leveldir_last_valid = NULL;
1862   boolean levelset_has_changed = FALSE;
1863   int fade_mask = REDRAW_FIELD;
1864
1865   LimitScreenUpdates(FALSE);
1866
1867   FadeSetLeaveScreen();
1868
1869   // do not fade out here -- function may continue and fade on editor screen
1870
1871   UnmapAllGadgets();
1872   FadeMenuSoundsAndMusic();
1873
1874   ExpireSoundLoops(FALSE);
1875
1876   KeyboardAutoRepeatOn();
1877
1878   audio.sound_deactivated = FALSE;
1879
1880   GetPlayerConfig();
1881
1882   // needed if last screen was the playing screen, invoked from level editor
1883   if (level_editor_test_game)
1884   {
1885     CloseDoor(DOOR_CLOSE_ALL);
1886
1887     SetGameStatus(GAME_MODE_EDITOR);
1888
1889     DrawLevelEd();
1890
1891     return;
1892   }
1893
1894   // needed if last screen was the playing screen, invoked from hall of fame
1895   if (score_info_tape_play)
1896   {
1897     CloseDoor(DOOR_CLOSE_ALL);
1898
1899     SetGameStatus(GAME_MODE_SCOREINFO);
1900
1901     DrawScoreInfo(scores.last_entry_nr);
1902
1903     return;
1904   }
1905
1906   // reset flag to continue playing next level from hall of fame
1907   scores.continue_playing = FALSE;
1908
1909   // leveldir_current may be invalid (level group, parent link, node copy)
1910   leveldir_current = getValidLevelSeries(leveldir_current, leveldir_last_valid);
1911
1912   if (leveldir_current != leveldir_last_valid)
1913   {
1914     // level setup config may have been loaded to "last played" tree node copy,
1915     // but "leveldir_current" now points to the "original" level set tree node,
1916     // in which case "handicap_level" may still default to the first level
1917     LoadLevelSetup_SeriesInfo();
1918
1919     UpdateLastPlayedLevels_TreeInfo();
1920
1921     levelset_has_changed = TRUE;
1922   }
1923
1924   // store valid level series information
1925   leveldir_last_valid = leveldir_current;
1926
1927   // store first level of this level set for "level_nr" style animations
1928   SetAnimationFirstLevel(leveldir_current->first_level);
1929
1930   // level_nr may have been set to value over handicap with level editor
1931   if (setup.handicap && level_nr > leveldir_current->handicap_level)
1932     level_nr = leveldir_current->handicap_level;
1933
1934   LoadLevel(level_nr);
1935   LoadScore(level_nr);
1936
1937   SaveLevelSetup_SeriesInfo();
1938
1939   // needed if last screen (level choice) changed graphics, sounds or music
1940   ReloadCustomArtwork(0);
1941
1942   if (CheckTitleScreen(levelset_has_changed))
1943   {
1944     SetGameStatus(GAME_MODE_TITLE);
1945
1946     DrawTitleScreen();
1947
1948     return;
1949   }
1950
1951   if (redraw_mask & REDRAW_ALL)
1952     fade_mask = REDRAW_ALL;
1953
1954   if (CheckFadeAll())
1955     fade_mask = REDRAW_ALL;
1956
1957   FadeOut(fade_mask);
1958
1959   // needed if different viewport properties defined for menues
1960   ChangeViewportPropertiesIfNeeded();
1961
1962   SetDrawtoField(DRAW_TO_BACKBUFFER);
1963
1964   // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it)
1965   SetDrawDeactivationMask(REDRAW_NONE);
1966   SetDrawBackgroundMask(REDRAW_FIELD);
1967
1968   SetMainBackgroundImage(IMG_BACKGROUND_MAIN);
1969
1970 #if 0
1971   if (fade_mask == REDRAW_ALL)
1972     RedrawGlobalBorder();
1973 #endif
1974
1975   ClearField();
1976
1977   InitializeMainControls();
1978
1979   DrawCursorAndText_Main(-1, FALSE, FALSE);
1980   DrawPreviewLevelInitial();
1981   DrawNetworkPlayers();
1982
1983   HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE);
1984
1985   TapeStop();
1986   if (TAPE_IS_EMPTY(tape))
1987     LoadTape(level_nr);
1988   DrawCompleteVideoDisplay();
1989
1990   PlayMenuSoundsAndMusic();
1991
1992   // create gadgets for main menu screen
1993   FreeScreenGadgets();
1994   CreateScreenGadgets();
1995
1996   // may be required if audio buttons shown on tape and changed in setup menu
1997   FreeGameButtons();
1998   CreateGameButtons();
1999
2000   // map gadgets for main menu screen
2001   MapTapeButtons();
2002   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
2003   UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SOLUTION, hasSolutionTape());
2004   UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SET_INFO, hasLevelSetInfo());
2005
2006   // copy actual game door content to door double buffer for OpenDoor()
2007   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
2008   BlitBitmap(drawto, bitmap_db_door_2, VX, VY, VXSIZE, VYSIZE, 0, 0);
2009
2010   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
2011
2012   DrawMaskedBorder(fade_mask);
2013
2014   FadeIn(fade_mask);
2015   FadeSetEnterMenu();
2016
2017   // update screen area with special editor door
2018   redraw_mask |= REDRAW_ALL;
2019   BackToFront();
2020
2021   SetMouseCursor(CURSOR_DEFAULT);
2022
2023   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
2024
2025   SyncEmscriptenFilesystem();
2026
2027   // needed once after program start or after user change
2028   CheckApiServerTasks();
2029 }
2030
2031 static void gotoTopLevelDir(void)
2032 {
2033   // move upwards until inside (but not above) top level directory
2034   while (leveldir_current->node_parent &&
2035          !strEqual(leveldir_current->node_parent->subdir, STRING_TOP_DIRECTORY))
2036   {
2037     // write a "path" into level tree for easy navigation to last level
2038     if (leveldir_current->node_parent->node_group->cl_first == -1)
2039     {
2040       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
2041       int leveldir_pos = getPosFromTreeInfo(leveldir_current);
2042       int num_page_entries = MIN(num_leveldirs, NUM_MENU_ENTRIES_ON_SCREEN);
2043       int cl_first, cl_cursor;
2044
2045       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
2046       cl_cursor = leveldir_pos - cl_first;
2047
2048       leveldir_current->node_parent->node_group->cl_first = cl_first;
2049       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
2050     }
2051
2052     leveldir_current = leveldir_current->node_parent;
2053   }
2054 }
2055
2056 static unsigned int getAutoDelayCounter(struct TitleFadingInfo *fi)
2057 {
2058   boolean use_frame_counter = (fi->auto_delay_unit == AUTO_DELAY_UNIT_FRAMES);
2059
2060   return (use_frame_counter ? video.frame_counter : Counter());
2061 }
2062
2063 static boolean TitleAutoDelayReached(unsigned int *counter_var,
2064                                      struct TitleFadingInfo *fi)
2065 {
2066   return DelayReachedExt2(counter_var, fi->auto_delay, getAutoDelayCounter(fi));
2067 }
2068
2069 static void ResetTitleAutoDelay(unsigned int *counter_var,
2070                                 struct TitleFadingInfo *fi)
2071 {
2072   *counter_var = getAutoDelayCounter(fi);
2073 }
2074
2075 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
2076 {
2077   static unsigned int title_delay = 0;
2078   static int title_screen_nr = 0;
2079   static int last_sound = -1, last_music = -1;
2080   boolean return_to_main_menu = FALSE;
2081   struct TitleControlInfo *tci;
2082   int sound, music;
2083
2084   if (button == MB_MENU_INITIALIZE)
2085   {
2086     title_delay = 0;
2087     title_screen_nr = 0;
2088     tci = &title_controls[title_screen_nr];
2089
2090     SetAnimStatus(getTitleAnimMode(tci));
2091
2092     last_sound = SND_UNDEFINED;
2093     last_music = MUS_UNDEFINED;
2094
2095     if (num_title_screens != 0)
2096     {
2097       FadeSetEnterScreen();
2098
2099       // use individual title fading instead of global "enter screen" fading
2100       fading = getTitleFading(tci);
2101     }
2102
2103     if (game_status_last_screen == GAME_MODE_INFO)
2104     {
2105       if (num_title_screens == 0)
2106       {
2107         // switch game mode from title screen mode back to info screen mode
2108         SetGameStatus(GAME_MODE_INFO);
2109
2110         // store that last screen was info screen, not main menu screen
2111         game_status_last_screen = GAME_MODE_INFO;
2112
2113         DrawInfoScreen_NotAvailable("Title screen information:",
2114                                     "No title screen for this level set.");
2115         return;
2116       }
2117     }
2118
2119     FadeMenuSoundsAndMusic();
2120
2121     FadeOut(REDRAW_ALL);
2122
2123     // title screens may have different window size
2124     ChangeViewportPropertiesIfNeeded();
2125
2126     // only required to update logic for redrawing global border
2127     ClearField();
2128
2129     if (tci->is_image)
2130       DrawTitleScreenImage(tci->local_nr, tci->initial);
2131     else
2132       DrawTitleScreenMessage(tci->local_nr, tci->initial);
2133
2134     sound = getTitleSound(tci);
2135     music = getTitleMusic(tci);
2136
2137     if (sound != last_sound)
2138       PlayMenuSoundExt(sound);
2139     if (music != last_music)
2140       PlayMenuMusicExt(music);
2141
2142     last_sound = sound;
2143     last_music = music;
2144
2145     SetMouseCursor(CURSOR_NONE);
2146
2147     FadeIn(REDRAW_ALL);
2148
2149     ResetTitleAutoDelay(&title_delay, &fading);
2150
2151     return;
2152   }
2153
2154   if (fading.auto_delay > 0 && TitleAutoDelayReached(&title_delay, &fading))
2155     button = MB_MENU_CHOICE;
2156
2157   if (button == MB_MENU_LEAVE)
2158   {
2159     return_to_main_menu = TRUE;
2160   }
2161   else if (button == MB_MENU_CHOICE || dx)
2162   {
2163     if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0)
2164     {
2165       SetGameStatus(GAME_MODE_INFO);
2166
2167       info_mode = INFO_MODE_MAIN;
2168
2169       DrawInfoScreen();
2170
2171       return;
2172     }
2173
2174     title_screen_nr +=
2175       (game_status_last_screen == GAME_MODE_INFO && dx < 0 ? -1 : +1);
2176
2177     if (title_screen_nr >= 0 && title_screen_nr < num_title_screens)
2178     {
2179       tci = &title_controls[title_screen_nr];
2180
2181       SetAnimStatus(getTitleAnimMode(tci));
2182
2183       sound = getTitleSound(tci);
2184       music = getTitleMusic(tci);
2185
2186       if (last_sound != SND_UNDEFINED && sound != last_sound)
2187         FadeSound(last_sound);
2188       if (last_music != MUS_UNDEFINED && music != last_music)
2189         FadeMusic();
2190
2191       fading = getTitleFading(tci);
2192
2193       FadeOut(REDRAW_ALL);
2194
2195       if (tci->is_image)
2196         DrawTitleScreenImage(tci->local_nr, tci->initial);
2197       else
2198         DrawTitleScreenMessage(tci->local_nr, tci->initial);
2199
2200       sound = getTitleSound(tci);
2201       music = getTitleMusic(tci);
2202
2203       if (sound != last_sound)
2204         PlayMenuSoundExt(sound);
2205       if (music != last_music)
2206         PlayMenuMusicExt(music);
2207
2208       last_sound = sound;
2209       last_music = music;
2210
2211       FadeIn(REDRAW_ALL);
2212
2213       ResetTitleAutoDelay(&title_delay, &fading);
2214     }
2215     else
2216     {
2217       FadeMenuSoundsAndMusic();
2218
2219       return_to_main_menu = TRUE;
2220     }
2221   }
2222   else
2223   {
2224     tci = &title_controls[title_screen_nr];
2225
2226     // check for optional title screen of native BD style level set
2227     if (tci->is_image && CheckTitleScreen_BD(tci->local_nr, tci->initial))
2228     {
2229       Bitmap **title_screen_bitmaps = GetTitleScreenBitmaps_BD();
2230
2231       // if title screen is animated, draw title screen animation
2232       if (title_screen_bitmaps[0] != NULL &&
2233           title_screen_bitmaps[1] != NULL)
2234         DrawTitleScreenImage(tci->local_nr, tci->initial);
2235     }
2236   }
2237
2238   if (return_to_main_menu)
2239   {
2240     SetMouseCursor(CURSOR_DEFAULT);
2241
2242     // force full menu screen redraw after displaying title screens
2243     redraw_mask = REDRAW_ALL;
2244
2245     if (game_status_last_screen == GAME_MODE_INFO)
2246     {
2247       SetGameStatus(GAME_MODE_INFO);
2248
2249       info_mode = INFO_MODE_MAIN;
2250
2251       DrawInfoScreen();
2252     }
2253     else        // default: return to main menu
2254     {
2255       SetGameStatus(GAME_MODE_MAIN);
2256
2257       DrawMainMenu();
2258     }
2259   }
2260 }
2261
2262 static void HandleMainMenu_ToggleTeamMode(void)
2263 {
2264   setup.team_mode = !setup.team_mode;
2265
2266   InitializeMainControls();
2267   DrawCursorAndText_Main(MAIN_CONTROL_NAME, TRUE, FALSE);
2268
2269   DrawPreviewPlayers();
2270 }
2271
2272 static void HandleMainMenu_SelectLevel(int step, int direction,
2273                                        int selected_level_nr)
2274 {
2275   int old_level_nr = level_nr;
2276   int new_level_nr;
2277
2278   if (selected_level_nr != NO_DIRECT_LEVEL_SELECT)
2279     new_level_nr = selected_level_nr;
2280   else
2281     new_level_nr = old_level_nr + step * direction;
2282
2283   if (new_level_nr < leveldir_current->first_level)
2284     new_level_nr = leveldir_current->first_level;
2285   if (new_level_nr > leveldir_current->last_level)
2286     new_level_nr = leveldir_current->last_level;
2287
2288   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
2289   {
2290     // skipping levels is only allowed when trying to skip single level
2291     // (also, skipping BD style intermission levels is always possible)
2292     if (new_level_nr == old_level_nr + 1 &&
2293         (level.bd_intermission ||
2294          (setup.skip_levels && Request("Level still unsolved! Skip it anyway?", REQ_ASK))))
2295     {
2296       leveldir_current->handicap_level++;
2297       SaveLevelSetup_SeriesInfo();
2298     }
2299
2300     new_level_nr = leveldir_current->handicap_level;
2301   }
2302
2303   if (new_level_nr != old_level_nr)
2304   {
2305     struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_LEVEL_NUMBER);
2306
2307     PlaySound(SND_MENU_ITEM_SELECTING);
2308
2309     level_nr = new_level_nr;
2310
2311     DrawText(mSX + mci->pos_text->x, mSY + mci->pos_text->y,
2312              int2str(level_nr, menu.main.text.level_number.size),
2313              mci->pos_text->font);
2314
2315     LoadLevel(level_nr);
2316     DrawPreviewLevelInitial();
2317
2318     TapeErase();
2319     LoadTape(level_nr);
2320     DrawCompleteVideoDisplay();
2321
2322     SaveLevelSetup_SeriesInfo();
2323
2324     UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SOLUTION, hasSolutionTape());
2325
2326     // force redraw of playfield area (may be reset at this point)
2327     redraw_mask |= REDRAW_FIELD;
2328   }
2329 }
2330
2331 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
2332 {
2333   static int choice = MAIN_CONTROL_GAME;
2334   static boolean button_pressed_last = FALSE;
2335   boolean button_pressed = FALSE;
2336   int pos = choice;
2337   int i = 0;    // needed to prevent compiler warning due to bad code below
2338
2339   if (button == MB_MENU_INITIALIZE)
2340   {
2341     DrawCursorAndText_Main(choice, TRUE, FALSE);
2342
2343     return;
2344   }
2345
2346   if (mx || my)         // mouse input
2347   {
2348     pos = -1;
2349
2350     for (i = 0; main_controls[i].nr != -1; i++)
2351     {
2352       if (insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY) ||
2353           insideTextPosRect(main_controls[i].pos_text,   mx - mSX, my - mSY) ||
2354           insideTextPosRect(main_controls[i].pos_input,  mx - mSX, my - mSY))
2355       {
2356         pos = main_controls[i].nr;
2357
2358         break;
2359       }
2360     }
2361
2362     // check if level preview was clicked
2363     if (insidePreviewRect(&preview, mx - SX, my - SY))
2364       pos = MAIN_CONTROL_GAME;
2365
2366     // handle pressed/unpressed state for active/inactive menu buttons
2367     // (if pos != -1, "i" contains index position corresponding to "pos")
2368     if (button &&
2369         pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT &&
2370         insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY))
2371       button_pressed = TRUE;
2372
2373     if (button_pressed != button_pressed_last)
2374     {
2375       DrawCursorAndText_Main(choice, TRUE, button_pressed);
2376
2377       if (button_pressed)
2378         PlaySound(SND_MENU_BUTTON_PRESSING);
2379       else
2380         PlaySound(SND_MENU_BUTTON_RELEASING);
2381     }
2382   }
2383   else if (dx || dy)    // keyboard input
2384   {
2385     if (dx > 0 && (choice == MAIN_CONTROL_INFO ||
2386                    choice == MAIN_CONTROL_SETUP))
2387       button = MB_MENU_CHOICE;
2388     else if (dy)
2389       pos = choice + dy;
2390   }
2391
2392   if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
2393   {
2394     HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
2395   }
2396   else if (pos == MAIN_CONTROL_LAST_LEVEL && !button)
2397   {
2398     HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT);
2399   }
2400   else if (pos == MAIN_CONTROL_LEVEL_NUMBER && !button)
2401   {
2402     CloseDoor(DOOR_CLOSE_2);
2403
2404     SetGameStatus(GAME_MODE_LEVELNR);
2405
2406     DrawChooseLevelNr();
2407   }
2408   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
2409   {
2410     if (button)
2411     {
2412       if (pos != choice)
2413       {
2414         PlaySound(SND_MENU_ITEM_ACTIVATING);
2415
2416         DrawCursorAndText_Main(choice, FALSE, FALSE);
2417         DrawCursorAndText_Main(pos, TRUE, button_pressed);
2418
2419         choice = pos;
2420       }
2421       else if (dx != 0)
2422       {
2423         if (choice == MAIN_CONTROL_NAME)
2424         {
2425           // special case: cursor left or right pressed -- toggle team mode
2426           HandleMainMenu_ToggleTeamMode();
2427         }
2428         else if (choice != MAIN_CONTROL_INFO &&
2429                  choice != MAIN_CONTROL_SETUP)
2430         {
2431           HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
2432         }
2433       }
2434     }
2435     else
2436     {
2437       PlaySound(SND_MENU_ITEM_SELECTING);
2438
2439       if (pos == MAIN_CONTROL_NAME)
2440       {
2441         if ((mx || my) &&
2442             insideTextPosRect(main_controls[i].pos_text, mx - mSX, my - mSY))
2443         {
2444           // special case: menu text "name/team" clicked -- toggle team mode
2445           HandleMainMenu_ToggleTeamMode();
2446         }
2447         else
2448         {
2449           if (setup.multiple_users)
2450           {
2451             CloseDoor(DOOR_CLOSE_2);
2452
2453             SetGameStatus(GAME_MODE_NAMES);
2454
2455             DrawChoosePlayerName();
2456           }
2457           else
2458           {
2459             SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
2460
2461             DrawTypeName();
2462           }
2463         }
2464       }
2465       else if (pos == MAIN_CONTROL_LEVELS)
2466       {
2467         if (leveldir_first)
2468         {
2469           CloseDoor(DOOR_CLOSE_2);
2470
2471           SetGameStatus(GAME_MODE_LEVELS);
2472
2473           SaveLevelSetup_LastSeries();
2474           SaveLevelSetup_SeriesInfo();
2475
2476           // restore level set if chosen from "last played level set" menu
2477           RestoreLastPlayedLevels(&leveldir_current);
2478
2479           if (setup.internal.choose_from_top_leveldir)
2480             gotoTopLevelDir();
2481
2482           DrawChooseLevelSet();
2483         }
2484       }
2485       else if (pos == MAIN_CONTROL_SCORES)
2486       {
2487         CloseDoor(DOOR_CLOSE_2);
2488
2489         SetGameStatus(GAME_MODE_SCORES);
2490
2491         DrawHallOfFame(level_nr);
2492       }
2493       else if (pos == MAIN_CONTROL_EDITOR)
2494       {
2495         if (leveldir_current->readonly &&
2496             setup.editor.show_read_only_warning)
2497           Request("This level is read-only!", REQ_CONFIRM | REQ_STAY_OPEN);
2498
2499         CloseDoor(DOOR_CLOSE_ALL);
2500
2501         SetGameStatus(GAME_MODE_EDITOR);
2502
2503         FadeSetEnterScreen();
2504
2505         DrawLevelEd();
2506       }
2507       else if (pos == MAIN_CONTROL_INFO)
2508       {
2509         CloseDoor(DOOR_CLOSE_2);
2510
2511         SetGameStatus(GAME_MODE_INFO);
2512
2513         info_mode = INFO_MODE_MAIN;
2514
2515         DrawInfoScreen();
2516       }
2517       else if (pos == MAIN_CONTROL_GAME)
2518       {
2519         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
2520       }
2521       else if (pos == MAIN_CONTROL_SETUP)
2522       {
2523         CloseDoor(DOOR_CLOSE_2);
2524
2525         SetGameStatus(GAME_MODE_SETUP);
2526
2527         setup_mode = SETUP_MODE_MAIN;
2528
2529         DrawSetupScreen();
2530       }
2531       else if (pos == MAIN_CONTROL_QUIT)
2532       {
2533         SaveLevelSetup_LastSeries();
2534         SaveLevelSetup_SeriesInfo();
2535
2536 #if defined(PLATFORM_EMSCRIPTEN)
2537         Request("Close the browser window to quit!", REQ_CONFIRM);
2538 #else
2539         if (!setup.ask_on_quit_program ||
2540             Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED))
2541           SetGameStatus(GAME_MODE_QUIT);
2542 #endif
2543       }
2544     }
2545   }
2546
2547   button_pressed_last = button_pressed;
2548 }
2549
2550
2551 // ============================================================================
2552 // info screen functions
2553 // ============================================================================
2554
2555 static struct TokenInfo *info_info;
2556 static int num_info_info;       // number of info entries shown on screen
2557 static int max_info_info;       // total number of info entries in list
2558
2559 static void execInfoTitleScreen(void)
2560 {
2561   info_mode = INFO_MODE_TITLE;
2562
2563   DrawInfoScreen();
2564 }
2565
2566 static void execInfoElements(void)
2567 {
2568   info_mode = INFO_MODE_ELEMENTS;
2569
2570   DrawInfoScreen();
2571 }
2572
2573 static void execInfoMusic(void)
2574 {
2575   info_mode = INFO_MODE_MUSIC;
2576
2577   DrawInfoScreen();
2578 }
2579
2580 static void execInfoCredits(void)
2581 {
2582   info_mode = INFO_MODE_CREDITS;
2583
2584   DrawInfoScreen();
2585 }
2586
2587 static void execInfoProgram(void)
2588 {
2589   info_mode = INFO_MODE_PROGRAM;
2590
2591   DrawInfoScreen();
2592 }
2593
2594 static void execInfoVersion(void)
2595 {
2596   info_mode = INFO_MODE_VERSION;
2597
2598   DrawInfoScreen();
2599 }
2600
2601 static void execInfoLevelSet(void)
2602 {
2603   info_mode = INFO_MODE_LEVELSET;
2604
2605   DrawInfoScreen();
2606 }
2607
2608 static void execExitInfo(void)
2609 {
2610   SetGameStatus(GAME_MODE_MAIN);
2611
2612   DrawMainMenu();
2613 }
2614
2615 static struct TokenInfo info_info_main[] =
2616 {
2617   { TYPE_ENTER_SCREEN,  execInfoTitleScreen,    STR_INFO_TITLE          },
2618   { TYPE_ENTER_SCREEN,  execInfoElements,       STR_INFO_ELEMENTS       },
2619   { TYPE_ENTER_SCREEN,  execInfoMusic,          STR_INFO_MUSIC          },
2620   { TYPE_ENTER_SCREEN,  execInfoCredits,        STR_INFO_CREDITS        },
2621   { TYPE_ENTER_SCREEN,  execInfoProgram,        STR_INFO_PROGRAM        },
2622   { TYPE_ENTER_SCREEN,  execInfoVersion,        STR_INFO_VERSION        },
2623   { TYPE_ENTER_SCREEN,  execInfoLevelSet,       STR_INFO_LEVELSET       },
2624   { TYPE_EMPTY,         NULL,                   ""                      },
2625   { TYPE_LEAVE_MENU,    execExitInfo,           STR_INFO_EXIT           },
2626
2627   { 0,                  NULL,                   NULL                    }
2628 };
2629
2630 static int getMenuTextFont(int type)
2631 {
2632   if (type & (TYPE_SWITCH       |
2633               TYPE_YES_NO       |
2634               TYPE_YES_NO_AUTO  |
2635               TYPE_STRING       |
2636               TYPE_PLAYER       |
2637               TYPE_ECS_AGA      |
2638               TYPE_KEYTEXT      |
2639               TYPE_ENTER_LIST   |
2640               TYPE_TEXT_INPUT))
2641     return FONT_MENU_2;
2642   else
2643     return FONT_MENU_1;
2644 }
2645
2646 static struct TokenInfo *setup_info;
2647 static struct TokenInfo setup_info_input[];
2648
2649 static struct TokenInfo *menu_info;
2650
2651 static void PlayInfoSound(void)
2652 {
2653   int info_sound = getInfoScreenBackgroundSound_Generic();
2654   char *next_sound = getSoundInfoEntryFilename(info_sound);
2655
2656   if (next_sound != NULL)
2657     PlayMenuSoundExt(info_sound);
2658   else
2659     PlayMenuSound();
2660 }
2661
2662 static void PlayInfoSoundIfLoop(void)
2663 {
2664   int info_sound = getInfoScreenBackgroundSound_Generic();
2665   char *next_sound = getSoundInfoEntryFilename(info_sound);
2666
2667   if (next_sound != NULL)
2668     PlayMenuSoundIfLoopExt(info_sound);
2669   else
2670     PlayMenuSoundIfLoop();
2671 }
2672
2673 static void PlayInfoMusic(void)
2674 {
2675   int info_music = getInfoScreenBackgroundMusic_Generic();
2676   char *curr_music = getCurrentlyPlayingMusicFilename();
2677   char *next_music = getMusicInfoEntryFilename(info_music);
2678
2679   if (next_music != NULL)
2680   {
2681     // play music if info screen music differs from current music
2682     if (!strEqual(curr_music, next_music))
2683       PlayMenuMusicExt(info_music);
2684   }
2685   else
2686   {
2687     // only needed if info screen was directly invoked from main menu
2688     PlayMenuMusic();
2689   }
2690 }
2691
2692 static void PlayInfoSoundsAndMusic(void)
2693 {
2694   PlayInfoSound();
2695   PlayInfoMusic();
2696 }
2697
2698 static void FadeInfoSounds(void)
2699 {
2700   FadeSounds();
2701 }
2702
2703 static void FadeInfoMusic(void)
2704 {
2705   int info_music = getInfoScreenBackgroundMusic_Generic();
2706   char *curr_music = getCurrentlyPlayingMusicFilename();
2707   char *next_music = getMusicInfoEntryFilename(info_music);
2708
2709   if (next_music != NULL)
2710   {
2711     // fade music if info screen music differs from current music
2712     if (!strEqual(curr_music, next_music))
2713       FadeMusic();
2714   }
2715 }
2716
2717 static void FadeInfoSoundsAndMusic(void)
2718 {
2719   FadeInfoSounds();
2720   FadeInfoMusic();
2721 }
2722
2723 static void DrawCursorAndText_Menu_Ext(struct TokenInfo *token_info,
2724                                        int screen_pos, int menu_info_pos_raw,
2725                                        boolean active)
2726 {
2727   int pos = (menu_info_pos_raw < 0 ? screen_pos : menu_info_pos_raw);
2728   struct TokenInfo *ti = &token_info[pos];
2729   int xpos = MENU_SCREEN_START_XPOS;
2730   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
2731   int font_nr = getMenuTextFont(ti->type);
2732
2733   if (setup_mode == SETUP_MODE_INPUT)
2734     font_nr = FONT_MENU_1;
2735
2736   if (active)
2737     font_nr = FONT_ACTIVE(font_nr);
2738
2739   DrawText(mSX + xpos * 32, mSY + ypos * 32, ti->text, font_nr);
2740
2741   if (ti->type & ~TYPE_SKIP_ENTRY)
2742     drawCursor(screen_pos, active);
2743 }
2744
2745 static void DrawCursorAndText_Menu(int screen_pos, int menu_info_pos_raw,
2746                                    boolean active)
2747 {
2748   DrawCursorAndText_Menu_Ext(menu_info, screen_pos, menu_info_pos_raw, active);
2749 }
2750
2751 static void DrawCursorAndText_Setup(int screen_pos, int menu_info_pos_raw,
2752                                     boolean active)
2753 {
2754   DrawCursorAndText_Menu_Ext(setup_info, screen_pos, menu_info_pos_raw, active);
2755 }
2756
2757 static char *window_size_text;
2758 static char *scaling_type_text;
2759 static char *network_server_text;
2760
2761 static void drawSetupValue(int, int);
2762
2763 static void drawMenuInfoList(int first_entry, int num_page_entries,
2764                              int max_page_entries)
2765 {
2766   int i;
2767
2768   if (first_entry + num_page_entries > max_page_entries)
2769     first_entry = 0;
2770
2771   clearMenuListArea();
2772
2773   for (i = 0; i < num_page_entries; i++)
2774   {
2775     int menu_info_pos = first_entry + i;
2776     struct TokenInfo *si = &menu_info[menu_info_pos];
2777     void *value_ptr = si->value;
2778
2779     // set some entries to "unchangeable" according to other variables
2780     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
2781         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
2782         (value_ptr == &setup.sound_music  && !audio.music_available) ||
2783         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
2784         (value_ptr == &window_size_text   && !video.window_scaling_available) ||
2785         (value_ptr == &scaling_type_text  && !video.window_scaling_available))
2786       si->type |= TYPE_GHOSTED;
2787
2788     if (si->type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
2789       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2790     else if (si->type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
2791       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2792     else if (si->type & ~TYPE_SKIP_ENTRY)
2793       initCursor(i, IMG_MENU_BUTTON);
2794
2795     DrawCursorAndText_Menu(i, menu_info_pos, FALSE);
2796
2797     if (si->type & TYPE_STRING)
2798     {
2799       int gadget_id = -1;
2800
2801       if (value_ptr == &network_server_text)
2802         gadget_id = SCREEN_CTRL_ID_NETWORK_SERVER;
2803
2804       if (gadget_id != -1)
2805       {
2806         struct GadgetInfo *gi = screen_gadget[gadget_id];
2807         int xpos = MENU_SCREEN_START_XPOS;
2808         int ypos = MENU_SCREEN_START_YPOS + i;
2809         int x = mSX + xpos * 32;
2810         int y = mSY + ypos * 32;
2811
2812         ModifyGadget(gi, GDI_X, x, GDI_Y, y, GDI_END);
2813       }
2814     }
2815
2816     if (si->type & TYPE_VALUE &&
2817         menu_info == setup_info)
2818       drawSetupValue(i, menu_info_pos);
2819   }
2820 }
2821
2822 static void DrawInfoScreen_Main(void)
2823 {
2824   int fade_mask = REDRAW_FIELD;
2825   int i;
2826
2827   // (needed after displaying info sub-screens directly from main menu)
2828   if (info_screens_from_main)
2829   {
2830     info_screens_from_main = FALSE;
2831
2832     SetGameStatus(GAME_MODE_MAIN);
2833
2834     DrawMainMenu();
2835
2836     return;
2837   }
2838
2839   if (redraw_mask & REDRAW_ALL)
2840     fade_mask = REDRAW_ALL;
2841
2842   if (CheckFadeAll())
2843     fade_mask = REDRAW_ALL;
2844
2845   UnmapAllGadgets();
2846   FadeMenuSoundsAndMusic();
2847
2848   FreeScreenGadgets();
2849   CreateScreenGadgets();
2850
2851   // (needed after displaying title screens which disable auto repeat)
2852   KeyboardAutoRepeatOn();
2853
2854   FadeSetLeaveScreen();
2855
2856   FadeOut(fade_mask);
2857
2858   // needed if different viewport properties defined for info screen
2859   ChangeViewportPropertiesIfNeeded();
2860
2861   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
2862
2863   ClearField();
2864
2865   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
2866
2867   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, STR_INFO_MAIN);
2868
2869   info_info = info_info_main;
2870
2871   // use modified info screen info without info screen entries marked as hidden
2872   info_info = getSetupInfoFinal(info_info);
2873
2874   // determine maximal number of info entries that can be displayed on screen
2875   num_info_info = 0;
2876   for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2877     num_info_info++;
2878
2879   // determine maximal number of info entries available for menu of info screen
2880   max_info_info = 0;
2881   for (i = 0; info_info[i].type != 0; i++)
2882     max_info_info++;
2883
2884   HandleInfoScreen_Main(0, 0, 0, 0, MB_MENU_INITIALIZE);
2885
2886   MapScreenGadgets(max_info_info);
2887
2888   PlayMenuSoundsAndMusic();
2889
2890   DrawMaskedBorder(fade_mask);
2891
2892   FadeIn(fade_mask);
2893 }
2894
2895 static void changeSetupValue(int, int, int);
2896
2897 static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
2898                              int mode, int num_page_entries,
2899                              int max_page_entries)
2900 {
2901   static int num_page_entries_all_last[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2902   static int choice_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2903   static int first_entry_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2904   boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
2905   int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
2906   int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
2907   int *num_page_entries_last = num_page_entries_all_last[game_status];
2908   int *choice_store = choice_stores[game_status];
2909   int *first_entry_store = first_entry_stores[game_status];
2910   int choice = choice_store[mode];              // starts with 0
2911   int first_entry = first_entry_store[mode];    // starts with 0
2912   int x = 0;
2913   int y = choice - first_entry;
2914   int y_old = y;
2915   boolean position_set_by_scrollbar = (dx == 999);
2916   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2917   int i;
2918
2919   if (button == MB_MENU_INITIALIZE)
2920   {
2921     // check if number of menu page entries has changed (may happen by change
2922     // of custom artwork definition value for 'list_size' for this menu screen)
2923     // (in this case, the last menu position most probably has to be corrected)
2924     if (num_page_entries != num_page_entries_last[mode])
2925     {
2926       choice_store[mode] = first_entry_store[mode] = 0;
2927
2928       choice = first_entry = 0;
2929       y = y_old = 0;
2930
2931       num_page_entries_last[mode] = num_page_entries;
2932     }
2933
2934     // advance to first valid menu entry
2935     while (choice < num_page_entries &&
2936            menu_info[choice].type & TYPE_SKIP_ENTRY)
2937       choice++;
2938
2939     if (position_set_by_scrollbar)
2940       first_entry = first_entry_store[mode] = dy;
2941     else
2942       AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2943                       NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2944
2945     drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2946
2947     if (choice < first_entry)
2948     {
2949       choice = first_entry;
2950
2951       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2952         choice++;
2953     }
2954     else if (choice > first_entry + num_page_entries - 1)
2955     {
2956       choice = first_entry + num_page_entries - 1;
2957
2958       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2959         choice--;
2960     }
2961
2962     choice_store[mode] = choice;
2963
2964     DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2965
2966     return;
2967   }
2968   else if (button == MB_MENU_LEAVE)
2969   {
2970     PlaySound(SND_MENU_ITEM_SELECTING);
2971
2972     for (i = 0; i < max_page_entries; i++)
2973     {
2974       if (menu_info[i].type & TYPE_LEAVE_MENU)
2975       {
2976         void (*menu_callback_function)(void) = menu_info[i].value;
2977
2978         FadeSetLeaveMenu();
2979
2980         menu_callback_function();
2981
2982         break;  // absolutely needed because function changes 'menu_info'!
2983       }
2984     }
2985
2986     return;
2987   }
2988
2989   if (mx || my)         // mouse input
2990   {
2991     x = (mx - mSX) / 32;
2992     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2993   }
2994   else if (dx || dy)    // keyboard or scrollbar/scrollbutton input
2995   {
2996     // move cursor instead of scrolling when already at start/end of list
2997     if (dy == -1 * SCROLL_LINE && first_entry == 0)
2998       dy = -1;
2999     else if (dy == +1 * SCROLL_LINE &&
3000              first_entry + num_page_entries == max_page_entries)
3001       dy = 1;
3002
3003     // handle scrolling screen one line or page
3004     if (y + dy < 0 ||
3005         y + dy > num_page_entries - 1)
3006     {
3007       boolean redraw = FALSE;
3008
3009       if (ABS(dy) == SCROLL_PAGE)
3010         step = num_page_entries - 1;
3011
3012       if (dy < 0 && first_entry > 0)
3013       {
3014         // scroll page/line up
3015
3016         first_entry -= step;
3017         if (first_entry < 0)
3018           first_entry = 0;
3019
3020         redraw = TRUE;
3021       }
3022       else if (dy > 0 && first_entry + num_page_entries < max_page_entries)
3023       {
3024         // scroll page/line down
3025
3026         first_entry += step;
3027         if (first_entry + num_page_entries > max_page_entries)
3028           first_entry = MAX(0, max_page_entries - num_page_entries);
3029
3030         redraw = TRUE;
3031       }
3032
3033       if (redraw)
3034       {
3035         choice += first_entry - first_entry_store[mode];
3036
3037         if (choice < first_entry)
3038         {
3039           choice = first_entry;
3040
3041           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
3042             choice++;
3043         }
3044         else if (choice > first_entry + num_page_entries - 1)
3045         {
3046           choice = first_entry + num_page_entries - 1;
3047
3048           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
3049             choice--;
3050         }
3051         else if (menu_info[choice].type & TYPE_SKIP_ENTRY)
3052         {
3053           choice += SIGN(dy);
3054
3055           if (choice < first_entry ||
3056               choice > first_entry + num_page_entries - 1)
3057           first_entry += SIGN(dy);
3058         }
3059
3060         first_entry_store[mode] = first_entry;
3061         choice_store[mode] = choice;
3062
3063         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
3064
3065         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
3066
3067         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
3068                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
3069       }
3070
3071       return;
3072     }
3073
3074     if (dx)
3075     {
3076       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
3077
3078       if (menu_info[choice].type & menu_navigation_type ||
3079           menu_info[choice].type & TYPE_BOOLEAN_STYLE ||
3080           menu_info[choice].type & TYPE_YES_NO_AUTO ||
3081           menu_info[choice].type & TYPE_PLAYER)
3082         button = MB_MENU_CHOICE;
3083     }
3084     else if (dy)
3085       y += dy;
3086
3087     // jump to next non-empty menu entry (up or down)
3088     while (first_entry + y > 0 &&
3089            first_entry + y < max_page_entries - 1 &&
3090            menu_info[first_entry + y].type & TYPE_SKIP_ENTRY)
3091       y += dy;
3092
3093     if (!IN_VIS_MENU(x, y))
3094     {
3095       choice += y - y_old;
3096
3097       if (choice < first_entry)
3098         first_entry = choice;
3099       else if (choice > first_entry + num_page_entries - 1)
3100         first_entry = choice - num_page_entries + 1;
3101
3102       if (first_entry >= 0 &&
3103           first_entry + num_page_entries <= max_page_entries)
3104       {
3105         first_entry_store[mode] = first_entry;
3106
3107         if (choice < first_entry)
3108           choice = first_entry;
3109         else if (choice > first_entry + num_page_entries - 1)
3110           choice = first_entry + num_page_entries - 1;
3111
3112         choice_store[mode] = choice;
3113
3114         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
3115
3116         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
3117
3118         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
3119                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
3120       }
3121
3122       return;
3123     }
3124   }
3125
3126   if (!anyScrollbarGadgetActive() &&
3127       IN_VIS_MENU(x, y) &&
3128       mx < mx_right_border &&
3129       y >= 0 && y < num_page_entries)
3130   {
3131     if (button)
3132     {
3133       if (first_entry + y != choice &&
3134           menu_info[first_entry + y].type & ~TYPE_SKIP_ENTRY)
3135       {
3136         PlaySound(SND_MENU_ITEM_ACTIVATING);
3137
3138         DrawCursorAndText_Menu(choice - first_entry, choice, FALSE);
3139         DrawCursorAndText_Menu(y, first_entry + y, TRUE);
3140
3141         choice = choice_store[mode] = first_entry + y;
3142       }
3143       else if (dx < 0)
3144       {
3145         PlaySound(SND_MENU_ITEM_SELECTING);
3146
3147         for (i = 0; menu_info[i].type != 0; i++)
3148         {
3149           if (menu_info[i].type & TYPE_LEAVE_MENU)
3150           {
3151             void (*menu_callback_function)(void) = menu_info[i].value;
3152
3153             FadeSetLeaveMenu();
3154
3155             menu_callback_function();
3156
3157             // absolutely needed because function changes 'menu_info'!
3158             break;
3159           }
3160         }
3161
3162         return;
3163       }
3164     }
3165     else if (!(menu_info[first_entry + y].type & TYPE_GHOSTED))
3166     {
3167       PlaySound(SND_MENU_ITEM_SELECTING);
3168
3169       // when selecting key headline, execute function for key value change
3170       if (menu_info[first_entry + y].type & TYPE_KEYTEXT &&
3171           menu_info[first_entry + y + 1].type & TYPE_KEY)
3172         y++;
3173
3174       // when selecting string value, execute function for list selection
3175       if (menu_info[first_entry + y].type & TYPE_STRING && y > 0 &&
3176           menu_info[first_entry + y - 1].type & TYPE_ENTER_LIST)
3177         y--;
3178
3179       // when selecting string value, execute function for text input gadget
3180       if (menu_info[first_entry + y].type & TYPE_STRING && y > 0 &&
3181           menu_info[first_entry + y - 1].type & TYPE_TEXT_INPUT)
3182         y--;
3183
3184       if (menu_info[first_entry + y].type & TYPE_ENTER_OR_LEAVE)
3185       {
3186         void (*menu_callback_function)(void) =
3187           menu_info[first_entry + y].value;
3188
3189         FadeSetFromType(menu_info[first_entry + y].type);
3190
3191         menu_callback_function();
3192       }
3193       else if (menu_info[first_entry + y].type & TYPE_TEXT_INPUT)
3194       {
3195         void (*gadget_callback_function)(void) =
3196           menu_info[first_entry + y].value;
3197
3198         gadget_callback_function();
3199       }
3200       else if (menu_info[first_entry + y].type & TYPE_VALUE &&
3201                menu_info == setup_info)
3202       {
3203         changeSetupValue(y, first_entry + y, dx);
3204       }
3205     }
3206   }
3207 }
3208
3209 void HandleInfoScreen_Main(int mx, int my, int dx, int dy, int button)
3210 {
3211   menu_info = info_info;
3212
3213   HandleMenuScreen(mx, my, dx, dy, button,
3214                    info_mode, num_info_info, max_info_info);
3215 }
3216
3217 static int getMenuFontSpacing(int spacing_height, int font_nr)
3218 {
3219   int font_spacing = getFontHeight(font_nr) + EXTRA_SPACING(game_status);
3220
3221   return (spacing_height < 0 ? ABS(spacing_height) * font_spacing :
3222           spacing_height);
3223 }
3224
3225 static int getMenuTextSpacing(int spacing_height, int font_nr)
3226 {
3227   return (getMenuFontSpacing(spacing_height, font_nr) +
3228           EXTRA_SPACING(game_status));
3229 }
3230
3231 static int getMenuTextStep(int spacing_height, int font_nr)
3232 {
3233   return getFontHeight(font_nr) + getMenuTextSpacing(spacing_height, font_nr);
3234 }
3235
3236 static int getHeadlineSpacing(void)
3237 {
3238   // special compatibility handling for "R'n'D jue 2022" game editions
3239   int spacing_check = menu.headline1_spacing[GAME_MODE_SCOREINFO];
3240   int spacing = (game_status == GAME_MODE_SCOREINFO ?
3241                  menu.headline1_spacing[GAME_MODE_SCOREINFO] :
3242                  menu.headline1_spacing_info[info_mode]);
3243   int font = MENU_INFO_FONT_TITLE;
3244
3245   return (spacing_check != -2 ? getMenuTextStep(spacing, font) : 0);
3246 }
3247
3248 void DrawInfoScreen_NotAvailable(char *text_title, char *text_error)
3249 {
3250   int font_error = FONT_TEXT_2;
3251   int font_foot  = MENU_INFO_FONT_FOOT;
3252   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
3253   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3254
3255   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO);
3256
3257   FadeOut(REDRAW_FIELD);
3258
3259   ClearField();
3260
3261   DrawInfoScreen_Headline(0, 1, FALSE);
3262
3263   DrawTextSCentered(ystart, font_error, text_error);
3264   DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_MENU);
3265
3266   FadeIn(REDRAW_FIELD);
3267 }
3268
3269 void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init)
3270 {
3271   static int infoscreen_step[MAX_INFO_ELEMENTS_IN_ARRAY];
3272   static int infoscreen_frame[MAX_INFO_ELEMENTS_IN_ARRAY];
3273   int font_foot = MENU_INFO_FONT_FOOT;
3274   int xstart = mSX + MENU_SCREEN_INFO_SPACE_LEFT;
3275   int ystart = mSY + MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
3276   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3277   int ystep = MENU_SCREEN_INFO_YSTEP;
3278   int row_height = MENU_SCREEN_INFO_ENTRY_SIZE;
3279   int tilesize = MENU_SCREEN_INFO_TILE_SIZE;
3280   int yoffset = (row_height - tilesize) / 2;
3281   int element, action, direction;
3282   int graphic;
3283   int delay;
3284   int sync_frame;
3285   int i, j;
3286
3287   if (init)
3288   {
3289     for (i = 0; i < NUM_INFO_ELEMENTS_ON_SCREEN; i++)
3290       infoscreen_step[i] = infoscreen_frame[i] = 0;
3291
3292     DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_PAGE);
3293
3294     FrameCounter = 0;
3295   }
3296
3297   i = j = 0;
3298   while (helpanim_info[j].element != HELPANIM_LIST_END)
3299   {
3300     if (i >= start + NUM_INFO_ELEMENTS_ON_SCREEN ||
3301         i >= max_anims)
3302       break;
3303     else if (i < start)
3304     {
3305       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
3306         j++;
3307
3308       j++;
3309       i++;
3310
3311       continue;
3312     }
3313
3314     int ypos = i - start;
3315     int ystart_pos = ystart + ypos * ystep + yoffset;
3316
3317     j += infoscreen_step[ypos];
3318
3319     element = helpanim_info[j].element;
3320     action = helpanim_info[j].action;
3321     direction = helpanim_info[j].direction;
3322
3323     if (element < 0)
3324       element = EL_UNKNOWN;
3325
3326     if (action != -1 && direction != -1)
3327       graphic = el_act_dir2img(element, action, direction);
3328     else if (action != -1)
3329       graphic = el_act2img(element, action);
3330     else if (direction != -1)
3331       graphic = el_dir2img(element, direction);
3332     else
3333       graphic = el2img(element);
3334
3335     delay = helpanim_info[j++].delay;
3336
3337     if (delay == -1)
3338       delay = 1000000;
3339
3340     if (infoscreen_frame[ypos] == 0)
3341     {
3342       sync_frame = 0;
3343       infoscreen_frame[ypos] = delay - 1;
3344     }
3345     else
3346     {
3347       sync_frame = delay - infoscreen_frame[ypos];
3348       infoscreen_frame[ypos]--;
3349     }
3350
3351     if (helpanim_info[j].element == HELPANIM_LIST_NEXT)
3352     {
3353       if (!infoscreen_frame[ypos])
3354         infoscreen_step[ypos] = 0;
3355     }
3356     else
3357     {
3358       if (!infoscreen_frame[ypos])
3359         infoscreen_step[ypos]++;
3360       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
3361         j++;
3362     }
3363
3364     j++;
3365
3366     ClearRectangleOnBackground(drawto, xstart, ystart_pos, tilesize, tilesize);
3367     DrawSizedGraphicAnimationExt(drawto, xstart, ystart_pos,
3368                                  graphic, sync_frame, tilesize, USE_MASKING);
3369
3370     if (init)
3371       DrawInfoScreen_HelpText(element, action, direction, ypos);
3372
3373     i++;
3374   }
3375
3376   redraw_mask |= REDRAW_FIELD;
3377
3378   FrameCounter++;
3379 }
3380
3381 static char *getHelpText(int element, int action, int direction)
3382 {
3383   char token[MAX_LINE_LEN];
3384
3385   strcpy(token, element_info[element].token_name);
3386
3387   if (action != -1)
3388     strcat(token, element_action_info[action].suffix);
3389
3390   if (direction != -1)
3391     strcat(token, element_direction_info[MV_DIR_TO_BIT(direction)].suffix);
3392
3393   return getHashEntry(helptext_info, token);
3394 }
3395
3396 void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
3397 {
3398   int font_nr = FONT_INFO_ELEMENTS;
3399   int font_width = getFontWidth(font_nr);
3400   int font_height = getFontHeight(font_nr);
3401   int line_spacing = MENU_SCREEN_INFO_SPACE_LINE;
3402   int left_spacing = MENU_SCREEN_INFO_SPACE_LEFT;
3403   int middle_spacing = MENU_SCREEN_INFO_SPACE_MIDDLE;
3404   int right_spacing = MENU_SCREEN_INFO_SPACE_RIGHT;
3405   int line_height = font_height + line_spacing;
3406   int row_height = MENU_SCREEN_INFO_ENTRY_SIZE;
3407   int tilesize = MENU_SCREEN_INFO_TILE_SIZE;
3408   int xstart = mSX + left_spacing + tilesize + middle_spacing;
3409   int ystart = mSY + MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
3410   int ystep = MENU_SCREEN_INFO_YSTEP;
3411   int pad_left = xstart - SX;
3412   int pad_right = right_spacing;
3413   int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width;
3414   int max_lines_per_text = (row_height + line_spacing) / line_height;
3415   char *text = NULL;
3416   boolean autowrap = TRUE;
3417   boolean centered = FALSE;
3418   boolean parse_comments = FALSE;
3419
3420   if (action != -1 && direction != -1)          // element.action.direction
3421     text = getHelpText(element, action, direction);
3422
3423   if (text == NULL && action != -1)             // element.action
3424     text = getHelpText(element, action, -1);
3425
3426   if (text == NULL && direction != -1)          // element.direction
3427     text = getHelpText(element, -1, direction);
3428
3429   if (text == NULL)                             // base element
3430     text = getHelpText(element, -1, -1);
3431
3432   if (text == NULL)                             // not found
3433     text = "No description available";
3434
3435   DisableDrawingText();
3436
3437   // first get number of text lines to calculate offset for centering text
3438   int num_lines_printed =
3439     DrawTextBuffer(0, 0, text, font_nr,
3440                    max_chars_per_line, -1, max_lines_per_text, line_spacing, -1,
3441                    autowrap, centered, parse_comments);
3442
3443   EnableDrawingText();
3444
3445   int size_lines_printed = num_lines_printed * line_height - line_spacing;
3446   int yoffset = (row_height - size_lines_printed) / 2;
3447
3448   DrawTextBuffer(xstart, ystart + ypos * ystep + yoffset, text, font_nr,
3449                  max_chars_per_line, -1, max_lines_per_text, line_spacing, -1,
3450                  autowrap, centered, parse_comments);
3451 }
3452
3453 static void DrawInfoScreen_TitleScreen(void)
3454 {
3455   SetGameStatus(GAME_MODE_TITLE);
3456
3457   UnmapAllGadgets();
3458
3459   DrawTitleScreen();
3460 }
3461
3462 void HandleInfoScreen_TitleScreen(int dx, int dy, int button)
3463 {
3464   HandleTitleScreen(0, 0, dx, dy, button);
3465 }
3466
3467 static void DrawInfoScreen_Elements(void)
3468 {
3469   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_ELEMENTS);
3470
3471   UnmapAllGadgets();
3472   FadeInfoSoundsAndMusic();
3473
3474   FadeOut(REDRAW_FIELD);
3475
3476   LoadHelpAnimInfo();
3477   LoadHelpTextInfo();
3478
3479   HandleInfoScreen_Elements(0, 0, MB_MENU_INITIALIZE);
3480
3481   PlayInfoSoundsAndMusic();
3482
3483   FadeIn(REDRAW_FIELD);
3484 }
3485
3486 void HandleInfoScreen_Elements(int dx, int dy, int button)
3487 {
3488   static DelayCounter info_delay = { 0 };
3489   static int num_anims;
3490   static int num_pages;
3491   static int page;
3492   int anims_per_page = NUM_INFO_ELEMENTS_ON_SCREEN;
3493   int i;
3494
3495   info_delay.value = GameFrameDelay;
3496
3497   if (button == MB_MENU_INITIALIZE)
3498   {
3499     boolean new_element = TRUE;
3500
3501     num_anims = 0;
3502
3503     for (i = 0; helpanim_info[i].element != HELPANIM_LIST_END; i++)
3504     {
3505       if (helpanim_info[i].element == HELPANIM_LIST_NEXT)
3506         new_element = TRUE;
3507       else if (new_element)
3508       {
3509         num_anims++;
3510         new_element = FALSE;
3511       }
3512     }
3513
3514     num_pages = (num_anims + anims_per_page - 1) / anims_per_page;
3515     page = 0;
3516   }
3517
3518   if (button == MB_MENU_LEAVE)
3519   {
3520     PlaySound(SND_MENU_ITEM_SELECTING);
3521
3522     info_mode = INFO_MODE_MAIN;
3523     DrawInfoScreen();
3524
3525     return;
3526   }
3527   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx)
3528   {
3529     if (button != MB_MENU_INITIALIZE)
3530     {
3531       PlaySound(SND_MENU_ITEM_SELECTING);
3532
3533       page += (dx < 0 ? -1 : +1);
3534     }
3535
3536     if (page < 0 || page >= num_pages)
3537     {
3538       FadeInfoSoundsAndMusic();
3539
3540       info_mode = INFO_MODE_MAIN;
3541       DrawInfoScreen();
3542
3543       return;
3544     }
3545
3546     if (button != MB_MENU_INITIALIZE)
3547       FadeSetNextScreen();
3548
3549     if (button != MB_MENU_INITIALIZE)
3550       FadeOut(REDRAW_FIELD);
3551
3552     ClearField();
3553
3554     DrawInfoScreen_Headline(page, num_pages, TRUE);
3555     DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, TRUE);
3556
3557     if (button != MB_MENU_INITIALIZE)
3558       FadeIn(REDRAW_FIELD);
3559   }
3560   else
3561   {
3562     if (DelayReached(&info_delay))
3563       if (page < num_pages)
3564         DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, FALSE);
3565
3566     PlayInfoSoundIfLoop();
3567   }
3568 }
3569
3570 static void DrawInfoScreen_Music(void)
3571 {
3572   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_MUSIC);
3573
3574   UnmapAllGadgets();
3575
3576   FadeOut(REDRAW_FIELD);
3577
3578   ClearField();
3579
3580   DrawInfoScreen_Headline(0, 1, TRUE);
3581
3582   LoadMusicInfo();
3583
3584   HandleInfoScreen_Music(0, 0, MB_MENU_INITIALIZE);
3585
3586   FadeIn(REDRAW_FIELD);
3587 }
3588
3589 void HandleInfoScreen_Music(int dx, int dy, int button)
3590 {
3591   static struct MusicFileInfo *list = NULL;
3592   static int num_screens = 0;
3593   static int screen_nr = 0;
3594   int font_title = MENU_INFO_FONT_TITLE;
3595   int font_head  = MENU_INFO_FONT_HEAD;
3596   int font_text  = MENU_INFO_FONT_TEXT;
3597   int font_foot  = MENU_INFO_FONT_FOOT;
3598   int spacing_head = menu.headline2_spacing_info[info_mode];
3599   int ystep_head = getMenuTextStep(spacing_head,  font_head);
3600   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART;
3601   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3602
3603   if (button == MB_MENU_INITIALIZE)
3604   {
3605     struct MusicFileInfo *list_ptr = music_file_info;
3606
3607     num_screens = 0;
3608     screen_nr = 0;
3609
3610     while (list_ptr != NULL)
3611     {
3612       list_ptr = list_ptr->next;
3613       num_screens++;
3614     }
3615
3616     list = music_file_info;
3617
3618     if (list == NULL)
3619     {
3620       FadeMenuSoundsAndMusic();
3621
3622       ClearField();
3623
3624       DrawInfoScreen_Headline(0, 1, TRUE);
3625
3626       DrawTextSCentered(ystart, font_title, "No music info for this level set.");
3627       DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_MENU);
3628
3629       return;
3630     }
3631   }
3632
3633   if (button == MB_MENU_LEAVE)
3634   {
3635     PlaySound(SND_MENU_ITEM_SELECTING);
3636
3637     FadeMenuSoundsAndMusic();
3638
3639     info_mode = INFO_MODE_MAIN;
3640     DrawInfoScreen();
3641
3642     return;
3643   }
3644   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx)
3645   {
3646     if (button != MB_MENU_INITIALIZE)
3647     {
3648       PlaySound(SND_MENU_ITEM_SELECTING);
3649
3650       if (list != NULL)
3651       {
3652         list = (dx < 0 ? list->prev : list->next);
3653         screen_nr += (dx < 0 ? -1 : +1);
3654       }
3655     }
3656
3657     if (list == NULL)
3658     {
3659       FadeMenuSoundsAndMusic();
3660
3661       info_mode = INFO_MODE_MAIN;
3662       DrawInfoScreen();
3663
3664       return;
3665     }
3666
3667     FadeMenuSoundsAndMusic();
3668
3669     if (list != music_file_info)
3670       FadeSetNextScreen();
3671
3672     if (button != MB_MENU_INITIALIZE)
3673       FadeOut(REDRAW_FIELD);
3674
3675     ClearField();
3676
3677     DrawInfoScreen_Headline(screen_nr, num_screens, TRUE);
3678
3679     if (list->is_sound)
3680     {
3681       int sound = list->music;
3682
3683       if (IS_LOOP_SOUND(sound))
3684         PlaySoundLoop(sound);
3685       else
3686         PlaySound(sound);
3687     }
3688     else
3689     {
3690       int music = list->music;
3691
3692       if (IS_LOOP_MUSIC(music))
3693         PlayMusicLoop(music);
3694       else
3695         PlayMusic(music);
3696     }
3697
3698     if (!strEqual(list->title, UNKNOWN_NAME))
3699     {
3700       if (!strEqual(list->title_header, UNKNOWN_NAME))
3701         DrawTextSCentered(ystart, font_head, list->title_header);
3702       else
3703         DrawTextSCentered(ystart, font_head, "Track");
3704
3705       ystart += ystep_head;
3706
3707       DrawTextFCentered(ystart, font_text, "\"%s\"", list->title);
3708       ystart += ystep_head;
3709     }
3710
3711     if (!strEqual(list->artist, UNKNOWN_NAME))
3712     {
3713       if (!strEqual(list->artist_header, UNKNOWN_NAME))
3714         DrawTextSCentered(ystart, font_head, list->artist_header);
3715       else
3716         DrawTextSCentered(ystart, font_head, "by");
3717
3718       ystart += ystep_head;
3719
3720       DrawTextFCentered(ystart, font_text, "%s", list->artist);
3721       ystart += ystep_head;
3722     }
3723
3724     if (!strEqual(list->album, UNKNOWN_NAME))
3725     {
3726       if (!strEqual(list->album_header, UNKNOWN_NAME))
3727         DrawTextSCentered(ystart, font_head, list->album_header);
3728       else
3729         DrawTextSCentered(ystart, font_head, "from the album");
3730
3731       ystart += ystep_head;
3732
3733       DrawTextFCentered(ystart, font_text, "\"%s\"", list->album);
3734       ystart += ystep_head;
3735     }
3736
3737     if (!strEqual(list->year, UNKNOWN_NAME))
3738     {
3739       if (!strEqual(list->year_header, UNKNOWN_NAME))
3740         DrawTextSCentered(ystart, font_head, list->year_header);
3741       else
3742         DrawTextSCentered(ystart, font_head, "from the year");
3743
3744       ystart += ystep_head;
3745
3746       DrawTextFCentered(ystart, font_text, "%s", list->year);
3747       ystart += ystep_head;
3748     }
3749
3750     if (!strEqual(list->played, UNKNOWN_NAME))
3751     {
3752       if (!strEqual(list->played_header, UNKNOWN_NAME))
3753         DrawTextSCentered(ystart, font_head, list->played_header);
3754       else
3755         DrawTextSCentered(ystart, font_head, "played in");
3756
3757       ystart += ystep_head;
3758
3759       DrawTextFCentered(ystart, font_text, "%s", list->played);
3760       ystart += ystep_head;
3761     }
3762     else if (!list->is_sound)
3763     {
3764       int music_level_nr = -1;
3765       int i;
3766
3767       // check if this music is configured for a certain level
3768       for (i = leveldir_current->first_level;
3769            i <= leveldir_current->last_level; i++)
3770       {
3771         // (special case: "list->music" may be negative for unconfigured music)
3772         if (levelset.music[i] != MUS_UNDEFINED &&
3773             levelset.music[i] == list->music)
3774         {
3775           music_level_nr = i;
3776
3777           break;
3778         }
3779       }
3780
3781       if (music_level_nr != -1)
3782       {
3783         if (!strEqual(list->played_header, UNKNOWN_NAME))
3784           DrawTextSCentered(ystart, font_head, list->played_header);
3785         else
3786           DrawTextSCentered(ystart, font_head, "played in");
3787
3788         ystart += ystep_head;
3789
3790         DrawTextFCentered(ystart, font_text, "level %03d", music_level_nr);
3791         ystart += ystep_head;
3792       }
3793     }
3794
3795     DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_PAGE);
3796
3797     if (button != MB_MENU_INITIALIZE)
3798       FadeIn(REDRAW_FIELD);
3799   }
3800
3801   if (list != NULL && list->is_sound && IS_LOOP_SOUND(list->music))
3802     PlaySoundLoop(list->music);
3803 }
3804
3805 static void DrawInfoScreen_Version(void)
3806 {
3807   int font_head = MENU_INFO_FONT_HEAD;
3808   int font_text = MENU_INFO_FONT_TEXT;
3809   int font_foot = MENU_INFO_FONT_FOOT;
3810   int spacing_head = menu.headline2_spacing_info[info_mode];
3811   int spacing_para = menu.paragraph_spacing_info[info_mode];
3812   int spacing_line = menu.line_spacing_info[info_mode];
3813   int xstep = getFontWidth(font_text);
3814   int ystep_head = getMenuTextStep(spacing_head,  font_head);
3815   int ystep_para = getMenuTextStep(spacing_para,  font_text);
3816   int ystep_line = getMenuTextStep(spacing_line,  font_text);
3817   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
3818   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3819   int xstart1 = mSX - SX + 2 * xstep;
3820   int xstart2 = mSX - SX + 18 * xstep;
3821   int xstart3 = mSX - SX + 28 * xstep;
3822   SDL_version sdl_version_compiled;
3823   const SDL_version *sdl_version_linked;
3824   int driver_name_len = 10;
3825   SDL_version sdl_version_linked_ext;
3826   const char *driver_name = NULL;
3827
3828   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_VERSION);
3829
3830   UnmapAllGadgets();
3831   FadeInfoSoundsAndMusic();
3832
3833   FadeOut(REDRAW_FIELD);
3834
3835   ClearField();
3836
3837   DrawInfoScreen_Headline(0, 1, TRUE);
3838
3839   DrawTextF(xstart1, ystart, font_head, "Name");
3840   DrawTextF(xstart2, ystart, font_text, getProgramTitleString());
3841   ystart += ystep_line;
3842
3843   if (!strEqual(getProgramVersionString(), getProgramRealVersionString()))
3844   {
3845     DrawTextF(xstart1, ystart, font_head, "Version (fake)");
3846     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3847     ystart += ystep_line;
3848
3849     DrawTextF(xstart1, ystart, font_head, "Version (real)");
3850     DrawTextF(xstart2, ystart, font_text, getProgramRealVersionString());
3851     ystart += ystep_line;
3852   }
3853   else
3854   {
3855     DrawTextF(xstart1, ystart, font_head, "Version");
3856     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3857     ystart += ystep_line;
3858   }
3859
3860   DrawTextF(xstart1, ystart, font_head, "Platform");
3861   DrawTextF(xstart2, ystart, font_text, "%s (%s)",
3862             PLATFORM_STRING,
3863             PLATFORM_XX_BIT_STRING);
3864   ystart += ystep_line;
3865
3866   DrawTextF(xstart1, ystart, font_head, "Target");
3867   DrawTextF(xstart2, ystart, font_text, TARGET_STRING);
3868   ystart += ystep_line;
3869
3870   DrawTextF(xstart1, ystart, font_head, "Source date");
3871   DrawTextF(xstart2, ystart, font_text, getSourceDateString());
3872   ystart += ystep_line;
3873
3874   DrawTextF(xstart1, ystart, font_head, "Commit hash");
3875   DrawTextF(xstart2, ystart, font_text, getSourceHashString());
3876   ystart += ystep_para;
3877
3878   DrawTextF(xstart1, ystart, font_head, "Library");
3879   DrawTextF(xstart2, ystart, font_head, "compiled");
3880   DrawTextF(xstart3, ystart, font_head, "linked");
3881   ystart += ystep_head;
3882
3883   SDL_VERSION(&sdl_version_compiled);
3884   SDL_GetVersion(&sdl_version_linked_ext);
3885   sdl_version_linked = &sdl_version_linked_ext;
3886
3887   DrawTextF(xstart1, ystart, font_text, "SDL");
3888   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3889             sdl_version_compiled.major,
3890             sdl_version_compiled.minor,
3891             sdl_version_compiled.patch);
3892   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3893             sdl_version_linked->major,
3894             sdl_version_linked->minor,
3895             sdl_version_linked->patch);
3896   ystart += ystep_line;
3897
3898   SDL_IMAGE_VERSION(&sdl_version_compiled);
3899   sdl_version_linked = IMG_Linked_Version();
3900
3901   DrawTextF(xstart1, ystart, font_text, "SDL_image");
3902   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3903             sdl_version_compiled.major,
3904             sdl_version_compiled.minor,
3905             sdl_version_compiled.patch);
3906   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3907             sdl_version_linked->major,
3908             sdl_version_linked->minor,
3909             sdl_version_linked->patch);
3910   ystart += ystep_line;
3911
3912   SDL_MIXER_VERSION(&sdl_version_compiled);
3913   sdl_version_linked = Mix_Linked_Version();
3914
3915   DrawTextF(xstart1, ystart, font_text, "SDL_mixer");
3916   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3917             sdl_version_compiled.major,
3918             sdl_version_compiled.minor,
3919             sdl_version_compiled.patch);
3920   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3921             sdl_version_linked->major,
3922             sdl_version_linked->minor,
3923             sdl_version_linked->patch);
3924   ystart += ystep_line;
3925
3926   SDL_NET_VERSION(&sdl_version_compiled);
3927   sdl_version_linked = SDLNet_Linked_Version();
3928
3929   DrawTextF(xstart1, ystart, font_text, "SDL_net");
3930   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3931             sdl_version_compiled.major,
3932             sdl_version_compiled.minor,
3933             sdl_version_compiled.patch);
3934   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3935             sdl_version_linked->major,
3936             sdl_version_linked->minor,
3937             sdl_version_linked->patch);
3938   ystart += ystep_para;
3939
3940   DrawTextF(xstart1, ystart, font_head, "Driver");
3941   DrawTextF(xstart2, ystart, font_head, "Requested");
3942   DrawTextF(xstart3, ystart, font_head, "Used");
3943   ystart += ystep_head;
3944
3945   driver_name =
3946     getStringCopyNStatic(SDLGetRendererName(), driver_name_len);
3947
3948   DrawTextF(xstart1, ystart, font_text, "Render Driver");
3949   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_renderdriver);
3950   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3951   ystart += ystep_line;
3952
3953   driver_name =
3954     getStringCopyNStatic(SDL_GetCurrentVideoDriver(), driver_name_len);
3955
3956   DrawTextF(xstart1, ystart, font_text, "Video Driver");
3957   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
3958   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3959   ystart += ystep_line;
3960
3961   driver_name =
3962     getStringCopyNStatic(SDL_GetCurrentAudioDriver(), driver_name_len);
3963
3964   DrawTextF(xstart1, ystart, font_text, "Audio Driver");
3965   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
3966   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3967
3968   DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_MENU);
3969
3970   PlayInfoSoundsAndMusic();
3971
3972   FadeIn(REDRAW_FIELD);
3973 }
3974
3975 void HandleInfoScreen_Version(int button)
3976 {
3977   if (button == MB_MENU_LEAVE)
3978   {
3979     PlaySound(SND_MENU_ITEM_SELECTING);
3980
3981     info_mode = INFO_MODE_MAIN;
3982     DrawInfoScreen();
3983
3984     return;
3985   }
3986   else if (button == MB_MENU_CHOICE)
3987   {
3988     PlaySound(SND_MENU_ITEM_SELECTING);
3989
3990     FadeMenuSoundsAndMusic();
3991
3992     info_mode = INFO_MODE_MAIN;
3993     DrawInfoScreen();
3994   }
3995   else
3996   {
3997     PlayMenuSoundIfLoop();
3998   }
3999 }
4000
4001 static char *getInfoScreenTitle_Generic(void)
4002 {
4003   return (info_mode == INFO_MODE_MAIN     ? STR_INFO_MAIN     :
4004           info_mode == INFO_MODE_TITLE    ? STR_INFO_TITLE    :
4005           info_mode == INFO_MODE_ELEMENTS ? STR_INFO_ELEMENTS :
4006           info_mode == INFO_MODE_MUSIC    ? STR_INFO_MUSIC    :
4007           info_mode == INFO_MODE_CREDITS  ? STR_INFO_CREDITS  :
4008           info_mode == INFO_MODE_PROGRAM  ? STR_INFO_PROGRAM  :
4009           info_mode == INFO_MODE_VERSION  ? STR_INFO_VERSION  :
4010           info_mode == INFO_MODE_LEVELSET ? STR_INFO_LEVELSET :
4011           "");
4012 }
4013
4014 static int getInfoScreenBackgroundImage_Generic(void)
4015 {
4016   return (info_mode == INFO_MODE_ELEMENTS ? IMG_BACKGROUND_INFO_ELEMENTS :
4017           info_mode == INFO_MODE_MUSIC    ? IMG_BACKGROUND_INFO_MUSIC    :
4018           info_mode == INFO_MODE_CREDITS  ? IMG_BACKGROUND_INFO_CREDITS  :
4019           info_mode == INFO_MODE_PROGRAM  ? IMG_BACKGROUND_INFO_PROGRAM  :
4020           info_mode == INFO_MODE_VERSION  ? IMG_BACKGROUND_INFO_VERSION  :
4021           info_mode == INFO_MODE_LEVELSET ? IMG_BACKGROUND_INFO_LEVELSET :
4022           IMG_BACKGROUND_INFO);
4023 }
4024
4025 static int getInfoScreenBackgroundSound_Generic(void)
4026 {
4027   return (info_mode == INFO_MODE_ELEMENTS ? SND_BACKGROUND_INFO_ELEMENTS :
4028           info_mode == INFO_MODE_CREDITS  ? SND_BACKGROUND_INFO_CREDITS  :
4029           info_mode == INFO_MODE_PROGRAM  ? SND_BACKGROUND_INFO_PROGRAM  :
4030           info_mode == INFO_MODE_VERSION  ? SND_BACKGROUND_INFO_VERSION  :
4031           info_mode == INFO_MODE_LEVELSET ? SND_BACKGROUND_INFO_LEVELSET :
4032           SND_BACKGROUND_INFO);
4033 }
4034
4035 static int getInfoScreenBackgroundMusic_Generic(void)
4036 {
4037   return (info_mode == INFO_MODE_ELEMENTS ? MUS_BACKGROUND_INFO_ELEMENTS :
4038           info_mode == INFO_MODE_CREDITS  ? MUS_BACKGROUND_INFO_CREDITS  :
4039           info_mode == INFO_MODE_PROGRAM  ? MUS_BACKGROUND_INFO_PROGRAM  :
4040           info_mode == INFO_MODE_VERSION  ? MUS_BACKGROUND_INFO_VERSION  :
4041           info_mode == INFO_MODE_LEVELSET ? MUS_BACKGROUND_INFO_LEVELSET :
4042           MUS_BACKGROUND_INFO);
4043 }
4044
4045 static char *getInfoScreenFilename_Generic(int nr, boolean global)
4046 {
4047   return (info_mode == INFO_MODE_CREDITS  ? getCreditsFilename(nr, global) :
4048           info_mode == INFO_MODE_PROGRAM  ? getProgramInfoFilename(nr)     :
4049           info_mode == INFO_MODE_LEVELSET ? getLevelSetInfoFilename(nr)    :
4050           NULL);
4051 }
4052
4053 static void DrawInfoScreen_GenericScreen(int screen_nr, int num_screens,
4054                                          int use_global_screens)
4055 {
4056   char *filename = getInfoScreenFilename_Generic(screen_nr, use_global_screens);
4057   int font_text = MENU_INFO_FONT_TEXT;
4058   int font_foot = MENU_INFO_FONT_FOOT;
4059   int spacing_line = menu.line_spacing_info[info_mode];
4060   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
4061
4062   ClearField();
4063
4064   DrawInfoScreen_Headline(screen_nr, num_screens, use_global_screens);
4065
4066   if (info_mode == INFO_MODE_CREDITS ||
4067       info_mode == INFO_MODE_PROGRAM)
4068   {
4069     int width = SXSIZE;
4070     int height = MENU_SCREEN_INFO_YBOTTOM - MENU_SCREEN_INFO_YSTART;
4071     int chars = width / getFontWidth(font_text);
4072     int lines = height / getFontHeight(font_text);
4073     int padx = (width - chars * getFontWidth(font_text)) / 2;
4074     int line_spacing = getMenuTextSpacing(spacing_line, font_text);
4075     int xstart = mSX + padx;
4076     int ystart = mSY + MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
4077     boolean autowrap = FALSE;
4078     boolean centered = TRUE;
4079     boolean parse_comments = TRUE;
4080
4081     DrawTextFile(xstart, ystart,
4082                  filename, font_text, chars, -1, lines, line_spacing, -1,
4083                  autowrap, centered, parse_comments);
4084   }
4085   else if (info_mode == INFO_MODE_LEVELSET)
4086   {
4087     struct TitleMessageInfo *tmi = &readme;
4088
4089     // if x position set to "-1", automatically determine by playfield width
4090     if (tmi->x == -1)
4091       tmi->x = SXSIZE / 2;
4092
4093     // if y position set to "-1", use static default value
4094     if (tmi->y == -1)
4095       tmi->y = MENU_SCREEN_INFO_YSTART + getHeadlineSpacing();
4096
4097     // if width set to "-1", automatically determine by playfield width
4098     if (tmi->width == -1)
4099       tmi->width = SXSIZE - 2 * TILEX;
4100
4101     // if height set to "-1", automatically determine by playfield height
4102     if (tmi->height == -1)
4103       tmi->height = MENU_SCREEN_INFO_YBOTTOM - tmi->y - 10;
4104
4105     // if chars set to "-1", automatically determine by text and font width
4106     if (tmi->chars == -1)
4107       tmi->chars = tmi->width / getFontWidth(tmi->font);
4108     else
4109       tmi->width = tmi->chars * getFontWidth(tmi->font);
4110
4111     // if lines set to "-1", automatically determine by text and font height
4112     if (tmi->lines == -1)
4113       tmi->lines = tmi->height / getFontHeight(tmi->font);
4114     else
4115       tmi->height = tmi->lines * getFontHeight(tmi->font);
4116
4117     DrawTextFile(mSX + ALIGNED_TEXT_XPOS(tmi), mSY + ALIGNED_TEXT_YPOS(tmi),
4118                  filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
4119                  tmi->autowrap, tmi->centered, tmi->parse_comments);
4120   }
4121
4122   boolean last_screen = (screen_nr == num_screens - 1);
4123   char *text_foot = (last_screen ? TEXT_NEXT_MENU : TEXT_NEXT_PAGE);
4124
4125   DrawTextSCentered(ybottom, font_foot, text_foot);
4126 }
4127
4128 static void DrawInfoScreen_Generic(void)
4129 {
4130   SetMainBackgroundImageIfDefined(getInfoScreenBackgroundImage_Generic());
4131
4132   UnmapAllGadgets();
4133   FadeInfoSoundsAndMusic();
4134
4135   FadeOut(REDRAW_FIELD);
4136
4137   HandleInfoScreen_Generic(0, 0, MB_MENU_INITIALIZE);
4138
4139   PlayInfoSoundsAndMusic();
4140
4141   FadeIn(REDRAW_FIELD);
4142 }
4143
4144 void HandleInfoScreen_Generic(int dx, int dy, int button)
4145 {
4146   static char *text_no_info = "";
4147   static int num_screens = 0;
4148   static int screen_nr = 0;
4149   static boolean use_global_screens = FALSE;
4150
4151   if (button == MB_MENU_INITIALIZE)
4152   {
4153     num_screens = 0;
4154     screen_nr = 0;
4155
4156     if (info_mode == INFO_MODE_CREDITS)
4157     {
4158       int i;
4159
4160       for (i = 0; i < 2; i++)
4161       {
4162         use_global_screens = i;         // check for "FALSE", then "TRUE"
4163
4164         // determine number of (global or level set specific) credits screens
4165         while (getCreditsFilename(num_screens, use_global_screens) != NULL)
4166           num_screens++;
4167
4168         if (num_screens > 0)
4169           break;
4170       }
4171
4172       text_no_info = "No credits available.";
4173     }
4174     else if (info_mode == INFO_MODE_PROGRAM)
4175     {
4176       use_global_screens = TRUE;
4177
4178       // determine number of program info screens
4179       while (getProgramInfoFilename(num_screens) != NULL)
4180         num_screens++;
4181
4182       text_no_info = "No program info available.";
4183     }
4184     else if (info_mode == INFO_MODE_LEVELSET)
4185     {
4186       use_global_screens = FALSE;
4187
4188       // determine number of levelset info screens
4189       while (getLevelSetInfoFilename(num_screens) != NULL)
4190         num_screens++;
4191
4192       text_no_info = "No level set info available.";
4193     }
4194
4195     if (num_screens == 0)
4196     {
4197       int font_title = MENU_INFO_FONT_TITLE;
4198       int font_foot  = MENU_INFO_FONT_FOOT;
4199       int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART;
4200       int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
4201
4202       ClearField();
4203
4204       DrawInfoScreen_Headline(screen_nr, num_screens, use_global_screens);
4205
4206       DrawTextSCentered(ystart, font_title, text_no_info);
4207       DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_MENU);
4208
4209       return;
4210     }
4211
4212     DrawInfoScreen_GenericScreen(screen_nr, num_screens, use_global_screens);
4213   }
4214   else if (button == MB_MENU_LEAVE)
4215   {
4216     PlaySound(SND_MENU_ITEM_SELECTING);
4217
4218     info_mode = INFO_MODE_MAIN;
4219     DrawInfoScreen();
4220   }
4221   else if (button == MB_MENU_CHOICE || dx)
4222   {
4223     PlaySound(SND_MENU_ITEM_SELECTING);
4224
4225     screen_nr += (dx < 0 ? -1 : +1);
4226
4227     if (screen_nr < 0 || screen_nr >= num_screens)
4228     {
4229       FadeInfoSoundsAndMusic();
4230
4231       info_mode = INFO_MODE_MAIN;
4232       DrawInfoScreen();
4233     }
4234     else
4235     {
4236       FadeSetNextScreen();
4237
4238       FadeOut(REDRAW_FIELD);
4239
4240       DrawInfoScreen_GenericScreen(screen_nr, num_screens, use_global_screens);
4241
4242       FadeIn(REDRAW_FIELD);
4243     }
4244   }
4245   else
4246   {
4247     PlayInfoSoundIfLoop();
4248   }
4249 }
4250
4251 static void DrawInfoScreen(void)
4252 {
4253   if (info_mode == INFO_MODE_TITLE)
4254     DrawInfoScreen_TitleScreen();
4255   else if (info_mode == INFO_MODE_ELEMENTS)
4256     DrawInfoScreen_Elements();
4257   else if (info_mode == INFO_MODE_MUSIC)
4258     DrawInfoScreen_Music();
4259   else if (info_mode == INFO_MODE_CREDITS)
4260     DrawInfoScreen_Generic();
4261   else if (info_mode == INFO_MODE_PROGRAM)
4262     DrawInfoScreen_Generic();
4263   else if (info_mode == INFO_MODE_VERSION)
4264     DrawInfoScreen_Version();
4265   else if (info_mode == INFO_MODE_LEVELSET)
4266     DrawInfoScreen_Generic();
4267   else
4268     DrawInfoScreen_Main();
4269 }
4270
4271 void DrawInfoScreen_FromMainMenu(int nr)
4272 {
4273   int fade_mask = REDRAW_FIELD;
4274
4275   if (nr < INFO_MODE_MAIN || nr >= MAX_INFO_MODES)
4276     return;
4277
4278   CloseDoor(DOOR_CLOSE_2);
4279
4280   SetGameStatus(GAME_MODE_INFO);
4281
4282   info_mode = nr;
4283   info_screens_from_main = TRUE;
4284
4285   if (redraw_mask & REDRAW_ALL)
4286     fade_mask = REDRAW_ALL;
4287
4288   if (CheckFadeAll())
4289     fade_mask = REDRAW_ALL;
4290
4291   UnmapAllGadgets();
4292   FadeMenuSoundsAndMusic();
4293
4294   FadeSetEnterScreen();
4295
4296   FadeOut(fade_mask);
4297
4298   FadeSkipNextFadeOut();
4299
4300   // needed if different viewport properties defined for info screen
4301   ChangeViewportPropertiesIfNeeded();
4302
4303   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
4304
4305   DrawInfoScreen();
4306 }
4307
4308 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
4309 {
4310   if (info_mode == INFO_MODE_TITLE)
4311     HandleInfoScreen_TitleScreen(dx, dy, button);
4312   else if (info_mode == INFO_MODE_ELEMENTS)
4313     HandleInfoScreen_Elements(dx, dy, button);
4314   else if (info_mode == INFO_MODE_MUSIC)
4315     HandleInfoScreen_Music(dx, dy, button);
4316   else if (info_mode == INFO_MODE_CREDITS)
4317     HandleInfoScreen_Generic(dx, dy, button);
4318   else if (info_mode == INFO_MODE_PROGRAM)
4319     HandleInfoScreen_Generic(dx, dy, button);
4320   else if (info_mode == INFO_MODE_VERSION)
4321     HandleInfoScreen_Version(button);
4322   else if (info_mode == INFO_MODE_LEVELSET)
4323     HandleInfoScreen_Generic(dx, dy, button);
4324   else
4325     HandleInfoScreen_Main(mx, my, dx, dy, button);
4326 }
4327
4328
4329 // ============================================================================
4330 // type name functions
4331 // ============================================================================
4332
4333 static TreeInfo *type_name_node = NULL;
4334 static char type_name_last[MAX_PLAYER_NAME_LEN + 1] = { 0 };
4335 static int type_name_nr = 0;
4336
4337 static int getPlayerNameColor(char *name)
4338 {
4339   return (strEqual(name, EMPTY_PLAYER_NAME) ? FC_BLUE : FC_RED);
4340 }
4341
4342 static void drawTypeNameText(char *name, struct TextPosInfo *pos,
4343                              boolean active)
4344 {
4345   char text[MAX_PLAYER_NAME_LEN + 2] = { 0 };
4346   boolean multiple_users = (game_status == GAME_MODE_PSEUDO_TYPENAMES);
4347   int sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos));
4348   int sy = (multiple_users ? amSY + pos->y : mSY + ALIGNED_TEXT_YPOS(pos));
4349   int font_nr = (active ? FONT_ACTIVE(pos->font) : pos->font);
4350   int font_width = getFontWidth(font_nr);
4351   int font_xoffset = getFontDrawOffsetX(font_nr);
4352   int font_yoffset = getFontDrawOffsetY(font_nr);
4353   int font_sx = sx + font_xoffset;
4354   int font_sy = sy + font_yoffset;
4355
4356   DrawBackgroundForFont(font_sx, font_sy, pos->width, pos->height, font_nr);
4357
4358   sprintf(text, "%s%c", name, (active ? '_' : '\0'));
4359
4360   pos->width = strlen(text) * font_width;
4361   sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos));
4362
4363   DrawText(sx, sy, text, font_nr);
4364 }
4365
4366 static void getTypeNameValues(char *name, struct TextPosInfo *pos, int *xpos)
4367 {
4368   struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
4369
4370   *pos = *mci->pos_input;
4371
4372   if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
4373   {
4374     TreeInfo *ti = player_name_current;
4375     int first_entry = ti->cl_first;
4376     int entry_pos = first_entry + ti->cl_cursor;
4377     TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
4378     int xpos = MENU_SCREEN_START_XPOS;
4379     int ypos = MENU_SCREEN_START_YPOS + ti->cl_cursor;
4380
4381     type_name_node = getTreeInfoFromPos(node_first, entry_pos);
4382     type_name_nr = entry_pos;
4383
4384     strcpy(name, type_name_node->name);
4385
4386     pos->x = xpos * 32;
4387     pos->y = ypos * 32;
4388     pos->width = MAX_PLAYER_NAME_LEN * 32;
4389   }
4390   else
4391   {
4392     type_name_nr = user.nr;
4393
4394     strcpy(name, setup.player_name);
4395   }
4396
4397   strcpy(type_name_last, name);
4398
4399   if (strEqual(name, EMPTY_PLAYER_NAME))
4400     strcpy(name, "");
4401
4402   *xpos = strlen(name);
4403 }
4404
4405 static void setTypeNameValues_Name(char *name, struct TextPosInfo *pos)
4406 {
4407   // change name of edited user in global list of user names
4408   setString(&global.user_names[type_name_nr], name);
4409
4410   if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
4411   {
4412     TreeInfo *node = type_name_node;
4413
4414     // change name of edited user in local menu tree structure
4415     setString(&node->name, name);
4416     setString(&node->name_sorting, name);
4417
4418     node->color = getPlayerNameColor(name);
4419     pos->font = MENU_CHOOSE_TREE_FONT(node->color);
4420   }
4421 }
4422
4423 static void setTypeNameValues(char *name, struct TextPosInfo *pos,
4424                               boolean changed)
4425 {
4426   boolean reset_setup = strEqual(name, "");
4427   boolean remove_user = strEqual(name, EMPTY_PLAYER_NAME);
4428   boolean create_user = strEqual(type_name_last, EMPTY_PLAYER_NAME);
4429
4430   if (!changed)
4431     strcpy(name, type_name_last);
4432
4433   if (strEqual(name, ""))
4434     strcpy(name, EMPTY_PLAYER_NAME);
4435
4436   setTypeNameValues_Name(name, pos);
4437
4438   // if player name not changed, no further action required
4439   if (strEqual(name, type_name_last))
4440     return;
4441
4442   // redraw player name before (possibly) opening request dialogs
4443   drawTypeNameText(name, pos, FALSE);
4444
4445   int last_user_nr = user.nr;
4446
4447   if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
4448   {
4449     // save setup of currently active user (may differ from edited user)
4450     SaveSetup();
4451
4452     // temporarily change active user to edited user
4453     user.nr = type_name_nr;
4454
4455     if (create_user &&
4456         Request("Use current setup values for the new player?", REQ_ASK))
4457     {
4458       // use current setup values for new user, but create new player UUID
4459       setup.player_uuid = getStringCopy(getUUID());
4460     }
4461     else
4462     {
4463       // load setup for existing user (or start with defaults for new user)
4464       LoadSetup();
4465     }
4466   }
4467
4468   char *setup_filename = getSetupFilename();
4469   boolean setup_exists = fileExists(setup_filename);
4470
4471   // change name of edited user in setup structure
4472   strcpy(setup.player_name, name);
4473
4474   // save setup of edited user
4475   SaveSetup();
4476
4477   // change name of edited user on score server
4478   ApiRenamePlayerAsThread();
4479
4480   if (game_status == GAME_MODE_PSEUDO_TYPENAMES || reset_setup)
4481   {
4482     if (reset_setup)
4483     {
4484       if (Request("Reset setup values for this player?", REQ_ASK))
4485       {
4486         // remove setup config file
4487         unlink(setup_filename);
4488
4489         // set player name to default player name
4490         LoadSetup();
4491
4492         // update player name used by name typing functions
4493         strcpy(name, setup.player_name);
4494
4495         setTypeNameValues_Name(name, pos);
4496       }
4497     }
4498     else if (remove_user && type_name_nr != 0)
4499     {
4500       if (Request("Remove settings and tapes for deleted player?", REQ_ASK))
4501       {
4502         char *user_dir = getUserGameDataDir();
4503         char *user_dir_removed =
4504           getStringCat3WithSeparator(user_dir, "REMOVED",
4505                                      getCurrentTimestamp(), ".");
4506
4507         if (rename(user_dir, user_dir_removed) != 0)
4508           Request("Removing settings and tapes failed!", REQ_CONFIRM);
4509
4510         checked_free(user_dir_removed);
4511       }
4512     }
4513     else if (create_user && type_name_nr != 0 && !setup_exists)
4514     {
4515       if (Request("Create empty level set for the new player?", REQ_ASK))
4516       {
4517         char *levelset_subdir = getNewUserLevelSubdir();
4518
4519         if (CreateUserLevelSet(levelset_subdir, name, name, 100, FALSE))
4520         {
4521           AddUserLevelSetToLevelInfo(levelset_subdir);
4522
4523           LevelDirTree *leveldir_current_last = leveldir_current;
4524
4525           leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
4526                                                        levelset_subdir);
4527
4528           // set level number of newly created level set to default value
4529           LoadLevelSetup_SeriesInfo();
4530
4531           // set newly created level set as current level set for new user
4532           SaveLevelSetup_LastSeries();
4533           SaveLevelSetup_SeriesInfo();
4534
4535           leveldir_current = leveldir_current_last;
4536         }
4537         else
4538         {
4539           Request("Creating new level set failed!", REQ_CONFIRM);
4540         }
4541       }
4542     }
4543
4544     // restore currently active user
4545     user.nr = last_user_nr;
4546
4547     // restore setup of currently active user
4548     LoadSetup();
4549
4550     // restore last level set of currently active user
4551     LoadLevelSetup_LastSeries();
4552     LoadLevelSetup_SeriesInfo();
4553   }
4554 }
4555
4556 static void HandleTypeNameExt(boolean initialize, Key key)
4557 {
4558   static struct TextPosInfo pos_name = { 0 };
4559   static char name[MAX_PLAYER_NAME_LEN + 1] = { 0 };
4560   static int xpos = 0;
4561   struct TextPosInfo *pos = &pos_name;
4562   char key_char = getValidConfigValueChar(getCharFromKey(key));
4563   boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
4564   boolean active = TRUE;
4565
4566   if (initialize)
4567   {
4568     getTypeNameValues(name, pos, &xpos);
4569
4570     int sx = mSX + ALIGNED_TEXT_XPOS(pos);
4571     int sy = mSY + ALIGNED_TEXT_YPOS(pos);
4572
4573     StartTextInput(sx, sy, pos->width, pos->height);
4574   }
4575   else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
4576   {
4577     name[xpos] = key_char;
4578     name[xpos + 1] = 0;
4579
4580     xpos++;
4581   }
4582   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
4583   {
4584     xpos--;
4585
4586     name[xpos] = 0;
4587   }
4588   else if (key == KSYM_Return || key == KSYM_Escape)
4589   {
4590     boolean changed = (key == KSYM_Return);
4591
4592     StopTextInput();
4593
4594     setTypeNameValues(name, pos, changed);
4595
4596     active = FALSE;
4597   }
4598
4599   drawTypeNameText(name, pos, active);
4600
4601   if (!active)
4602   {
4603     SetGameStatus(game_status_last_screen);
4604
4605     if (game_status == GAME_MODE_MAIN)
4606       InitializeMainControls();
4607   }
4608 }
4609
4610 static void DrawTypeName(void)
4611 {
4612   HandleTypeNameExt(TRUE, 0);
4613 }
4614
4615 void HandleTypeName(Key key)
4616 {
4617   HandleTypeNameExt(FALSE, key);
4618 }
4619
4620
4621 // ============================================================================
4622 // tree menu functions
4623 // ============================================================================
4624
4625 static int getAlignXOffsetFromTreeInfo(TreeInfo *ti)
4626 {
4627   if (game_status != GAME_MODE_SETUP ||
4628       DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
4629     return 0;
4630
4631   int max_text_size = 0;
4632   TreeInfo *node;
4633
4634   for (node = getTreeInfoFirstGroupEntry(ti); node != NULL; node = node->next)
4635     max_text_size = MAX(max_text_size, strlen(node->name));
4636
4637   int num_entries = numTreeInfoInGroup(ti);
4638   boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
4639   int font_nr = MENU_CHOOSE_TREE_FONT(FC_RED);
4640   int text_width = max_text_size * getFontWidth(font_nr);
4641   int button_width = SC_MENUBUTTON_XSIZE;
4642   int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
4643   int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
4644   int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].align;
4645   int x = ALIGNED_XPOS(0, screen_width, align) * -1;
4646   int align_xoffset_raw = ALIGNED_XPOS(x, button_width + text_width, align);
4647   int align_xoffset = MAX(0, align_xoffset_raw);
4648
4649   return align_xoffset;
4650 }
4651
4652 static int getAlignYOffsetFromTreeInfo(TreeInfo *ti)
4653 {
4654   if (game_status != GAME_MODE_SETUP ||
4655       DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
4656     return 0;
4657
4658   int num_entries = numTreeInfoInGroup(ti);
4659   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
4660   int font_nr = MENU_CHOOSE_TREE_FONT(FC_RED);
4661   int font_height = getFontHeight(font_nr);
4662   int text_height = font_height * num_page_entries;
4663   int page_height = font_height * NUM_MENU_ENTRIES_ON_SCREEN;
4664   int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].valign;
4665   int y = ALIGNED_YPOS(0, page_height, align) * -1;
4666   int align_yoffset_raw = ALIGNED_YPOS(y, text_height, align);
4667   int align_yoffset = MAX(0, align_yoffset_raw);
4668
4669   return align_yoffset;
4670 }
4671
4672 static void StartPlayingFromHallOfFame(void)
4673 {
4674   level_nr = scores.next_level_nr;
4675   LoadLevel(level_nr);
4676
4677   StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4678 }
4679
4680 static void DrawChooseTree(TreeInfo **ti_ptr)
4681 {
4682   int fade_mask = REDRAW_FIELD;
4683   boolean restart_music = (game_status != game_status_last_screen &&
4684                            game_status_last_screen != GAME_MODE_SCOREINFO);
4685
4686   scores.continue_on_return = (game_status == GAME_MODE_SCORES &&
4687                                game_status_last_screen == GAME_MODE_PLAYING);
4688
4689   if (CheckFadeAll())
4690     fade_mask = REDRAW_ALL;
4691
4692   if (*ti_ptr != NULL && strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
4693   {
4694     if (game_status == GAME_MODE_SETUP)
4695     {
4696       execSetupArtwork();
4697     }
4698     else if (game_status == GAME_MODE_SCORES && scores.continue_playing)
4699     {
4700       StartPlayingFromHallOfFame();
4701     }
4702     else
4703     {
4704       SetGameStatus(GAME_MODE_MAIN);
4705
4706       DrawMainMenu();
4707     }
4708
4709     return;
4710   }
4711
4712   UnmapAllGadgets();
4713
4714   FreeScreenGadgets();
4715   CreateScreenGadgets();
4716
4717   if (restart_music)
4718     FadeMenuSoundsAndMusic();
4719
4720   FadeOut(fade_mask);
4721
4722   // needed if different viewport properties defined for this screen
4723   ChangeViewportPropertiesIfNeeded();
4724
4725   if (game_status == GAME_MODE_NAMES)
4726     SetMainBackgroundImage(IMG_BACKGROUND_NAMES);
4727   else if (game_status == GAME_MODE_LEVELNR)
4728     SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
4729   else if (game_status == GAME_MODE_LEVELS)
4730     SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
4731   else if (game_status == GAME_MODE_SCORES)
4732     SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
4733
4734   ClearField();
4735
4736   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
4737
4738   // map gadgets for high score screen
4739   if (game_status == GAME_MODE_SCORES)
4740     MapScreenMenuGadgets(SCREEN_MASK_SCORES);
4741
4742   MapScreenTreeGadgets(*ti_ptr);
4743
4744   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
4745
4746   DrawMaskedBorder(fade_mask);
4747
4748   if (restart_music)
4749     PlayMenuSoundsAndMusic();
4750
4751   FadeIn(fade_mask);
4752 }
4753
4754 static int getChooseTreeFont(TreeInfo *node, boolean active)
4755 {
4756   if (game_status == GAME_MODE_SCORES)
4757     return (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
4758   else
4759     return MENU_CHOOSE_TREE_FONT(MENU_CHOOSE_TREE_COLOR(node, active));
4760 }
4761
4762 static void drawChooseTreeText(TreeInfo *ti, int y, boolean active)
4763 {
4764   int num_entries = numTreeInfoInGroup(ti);
4765   boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
4766   int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
4767   int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
4768   int first_entry = ti->cl_first;
4769   int entry_pos = first_entry + y;
4770   TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
4771   TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos);
4772   int font_nr = getChooseTreeFont(node, active);
4773   int font_xoffset = getFontDrawOffsetX(font_nr);
4774   int xpos = MENU_SCREEN_START_XPOS;
4775   int ypos = MENU_SCREEN_START_YPOS + y;
4776   int startdx = xpos * 32;
4777   int startdy = ypos * 32;
4778   int startx = amSX + startdx;
4779   int starty = amSY + startdy;
4780   int startx_text = startx + font_xoffset;
4781   int endx_text = amSX + screen_width;
4782   int max_text_size = endx_text - startx_text;
4783   int max_buffer_len = max_text_size / getFontWidth(font_nr);
4784   char buffer[max_buffer_len + 1];
4785
4786   if (game_status == GAME_MODE_SCORES && !node->parent_link)
4787   {
4788     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
4789     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
4790     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
4791     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
4792     int font_size_1 = getFontWidth(font_nr1);
4793     int font_size_3 = getFontWidth(font_nr3);
4794     int font_size_4 = getFontWidth(font_nr4);
4795     int text_size_1 = 4 * font_size_1;
4796     int text_size_4 = 5 * font_size_4;
4797     int border = amSX - SX + getFontDrawOffsetX(font_nr1);
4798     int dx1 = 0;
4799     int dx3 = text_size_1;
4800     int dx4 = SXSIZE - 2 * startdx - 2 * border - text_size_4;
4801     int num_dots = (dx4 - dx3) / font_size_3;
4802     int startx1 = startx + dx1;
4803     int startx3 = startx + dx3;
4804     int startx4 = startx + dx4;
4805     int pos = node->pos;
4806     char *pos_text = getHallOfFameRankText(pos, 3);
4807     int i;
4808
4809     // highlight all high score entries of the current player
4810     if (strEqual(scores.entry[pos].name, setup.player_name))
4811       font_nr2 = FONT_TEXT_2_ACTIVE;
4812
4813     DrawText(startx1, starty, pos_text, font_nr1);
4814
4815     for (i = 0; i < num_dots; i++)
4816       DrawText(startx3 + i * font_size_3, starty, ".", font_nr3);
4817
4818     if (!strEqual(scores.entry[pos].name, EMPTY_PLAYER_NAME))
4819       DrawText(startx3, starty, scores.entry[pos].name, font_nr2);
4820
4821     DrawText(startx4, starty, getHallOfFameScoreText(pos, 5), font_nr4);
4822   }
4823   else
4824   {
4825     strncpy(buffer, node->name, max_buffer_len);
4826     buffer[max_buffer_len] = '\0';
4827
4828     DrawText(startx, starty, buffer, font_nr);
4829   }
4830 }
4831
4832 static void drawChooseTreeHeadExt(int type, char *title_string)
4833 {
4834   int yoffset_sets = MENU_TITLE1_YPOS;
4835   int yoffset_setup = 16;
4836   int yoffset = (type == TREE_TYPE_SCORE_ENTRY ||
4837                  type == TREE_TYPE_LEVEL_DIR ||
4838                  type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup);
4839
4840   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
4841 }
4842
4843 static void drawChooseTreeHead(TreeInfo *ti)
4844 {
4845   drawChooseTreeHeadExt(ti->type, ti->infotext);
4846 }
4847
4848 static void drawChooseTreeList(TreeInfo *ti)
4849 {
4850   int first_entry = ti->cl_first;
4851   int num_entries = numTreeInfoInGroup(ti);
4852   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
4853   int i;
4854
4855   clearMenuListArea();
4856
4857   for (i = 0; i < num_page_entries; i++)
4858   {
4859     TreeInfo *node, *node_first;
4860     int entry_pos = first_entry + i;
4861
4862     node_first = getTreeInfoFirstGroupEntry(ti);
4863     node = getTreeInfoFromPos(node_first, entry_pos);
4864
4865     drawChooseTreeText(ti, i, FALSE);
4866
4867     if (node->parent_link)
4868       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
4869     else if (node->level_group)
4870       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
4871     else
4872       initCursor(i, IMG_MENU_BUTTON);
4873
4874     if (game_status == GAME_MODE_SCORES && node->pos == scores.last_added)
4875       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
4876
4877     if (game_status == GAME_MODE_NAMES)
4878       drawChooseTreeEdit(i, FALSE);
4879   }
4880
4881   redraw_mask |= REDRAW_FIELD;
4882 }
4883
4884 static void drawChooseTreeInfo(TreeInfo *ti)
4885 {
4886   int entry_pos = ti->cl_first + ti->cl_cursor;
4887   int last_redraw_mask = redraw_mask;
4888   int ypos = MENU_TITLE2_YPOS;
4889   int font_nr = FONT_TITLE_2;
4890   int x;
4891
4892   if (ti->type == TREE_TYPE_LEVEL_NR)
4893     DrawTextFCentered(ypos, font_nr, leveldir_current->name);
4894
4895   if (ti->type == TREE_TYPE_SCORE_ENTRY)
4896     DrawTextFCentered(ypos, font_nr, "HighScores of Level %d",
4897                       scores.last_level_nr);
4898
4899   if (ti->type != TREE_TYPE_LEVEL_DIR)
4900     return;
4901
4902   TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti);
4903   TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos);
4904
4905   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
4906
4907   if (node->parent_link)
4908     DrawTextFCentered(ypos, font_nr, "leave \"%s\"",
4909                       node->node_parent->name);
4910   else if (node->level_group)
4911     DrawTextFCentered(ypos, font_nr, "enter \"%s\"",
4912                       node->name);
4913   else if (ti->type == TREE_TYPE_LEVEL_DIR)
4914     DrawTextFCentered(ypos, font_nr, "%3d %s (%s)",
4915                       node->levels, (node->levels > 1 ? "levels" : "level"),
4916                       node->class_desc);
4917
4918   // let BackToFront() redraw only what is needed
4919   redraw_mask = last_redraw_mask;
4920   for (x = 0; x < SCR_FIELDX; x++)
4921     MarkTileDirty(x, 1);
4922 }
4923
4924 static void drawChooseTreeCursorAndText(TreeInfo *ti, boolean active)
4925 {
4926   drawChooseTreeCursor(ti->cl_cursor, active);
4927   drawChooseTreeText(ti, ti->cl_cursor, active);
4928 }
4929
4930 static void drawChooseTreeScreen(TreeInfo *ti)
4931 {
4932   drawChooseTreeHead(ti);
4933   drawChooseTreeList(ti);
4934   drawChooseTreeInfo(ti);
4935   drawChooseTreeCursorAndText(ti, TRUE);
4936
4937   AdjustChooseTreeScrollbar(ti, SCREEN_CTRL_ID_SCROLL_VERTICAL);
4938
4939   // scroll bar and buttons may just have been added after reloading scores
4940   if (game_status == GAME_MODE_SCORES)
4941     MapScreenTreeGadgets(ti);
4942 }
4943
4944 static TreeInfo *setHallOfFameActiveEntry(TreeInfo **ti_ptr)
4945 {
4946   int score_pos = scores.last_added;
4947
4948   if (game_status_last_screen == GAME_MODE_SCOREINFO)
4949     score_pos = scores.last_entry_nr;
4950
4951   // set current tree entry to last added score entry
4952   *ti_ptr = getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos));
4953
4954   // if that fails, set current tree entry to first entry (back link)
4955   if (*ti_ptr == NULL)
4956     *ti_ptr = score_entries->node_group;
4957
4958   int num_entries = numTreeInfoInGroup(*ti_ptr);
4959   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
4960   int pos_score = getPosFromTreeInfo(*ti_ptr);
4961   int pos_first_raw = pos_score - (num_page_entries + 1) / 2 + 1;
4962   int pos_first = MIN(MAX(0, pos_first_raw), num_entries - num_page_entries);
4963
4964   (*ti_ptr)->cl_first = pos_first;
4965   (*ti_ptr)->cl_cursor = pos_score - pos_first;
4966
4967   return *ti_ptr;
4968 }
4969
4970 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
4971                              TreeInfo **ti_ptr)
4972 {
4973   TreeInfo *ti = *ti_ptr;
4974   boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
4975   int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
4976   int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
4977   int sx1_edit_name = getChooseTreeEditXPosReal(POS_LEFT);
4978   int sx2_edit_name = getChooseTreeEditXPosReal(POS_RIGHT);
4979   int x = 0;
4980   int y = (ti != NULL ? ti->cl_cursor : 0);
4981   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4982   int num_entries = numTreeInfoInGroup(ti);
4983   int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
4984   boolean position_set_by_scrollbar = (dx == 999);
4985
4986   if (game_status == GAME_MODE_SCORES)
4987   {
4988     if (server_scores.updated)
4989     {
4990       // reload scores, using updated server score cache file
4991       LoadLocalAndServerScore(scores.last_level_nr, FALSE);
4992
4993       server_scores.updated = FALSE;
4994
4995       DrawHallOfFame_setScoreEntries();
4996
4997       ti = setHallOfFameActiveEntry(ti_ptr);
4998
4999       if (button != MB_MENU_INITIALIZE)
5000         drawChooseTreeScreen(ti);
5001     }
5002   }
5003
5004   if (button == MB_MENU_INITIALIZE)
5005   {
5006     int num_entries = numTreeInfoInGroup(ti);
5007     int entry_pos = getPosFromTreeInfo(ti);
5008
5009     align_xoffset = getAlignXOffsetFromTreeInfo(ti);
5010     align_yoffset = getAlignYOffsetFromTreeInfo(ti);
5011
5012     if (game_status == GAME_MODE_SCORES)
5013     {
5014       ti = setHallOfFameActiveEntry(ti_ptr);
5015     }
5016     else if (ti->cl_first == -1)
5017     {
5018       // only on initialization
5019       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
5020       ti->cl_cursor = entry_pos - ti->cl_first;
5021
5022     }
5023     else if (ti->cl_cursor >= num_page_entries ||
5024              (num_entries > num_page_entries &&
5025               num_entries - ti->cl_first < num_page_entries))
5026     {
5027       // only after change of list size (by custom graphic configuration)
5028       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
5029       ti->cl_cursor = entry_pos - ti->cl_first;
5030     }
5031
5032     if (position_set_by_scrollbar)
5033       ti->cl_first = dy;
5034
5035     drawChooseTreeScreen(ti);
5036
5037     return;
5038   }
5039   else if (button == MB_MENU_LEAVE)
5040   {
5041     if (game_status != GAME_MODE_SCORES)
5042       FadeSetLeaveMenu();
5043
5044     PlaySound(SND_MENU_ITEM_SELECTING);
5045
5046     if (ti->node_parent)
5047     {
5048       *ti_ptr = ti->node_parent;
5049       DrawChooseTree(ti_ptr);
5050     }
5051     else if (game_status == GAME_MODE_SETUP)
5052     {
5053       if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
5054           setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
5055           setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
5056           setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
5057         execSetupGame();
5058       else if (setup_mode == SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE ||
5059                setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64 ||
5060                setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV ||
5061                setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_ATARI ||
5062                setup_mode == SETUP_MODE_CHOOSE_BD_COLOR_TYPE)
5063         execSetupEngines();
5064       else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
5065                setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
5066                setup_mode == SETUP_MODE_CHOOSE_RENDERING ||
5067                setup_mode == SETUP_MODE_CHOOSE_VSYNC)
5068         execSetupGraphics();
5069       else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
5070                setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
5071                setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
5072         execSetupSound();
5073       else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
5074                setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
5075                setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
5076                setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
5077                setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
5078                setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
5079                setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
5080                setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
5081         execSetupTouch();
5082       else
5083         execSetupArtwork();
5084     }
5085     else
5086     {
5087       if (game_status == GAME_MODE_LEVELNR)
5088       {
5089         int new_level_nr = atoi(level_number_current->identifier);
5090
5091         HandleMainMenu_SelectLevel(0, 0, new_level_nr);
5092       }
5093
5094       SetGameStatus(GAME_MODE_MAIN);
5095
5096       DrawMainMenu();
5097     }
5098
5099     return;
5100   }
5101
5102 #if defined(PLATFORM_ANDROID)
5103   // directly continue when touching the screen after playing
5104   if ((mx || my) && scores.continue_on_return)
5105   {
5106     // ignore touch events until released
5107     mx = my = 0;
5108   }
5109 #endif
5110
5111   // any mouse click or cursor key stops leaving scores by "Return" key
5112   if ((mx || my || dx || dy) && scores.continue_on_return)
5113   {
5114     scores.continue_on_return = FALSE;
5115     level_nr = scores.last_level_nr;
5116     LoadLevel(level_nr);
5117   }
5118
5119   if (mx || my)         // mouse input
5120   {
5121     x = (mx - amSX) / 32;
5122     y = (my - amSY) / 32 - MENU_SCREEN_START_YPOS;
5123
5124     if (game_status == GAME_MODE_NAMES)
5125       drawChooseTreeEdit(ti->cl_cursor, FALSE);
5126   }
5127   else if (dx || dy)    // keyboard or scrollbar/scrollbutton input
5128   {
5129     // move cursor instead of scrolling when already at start/end of list
5130     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
5131       dy = -1;
5132     else if (dy == +1 * SCROLL_LINE &&
5133              ti->cl_first + num_page_entries == num_entries)
5134       dy = 1;
5135
5136     // handle scrolling screen one line or page
5137     if (ti->cl_cursor + dy < 0 ||
5138         ti->cl_cursor + dy > num_page_entries - 1)
5139     {
5140       boolean redraw = FALSE;
5141
5142       if (ABS(dy) == SCROLL_PAGE)
5143         step = num_page_entries - 1;
5144
5145       if (dy < 0 && ti->cl_first > 0)
5146       {
5147         // scroll page/line up
5148
5149         ti->cl_first -= step;
5150         if (ti->cl_first < 0)
5151           ti->cl_first = 0;
5152
5153         redraw = TRUE;
5154       }
5155       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
5156       {
5157         // scroll page/line down
5158
5159         ti->cl_first += step;
5160         if (ti->cl_first + num_page_entries > num_entries)
5161           ti->cl_first = MAX(0, num_entries - num_page_entries);
5162
5163         redraw = TRUE;
5164       }
5165
5166       if (redraw)
5167         drawChooseTreeScreen(ti);
5168
5169       return;
5170     }
5171
5172     // handle moving cursor one line
5173     y = ti->cl_cursor + dy;
5174   }
5175
5176   if (game_status == GAME_MODE_SCORES && ABS(dx) == 1)
5177   {
5178     HandleHallOfFame_SelectLevel(1, dx);
5179
5180     return;
5181   }
5182   else if (game_status == GAME_MODE_NAMES && dx == 1)
5183   {
5184     SetGameStatus(GAME_MODE_PSEUDO_TYPENAMES);
5185
5186     DrawTypeName();
5187
5188     return;
5189   }
5190   else if (dx == 1)
5191   {
5192     TreeInfo *node_first, *node_cursor;
5193     int entry_pos = ti->cl_first + y;
5194
5195     node_first = getTreeInfoFirstGroupEntry(ti);
5196     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
5197
5198     if (node_cursor->node_group)
5199     {
5200       FadeSetEnterMenu();
5201
5202       PlaySound(SND_MENU_ITEM_SELECTING);
5203
5204       node_cursor->cl_first = ti->cl_first;
5205       node_cursor->cl_cursor = ti->cl_cursor;
5206
5207       *ti_ptr = node_cursor->node_group;
5208       DrawChooseTree(ti_ptr);
5209
5210       return;
5211     }
5212   }
5213   else if ((dx == -1 || button == MB_MENU_CONTINUE) && ti->node_parent)
5214   {
5215     if (game_status != GAME_MODE_SCORES)
5216       FadeSetLeaveMenu();
5217
5218     PlaySound(SND_MENU_ITEM_SELECTING);
5219
5220     *ti_ptr = ti->node_parent;
5221     DrawChooseTree(ti_ptr);
5222
5223     return;
5224   }
5225
5226   if (!anyScrollbarGadgetActive() &&
5227       IN_VIS_MENU(x, y) &&
5228       mx < mx_right_border &&
5229       y >= 0 && y < num_page_entries)
5230   {
5231     if (button)
5232     {
5233       if (game_status == GAME_MODE_NAMES)
5234       {
5235         if (mx >= sx1_edit_name && mx <= sx2_edit_name)
5236           drawChooseTreeEdit(y, TRUE);
5237       }
5238
5239       if (y != ti->cl_cursor)
5240       {
5241         PlaySound(SND_MENU_ITEM_ACTIVATING);
5242
5243         drawChooseTreeCursorAndText(ti, FALSE);
5244
5245         ti->cl_cursor = y;
5246
5247         drawChooseTreeCursorAndText(ti, TRUE);
5248
5249         drawChooseTreeInfo(ti);
5250       }
5251       else if (dx < 0)
5252       {
5253         if (game_status == GAME_MODE_SETUP)
5254         {
5255           if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
5256               setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
5257               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
5258               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
5259             execSetupGame();
5260           else if (setup_mode == SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE ||
5261                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64 ||
5262                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV ||
5263                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_ATARI ||
5264                    setup_mode == SETUP_MODE_CHOOSE_BD_COLOR_TYPE)
5265             execSetupEngines();
5266           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
5267                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
5268                    setup_mode == SETUP_MODE_CHOOSE_RENDERING ||
5269                    setup_mode == SETUP_MODE_CHOOSE_VSYNC)
5270             execSetupGraphics();
5271           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
5272                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
5273                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
5274             execSetupSound();
5275           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
5276                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
5277                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
5278                    setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
5279                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
5280                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
5281                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
5282                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
5283             execSetupTouch();
5284           else
5285             execSetupArtwork();
5286         }
5287       }
5288     }
5289     else
5290     {
5291       TreeInfo *node_first, *node_cursor;
5292       int entry_pos = ti->cl_first + y;
5293
5294       PlaySound(SND_MENU_ITEM_SELECTING);
5295
5296       node_first = getTreeInfoFirstGroupEntry(ti);
5297       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
5298
5299       if (node_cursor->node_group)
5300       {
5301         FadeSetEnterMenu();
5302
5303         node_cursor->cl_first = ti->cl_first;
5304         node_cursor->cl_cursor = ti->cl_cursor;
5305
5306         *ti_ptr = node_cursor->node_group;
5307         DrawChooseTree(ti_ptr);
5308       }
5309       else if (node_cursor->parent_link)
5310       {
5311         if (game_status != GAME_MODE_SCORES)
5312           FadeSetLeaveMenu();
5313
5314         *ti_ptr = node_cursor->node_parent;
5315         DrawChooseTree(ti_ptr);
5316       }
5317       else
5318       {
5319         if (game_status != GAME_MODE_SCORES)
5320           FadeSetEnterMenu();
5321
5322         node_cursor->cl_first = ti->cl_first;
5323         node_cursor->cl_cursor = ti->cl_cursor;
5324
5325         *ti_ptr = node_cursor;
5326
5327         if (ti->type == TREE_TYPE_LEVEL_DIR)
5328         {
5329           LoadLevelSetup_SeriesInfo();
5330
5331           SaveLevelSetup_LastSeries();
5332           SaveLevelSetup_SeriesInfo();
5333           TapeErase();
5334         }
5335
5336         if (game_status == GAME_MODE_SETUP)
5337         {
5338           if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE ||
5339               setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
5340               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
5341               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
5342             execSetupGame();
5343           else if (setup_mode == SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE ||
5344                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64 ||
5345                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV ||
5346                    setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_ATARI ||
5347                    setup_mode == SETUP_MODE_CHOOSE_BD_COLOR_TYPE)
5348             execSetupEngines();
5349           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
5350                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
5351                    setup_mode == SETUP_MODE_CHOOSE_RENDERING ||
5352                    setup_mode == SETUP_MODE_CHOOSE_VSYNC)
5353             execSetupGraphics();
5354           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
5355                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
5356                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
5357             execSetupSound();
5358           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
5359                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
5360                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
5361                    setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
5362                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
5363                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
5364                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
5365                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
5366             execSetupTouch();
5367           else
5368             execSetupArtwork();
5369         }
5370         else
5371         {
5372           if (game_status == GAME_MODE_LEVELNR)
5373           {
5374             int new_level_nr = atoi(level_number_current->identifier);
5375
5376             HandleMainMenu_SelectLevel(0, 0, new_level_nr);
5377           }
5378           else if (game_status == GAME_MODE_LEVELS)
5379           {
5380             // store level set if chosen from "last played level set" menu
5381             StoreLastPlayedLevels(leveldir_current);
5382
5383             // store if level set chosen from "last played level set" menu
5384             SaveLevelSetup_LastSeries();
5385           }
5386           else if (game_status == GAME_MODE_NAMES)
5387           {
5388             if (mx >= sx1_edit_name && mx <= sx2_edit_name)
5389             {
5390               SetGameStatus(GAME_MODE_PSEUDO_TYPENAMES);
5391
5392               DrawTypeName();
5393
5394               return;
5395             }
5396
5397             // change active user to selected user
5398             user.nr = entry_pos;
5399
5400             // save number of new active user
5401             SaveUserSetup();
5402
5403             // load setup of new active user
5404             LoadSetup();
5405
5406             // load last level set of new active user
5407             LoadLevelSetup_LastSeries();
5408             LoadLevelSetup_SeriesInfo();
5409
5410             // update list of last played level sets
5411             UpdateLastPlayedLevels_TreeInfo();
5412
5413             TapeErase();
5414
5415             ToggleFullscreenIfNeeded();
5416             ChangeWindowScalingIfNeeded();
5417
5418             ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_GRAPHICS);
5419             ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_SOUNDS);
5420             ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_MUSIC);
5421           }
5422           else if (game_status == GAME_MODE_SCORES)
5423           {
5424             if (scores.continue_playing && scores.continue_on_return)
5425             {
5426               StartPlayingFromHallOfFame();
5427
5428               return;
5429             }
5430             else if (!scores.continue_on_return)
5431             {
5432               SetGameStatus(GAME_MODE_SCOREINFO);
5433
5434               DrawScoreInfo(node_cursor->pos);
5435
5436               return;
5437             }
5438           }
5439
5440           SetGameStatus(GAME_MODE_MAIN);
5441
5442           DrawMainMenu();
5443         }
5444       }
5445     }
5446   }
5447
5448   if (game_status == GAME_MODE_SCORES)
5449     PlayMenuSoundIfLoop();
5450 }
5451
5452 void DrawChoosePlayerName(void)
5453 {
5454   int i;
5455
5456   if (player_name != NULL)
5457   {
5458     freeTreeInfo(player_name);
5459
5460     player_name = NULL;
5461   }
5462
5463   for (i = 0; i < MAX_PLAYER_NAMES; i++)
5464   {
5465     TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_PLAYER_NAME);
5466     char identifier[32], name[MAX_PLAYER_NAME_LEN + 1];
5467     int value = i;
5468
5469     ti->node_top = &player_name;
5470     ti->sort_priority = 10000 + value;
5471     ti->color = getPlayerNameColor(global.user_names[i]);
5472
5473     snprintf(identifier, sizeof(identifier), "%d", value);
5474     snprintf(name, sizeof(name), "%s", global.user_names[i]);
5475
5476     setString(&ti->identifier, identifier);
5477     setString(&ti->name, name);
5478     setString(&ti->name_sorting, name);
5479
5480     pushTreeInfo(&player_name, ti);
5481   }
5482
5483   // sort player entries by player number
5484   sortTreeInfo(&player_name);
5485
5486   // set current player entry to selected player entry
5487   player_name_current =
5488     getTreeInfoFromIdentifier(player_name, i_to_a(user.nr));
5489
5490   // if that fails, set current player name to first available name
5491   if (player_name_current == NULL)
5492     player_name_current = player_name;
5493
5494   // set text size for main name input (also used on name selection screen)
5495   InitializeMainControls();
5496
5497   DrawChooseTree(&player_name_current);
5498 }
5499
5500 void HandleChoosePlayerName(int mx, int my, int dx, int dy, int button)
5501 {
5502   HandleChooseTree(mx, my, dx, dy, button, &player_name_current);
5503 }
5504
5505 void DrawChooseLevelSet(void)
5506 {
5507   DrawChooseTree(&leveldir_current);
5508 }
5509
5510 void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
5511 {
5512   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
5513 }
5514
5515 void DrawChooseLevelNr(void)
5516 {
5517   int i;
5518
5519   if (level_number != NULL)
5520   {
5521     freeTreeInfo(level_number);
5522
5523     level_number = NULL;
5524   }
5525
5526   for (i = leveldir_current->first_level; i <= leveldir_current->last_level;i++)
5527   {
5528     TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_LEVEL_NR);
5529     char identifier[32], name[64];
5530     int value = i;
5531
5532     // temporarily load level info to get level name
5533     LoadLevelInfoOnly(i);
5534
5535     ti->node_top = &level_number;
5536     ti->sort_priority = 10000 + value;
5537     ti->color = (level.no_level_file ? FC_BLUE :
5538                  LevelStats_getSolved(i) ? FC_GREEN :
5539                  LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
5540
5541     snprintf(identifier, sizeof(identifier), "%d", value);
5542     snprintf(name, sizeof(name), "%03d: %s", value,
5543              (level.no_level_file ? "(no file)" : level.name));
5544
5545     setString(&ti->identifier, identifier);
5546     setString(&ti->name, name);
5547     setString(&ti->name_sorting, name);
5548
5549     pushTreeInfo(&level_number, ti);
5550   }
5551
5552   // sort level number values to start with lowest level number
5553   sortTreeInfo(&level_number);
5554
5555   // set current level number to current level number
5556   level_number_current =
5557     getTreeInfoFromIdentifier(level_number, i_to_a(level_nr));
5558
5559   // if that also fails, set current level number to first available level
5560   if (level_number_current == NULL)
5561     level_number_current = level_number;
5562
5563   DrawChooseTree(&level_number_current);
5564 }
5565
5566 void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
5567 {
5568   HandleChooseTree(mx, my, dx, dy, button, &level_number_current);
5569 }
5570
5571 static void DrawHallOfFame_setScoreEntries(void)
5572 {
5573   int max_empty_entries = 10;   // at least show "top ten" list, if empty
5574   int max_visible_entries = NUM_MENU_ENTRIES_ON_SCREEN - 1;   // w/o back link
5575   int min_score_entries = MIN(max_empty_entries, max_visible_entries);
5576   int score_pos = (scores.last_added >= 0 ? scores.last_added : 0);
5577   int i;
5578
5579   if (score_entries != NULL)
5580   {
5581     freeTreeInfo(score_entries);
5582
5583     score_entries = NULL;
5584   }
5585
5586   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5587   {
5588     // do not add empty score entries if off-screen
5589     if (scores.entry[i].score == 0 &&
5590         scores.entry[i].time == 0 &&
5591         i >= min_score_entries)
5592       break;
5593
5594     TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_SCORE_ENTRY);
5595     char identifier[32], name[64];
5596     int value = i;
5597
5598     ti->node_top = &score_entries;
5599     ti->sort_priority = 10000 + value;
5600     ti->color = FC_YELLOW;
5601     ti->pos = i;
5602
5603     snprintf(identifier, sizeof(identifier), "%d", value);
5604     snprintf(name, sizeof(name), "%03d.", value + 1);
5605
5606     setString(&ti->identifier, identifier);
5607     setString(&ti->name, name);
5608     setString(&ti->name_sorting, name);
5609
5610     pushTreeInfo(&score_entries, ti);
5611   }
5612
5613   // sort score entries to start with highest score entry
5614   sortTreeInfo(&score_entries);
5615
5616   // add top tree node to create back link to main menu
5617   score_entries = addTopTreeInfoNode(score_entries);
5618
5619   // set current score entry to last added or highest score entry
5620   score_entry_current =
5621     getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos));
5622
5623   // if that fails, set current score entry to first valid score entry
5624   if (score_entry_current == NULL)
5625     score_entry_current = getFirstValidTreeInfoEntry(score_entries);
5626
5627   if (score_entries != NULL && scores.continue_playing)
5628     setString(&score_entries->node_group->name, BACKLINK_TEXT_NEXT);
5629 }
5630
5631 void DrawHallOfFame(int nr)
5632 {
5633   scores.last_level_nr = nr;
5634
5635   // (this is needed when called from GameEnd() after winning a game)
5636   KeyboardAutoRepeatOn();
5637
5638   // (this is needed when called from GameEnd() after winning a game)
5639   SetDrawDeactivationMask(REDRAW_NONE);
5640   SetDrawBackgroundMask(REDRAW_FIELD);
5641
5642   LoadLocalAndServerScore(scores.last_level_nr, TRUE);
5643
5644   DrawHallOfFame_setScoreEntries();
5645
5646   if (scores.last_added >= 0)
5647     SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW);
5648
5649   FadeSetEnterScreen();
5650
5651   DrawChooseTree(&score_entry_current);
5652 }
5653
5654 static char *getHallOfFameRankText(int nr, int size)
5655 {
5656   static char rank_text[10];
5657   boolean forced = (scores.force_last_added && nr == scores.last_added);
5658   char *rank_text_raw = (forced ? "???" : int2str(nr + 1, size));
5659
5660   sprintf(rank_text, "%s%s", rank_text_raw, (size > 0 || !forced ? "." : ""));
5661
5662   return rank_text;
5663 }
5664
5665 static char *getHallOfFameTimeText(int nr)
5666 {
5667   static char score_text[10];
5668   int time_seconds = scores.entry[nr].time / FRAMES_PER_SECOND;
5669   int mm = (time_seconds / 60) % 60;
5670   int ss = (time_seconds % 60);
5671
5672   sprintf(score_text, "%02d:%02d", mm, ss);     // show playing time
5673
5674   return score_text;
5675 }
5676
5677 static char *getHallOfFameScoreText(int nr, int size)
5678 {
5679   if (!level.rate_time_over_score)
5680     return int2str(scores.entry[nr].score, size);       // show normal score
5681   else if (level.use_step_counter)
5682     return int2str(scores.entry[nr].time, size);        // show number of steps
5683   else
5684     return getHallOfFameTimeText(nr);                   // show playing time
5685 }
5686
5687 static char *getHallOfFameTapeDateText(struct ScoreEntry *entry)
5688 {
5689   static char tape_date[MAX_ISO_DATE_LEN + 1];
5690   int i, j;
5691
5692   if (!strEqual(entry->tape_date, UNKNOWN_NAME) ||
5693       strEqual(entry->tape_basename, UNDEFINED_FILENAME))
5694     return entry->tape_date;
5695
5696   for (i = 0, j = 0; i < 8; i++, j++)
5697   {
5698     tape_date[j] = entry->tape_basename[i];
5699
5700     if (i == 3 || i == 5)
5701       tape_date[++j] = '-';
5702   }
5703
5704   tape_date[MAX_ISO_DATE_LEN] = '\0';
5705
5706   return tape_date;
5707 }
5708
5709 static void HandleHallOfFame_SelectLevel(int step, int direction)
5710 {
5711   int old_level_nr = scores.last_level_nr;
5712   int new_level_nr = old_level_nr + step * direction;
5713
5714   if (new_level_nr < leveldir_current->first_level)
5715     new_level_nr = leveldir_current->first_level;
5716   if (new_level_nr > leveldir_current->last_level)
5717     new_level_nr = leveldir_current->last_level;
5718
5719   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
5720     new_level_nr = leveldir_current->handicap_level;
5721
5722   if (new_level_nr != old_level_nr)
5723   {
5724     PlaySound(SND_MENU_ITEM_SELECTING);
5725
5726     scores.last_level_nr = level_nr = new_level_nr;
5727     scores.last_entry_nr = 0;
5728
5729     LoadLevel(level_nr);
5730     LoadLocalAndServerScore(level_nr, TRUE);
5731
5732     DrawHallOfFame_setScoreEntries();
5733
5734     if (game_status == GAME_MODE_SCORES)
5735     {
5736       // force remapping optional gadgets (especially scroll bar)
5737       UnmapScreenTreeGadgets();
5738
5739       // redraw complete high score screen, as sub-title has changed
5740       ClearField();
5741
5742       // redraw level selection buttons (which have just been erased)
5743       RedrawScreenMenuGadgets(SCREEN_MASK_SCORES);
5744
5745       HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, &score_entry_current);
5746     }
5747     else
5748     {
5749       DrawScoreInfo_Content(scores.last_entry_nr);
5750     }
5751
5752     SaveLevelSetup_SeriesInfo();
5753   }
5754 }
5755
5756 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
5757 {
5758   HandleChooseTree(mx, my, dx, dy, button, &score_entry_current);
5759 }
5760
5761 static void DrawScoreInfo_Content(int entry_nr)
5762 {
5763   struct ScoreEntry *entry = &scores.entry[entry_nr];
5764   char *pos_text = getHallOfFameRankText(entry_nr, 0);
5765   char *tape_date = getHallOfFameTapeDateText(entry);
5766   int font_head = MENU_INFO_FONT_HEAD;
5767   int font_text = MENU_INFO_FONT_TEXT;
5768   int font_foot = MENU_INFO_FONT_FOOT;
5769   int spacing_para = menu.paragraph_spacing[GAME_MODE_SCOREINFO];
5770   int spacing_line = menu.line_spacing[GAME_MODE_SCOREINFO];
5771   int spacing_left = menu.left_spacing[GAME_MODE_SCOREINFO];
5772   int spacing_top  = menu.top_spacing[GAME_MODE_SCOREINFO];
5773   int xstep = getFontWidth(font_text);
5774   int ystep_para = getMenuTextStep(spacing_para,  font_text);
5775   int ystep_line = getMenuTextStep(spacing_line,  font_text);
5776   int xstart  = mSX - SX + spacing_left;
5777   int ystart  = mSY - SY + spacing_top + getHeadlineSpacing();
5778   int ybottom = mSY - SY + SYSIZE - menu.bottom_spacing[GAME_MODE_SCOREINFO];
5779   int xstart1 = xstart + xstep;
5780   int xstart2 = xstart + xstep * 12;
5781   int select_x = SX + xstart1;
5782   int select_y1, select_y2;
5783   int play_x, play_y;
5784   int play_height = screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE]->height;
5785   boolean play_visible = !strEqual(tape_date, UNKNOWN_NAME);
5786   int font_width = getFontWidth(font_text);
5787   int font_height = getFontHeight(font_text);
5788   int tape_date_width = getTextWidth(tape_date, font_text);
5789   int pad_left = xstart2;
5790   int pad_right = menu.right_spacing[GAME_MODE_SCOREINFO];
5791   int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width;
5792   int max_lines_per_text = 5;
5793   int lines;
5794
5795   ClearField();
5796
5797   // redraw level selection buttons (which have just been erased)
5798   RedrawScreenMenuGadgets(SCREEN_MASK_SCORES);
5799
5800   drawChooseTreeHead(score_entries);
5801   drawChooseTreeInfo(score_entries);
5802
5803   DrawTextF(xstart1, ystart, font_head, "Level Set");
5804   lines = DrawTextBufferS(xstart2, ystart, leveldir_current->name, font_text,
5805                           max_chars_per_line, -1, max_lines_per_text, 0, -1,
5806                           TRUE, FALSE, FALSE);
5807   ystart += ystep_line + (lines > 0 ? lines - 1 : 0) * font_height;
5808
5809   DrawTextF(xstart1, ystart, font_head, "Level");
5810   lines = DrawTextBufferS(xstart2, ystart, level.name, font_text,
5811                           max_chars_per_line, -1, max_lines_per_text, 0, -1,
5812                           TRUE, FALSE, FALSE);
5813   ystart += ystep_para + (lines > 0 ? lines - 1 : 0) * font_height;
5814
5815   select_y1 = SY + ystart;
5816   ystart += graphic_info[IMG_MENU_BUTTON_PREV_SCORE].height;
5817
5818   DrawTextF(xstart1, ystart, font_head, "Rank");
5819   DrawTextF(xstart2, ystart, font_text, pos_text);
5820   ystart += ystep_line;
5821
5822   DrawTextF(xstart1, ystart, font_head, "Player");
5823   DrawTextF(xstart2, ystart, font_text, entry->name);
5824   ystart += ystep_line;
5825
5826   if (level.use_step_counter)
5827   {
5828     DrawTextF(xstart1, ystart, font_head, "Steps");
5829     DrawTextF(xstart2, ystart, font_text, int2str(entry->time, 5));
5830     ystart += ystep_line;
5831   }
5832   else
5833   {
5834     DrawTextF(xstart1, ystart, font_head, "Time");
5835     DrawTextF(xstart2, ystart, font_text, getHallOfFameTimeText(entry_nr));
5836     ystart += ystep_line;
5837   }
5838
5839   if (!level.rate_time_over_score || entry->score > 0)
5840   {
5841     DrawTextF(xstart1, ystart, font_head, "Score");
5842     DrawTextF(xstart2, ystart, font_text, int2str(entry->score, 5));
5843     ystart += ystep_line;
5844   }
5845
5846   ystart += ystep_line;
5847
5848   play_x = SX + xstart2 + tape_date_width + font_width;
5849   play_y = SY + ystart + (font_height - play_height) / 2;
5850
5851   DrawTextF(xstart1, ystart, font_head, "Tape Date");
5852   DrawTextF(xstart2, ystart, font_text, tape_date);
5853   ystart += ystep_line;
5854
5855   DrawTextF(xstart1, ystart, font_head, "Platform");
5856   DrawTextF(xstart2, ystart, font_text, entry->platform);
5857   ystart += ystep_line;
5858
5859   DrawTextF(xstart1, ystart, font_head, "Version");
5860   DrawTextF(xstart2, ystart, font_text, entry->version);
5861   ystart += ystep_line;
5862
5863   DrawTextF(xstart1, ystart, font_head, "Country");
5864   lines = DrawTextBufferS(xstart2, ystart, entry->country_name, font_text,
5865                           max_chars_per_line, -1, max_lines_per_text, 0, -1,
5866                           TRUE, FALSE, FALSE);
5867   ystart += ystep_line;
5868
5869   select_y2 = SY + ystart;
5870
5871   DrawTextSCentered(ybottom, font_foot, "Press any key or button to go back");
5872
5873   AdjustScoreInfoButtons_SelectScore(select_x, select_y1, select_y2);
5874   AdjustScoreInfoButtons_PlayTape(play_x, play_y, play_visible);
5875 }
5876
5877 static void DrawScoreInfo(int entry_nr)
5878 {
5879   scores.last_entry_nr = entry_nr;
5880   score_info_tape_play = FALSE;
5881
5882   UnmapAllGadgets();
5883
5884   FreeScreenGadgets();
5885   CreateScreenGadgets();
5886
5887   FadeOut(REDRAW_FIELD);
5888
5889   // needed if different viewport properties defined after playing score tape
5890   ChangeViewportPropertiesIfNeeded();
5891
5892   // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it)
5893   SetDrawDeactivationMask(REDRAW_NONE);
5894   SetDrawBackgroundMask(REDRAW_FIELD);
5895
5896   // needed if different background image defined after playing score tape
5897   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
5898   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_SCOREINFO);
5899
5900   // special compatibility handling for "Snake Bite" graphics set
5901   if (strPrefix(leveldir_current->identifier, "snake_bite"))
5902     ClearRectangle(gfx.background_bitmap, gfx.real_sx, gfx.real_sy + 64,
5903                    gfx.full_sxsize, gfx.full_sysize - 64);
5904
5905   DrawScoreInfo_Content(entry_nr);
5906
5907   // map gadgets for score info screen
5908   MapScreenMenuGadgets(SCREEN_MASK_SCORES_INFO);
5909
5910   FadeIn(REDRAW_FIELD);
5911 }
5912
5913 static void HandleScoreInfo_SelectScore(int step, int direction)
5914 {
5915   int old_entry_nr = scores.last_entry_nr;
5916   int new_entry_nr = old_entry_nr + step * direction;
5917   int num_nodes = numTreeInfoInGroup(score_entry_current);
5918   int num_entries = num_nodes - 1;      // score nodes only, without back link
5919
5920   if (new_entry_nr < 0)
5921     new_entry_nr = 0;
5922   if (new_entry_nr > num_entries - 1)
5923     new_entry_nr = num_entries - 1;
5924
5925   if (new_entry_nr != old_entry_nr)
5926   {
5927     scores.last_entry_nr = new_entry_nr;
5928
5929     DrawScoreInfo_Content(new_entry_nr);
5930   }
5931 }
5932
5933 static void HandleScoreInfo_PlayTape(void)
5934 {
5935   if (!PlayScoreTape(scores.last_entry_nr))
5936   {
5937     DrawScoreInfo_Content(scores.last_entry_nr);
5938
5939     FadeIn(REDRAW_FIELD);
5940   }
5941 }
5942
5943 void HandleScoreInfo(int mx, int my, int dx, int dy, int button)
5944 {
5945   boolean button_action = (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE);
5946   boolean button_is_valid = (mx >= 0 && my >= 0);
5947   boolean button_screen_clicked = (button_action && button_is_valid);
5948
5949   if (server_scores.updated)
5950   {
5951     // reload scores, using updated server score cache file
5952     LoadLocalAndServerScore(scores.last_level_nr, FALSE);
5953
5954     server_scores.updated = FALSE;
5955
5956     DrawHallOfFame_setScoreEntries();
5957
5958     DrawScoreInfo_Content(scores.last_entry_nr);
5959   }
5960
5961   if (button_screen_clicked)
5962   {
5963     PlaySound(SND_MENU_ITEM_SELECTING);
5964
5965     SetGameStatus(GAME_MODE_SCORES);
5966
5967     DrawHallOfFame(scores.last_level_nr);
5968   }
5969   else if (dx)
5970   {
5971     HandleHallOfFame_SelectLevel(1, SIGN(dx) * (ABS(dx) > 1 ? 10 : 1));
5972   }
5973   else if (dy)
5974   {
5975     HandleScoreInfo_SelectScore(1, SIGN(dy) * (ABS(dy) > 1 ? 10 : 1));
5976   }
5977 }
5978
5979
5980 // ============================================================================
5981 // setup screen functions
5982 // ============================================================================
5983
5984 static struct TokenInfo *setup_info;
5985 static int num_setup_info;      // number of setup entries shown on screen
5986 static int max_setup_info;      // total number of setup entries in list
5987
5988 static char *window_size_text;
5989 static char *scaling_type_text;
5990 static char *rendering_mode_text;
5991 static char *vsync_mode_text;
5992 static char *scroll_delay_text;
5993 static char *snapshot_mode_text;
5994 static char *game_engine_type_text;
5995 static char *bd_palette_c64_text;
5996 static char *bd_palette_c64dtv_text;
5997 static char *bd_palette_atari_text;
5998 static char *bd_color_type_text;
5999 static char *game_speed_text;
6000 static char *scores_type_text;
6001 static char *network_server_text;
6002 static char *graphics_set_name;
6003 static char *sounds_set_name;
6004 static char *music_set_name;
6005 static char *volume_simple_text;
6006 static char *volume_loops_text;
6007 static char *volume_music_text;
6008 static char *touch_controls_text;
6009 static char *move_distance_text;
6010 static char *drop_distance_text;
6011 static char *transparency_text;
6012 static char *grid_size_text[2][2];
6013
6014 static void execSetupMain(void)
6015 {
6016   setup_mode = SETUP_MODE_MAIN;
6017
6018   DrawSetupScreen();
6019 }
6020
6021 static void execSetupGame_setScoresType(void)
6022 {
6023   if (scores_types == NULL)
6024   {
6025     int i;
6026
6027     for (i = 0; scores_types_list[i].value != NULL; i++)
6028     {
6029       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6030       char identifier[32], name[32];
6031       char *value = scores_types_list[i].value;
6032       char *text = scores_types_list[i].text;
6033
6034       ti->node_top = &scores_types;
6035       ti->sort_priority = i;
6036
6037       sprintf(identifier, "%s", value);
6038       sprintf(name, "%s", text);
6039
6040       setString(&ti->identifier, identifier);
6041       setString(&ti->name, name);
6042       setString(&ti->name_sorting, name);
6043       setString(&ti->infotext, STR_SETUP_CHOOSE_SCORES_TYPE);
6044
6045       pushTreeInfo(&scores_types, ti);
6046     }
6047
6048     // sort scores type values to start with lowest scores type value
6049     sortTreeInfo(&scores_types);
6050
6051     // set current scores type value to configured scores type value
6052     scores_type_current =
6053       getTreeInfoFromIdentifier(scores_types, setup.scores_in_highscore_list);
6054
6055     // if that fails, set current scores type to reliable default value
6056     if (scores_type_current == NULL)
6057       scores_type_current =
6058         getTreeInfoFromIdentifier(scores_types, STR_SCORES_TYPE_DEFAULT);
6059
6060     // if that also fails, set current scores type to first available value
6061     if (scores_type_current == NULL)
6062       scores_type_current = scores_types;
6063   }
6064
6065   setup.scores_in_highscore_list = scores_type_current->identifier;
6066
6067   // needed for displaying scores type text instead of identifier
6068   scores_type_text = scores_type_current->name;
6069 }
6070
6071 static void execSetupGame_setGameSpeeds(boolean update_value)
6072 {
6073   if (setup.game_speed_extended)
6074   {
6075     game_speeds_list = game_speeds_list_extended;
6076     game_speeds      = game_speeds_extended;
6077   }
6078   else
6079   {
6080     game_speeds_list = game_speeds_list_normal;
6081     game_speeds      = game_speeds_normal;
6082   }
6083
6084   if (game_speeds == NULL)
6085   {
6086     int i;
6087
6088     for (i = 0; game_speeds_list[i].value != -1; i++)
6089     {
6090       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6091       char identifier[32], name[32];
6092       int value = game_speeds_list[i].value;
6093       char *text = game_speeds_list[i].text;
6094
6095       ti->node_top = &game_speeds;
6096       ti->sort_priority = 10000 - value;
6097
6098       sprintf(identifier, "%d", value);
6099       sprintf(name, "%s", text);
6100
6101       setString(&ti->identifier, identifier);
6102       setString(&ti->name, name);
6103       setString(&ti->name_sorting, name);
6104       setString(&ti->infotext, STR_SETUP_CHOOSE_GAME_SPEED);
6105
6106       pushTreeInfo(&game_speeds, ti);
6107     }
6108
6109     // sort game speed values to start with slowest game speed
6110     sortTreeInfo(&game_speeds);
6111
6112     update_value = TRUE;
6113   }
6114
6115   if (update_value)
6116   {
6117     // set current game speed to configured game speed value
6118     game_speed_current =
6119       getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
6120
6121     // if that fails, set current game speed to reliable default value
6122     if (game_speed_current == NULL)
6123       game_speed_current =
6124         getTreeInfoFromIdentifier(game_speeds, i_to_a(GAME_FRAME_DELAY));
6125
6126     // if that also fails, set current game speed to first available speed
6127     if (game_speed_current == NULL)
6128       game_speed_current = game_speeds;
6129
6130     if (setup.game_speed_extended)
6131       game_speeds_extended = game_speeds;
6132     else
6133       game_speeds_normal = game_speeds;
6134   }
6135
6136   setup.game_frame_delay = atoi(game_speed_current->identifier);
6137
6138   // needed for displaying game speed text instead of identifier
6139   game_speed_text = game_speed_current->name;
6140 }
6141
6142 static void execSetupGame_setScrollDelays(void)
6143 {
6144   if (scroll_delays == NULL)
6145   {
6146     int i;
6147
6148     for (i = 0; scroll_delays_list[i].value != -1; i++)
6149     {
6150       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6151       char identifier[32], name[32];
6152       int value = scroll_delays_list[i].value;
6153       char *text = scroll_delays_list[i].text;
6154
6155       ti->node_top = &scroll_delays;
6156       ti->sort_priority = value;
6157
6158       sprintf(identifier, "%d", value);
6159       sprintf(name, "%s", text);
6160
6161       setString(&ti->identifier, identifier);
6162       setString(&ti->name, name);
6163       setString(&ti->name_sorting, name);
6164       setString(&ti->infotext, STR_SETUP_CHOOSE_SCROLL_DELAY);
6165
6166       pushTreeInfo(&scroll_delays, ti);
6167     }
6168
6169     // sort scroll delay values to start with lowest scroll delay value
6170     sortTreeInfo(&scroll_delays);
6171
6172     // set current scroll delay value to configured scroll delay value
6173     scroll_delay_current =
6174       getTreeInfoFromIdentifier(scroll_delays, i_to_a(setup.scroll_delay_value));
6175
6176     // if that fails, set current scroll delay to reliable default value
6177     if (scroll_delay_current == NULL)
6178       scroll_delay_current =
6179         getTreeInfoFromIdentifier(scroll_delays, i_to_a(STD_SCROLL_DELAY));
6180
6181     // if that also fails, set current scroll delay to first available value
6182     if (scroll_delay_current == NULL)
6183       scroll_delay_current = scroll_delays;
6184   }
6185
6186   setup.scroll_delay_value = atoi(scroll_delay_current->identifier);
6187
6188   // needed for displaying scroll delay text instead of identifier
6189   scroll_delay_text = scroll_delay_current->name;
6190 }
6191
6192 static void execSetupGame_setSnapshotModes(void)
6193 {
6194   if (snapshot_modes == NULL)
6195   {
6196     int i;
6197
6198     for (i = 0; snapshot_modes_list[i].value != NULL; i++)
6199     {
6200       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6201       char identifier[32], name[32];
6202       char *value = snapshot_modes_list[i].value;
6203       char *text = snapshot_modes_list[i].text;
6204
6205       ti->node_top = &snapshot_modes;
6206       ti->sort_priority = i;
6207
6208       sprintf(identifier, "%s", value);
6209       sprintf(name, "%s", text);
6210
6211       setString(&ti->identifier, identifier);
6212       setString(&ti->name, name);
6213       setString(&ti->name_sorting, name);
6214       setString(&ti->infotext, STR_SETUP_CHOOSE_SNAPSHOT_MODE);
6215
6216       pushTreeInfo(&snapshot_modes, ti);
6217     }
6218
6219     // sort snapshot mode values to start with lowest snapshot mode value
6220     sortTreeInfo(&snapshot_modes);
6221
6222     // set current snapshot mode value to configured snapshot mode value
6223     snapshot_mode_current =
6224       getTreeInfoFromIdentifier(snapshot_modes, setup.engine_snapshot_mode);
6225
6226     // if that fails, set current snapshot mode to reliable default value
6227     if (snapshot_mode_current == NULL)
6228       snapshot_mode_current =
6229         getTreeInfoFromIdentifier(snapshot_modes, STR_SNAPSHOT_MODE_DEFAULT);
6230
6231     // if that also fails, set current snapshot mode to first available value
6232     if (snapshot_mode_current == NULL)
6233       snapshot_mode_current = snapshot_modes;
6234   }
6235
6236   setup.engine_snapshot_mode = snapshot_mode_current->identifier;
6237
6238   // needed for displaying snapshot mode text instead of identifier
6239   snapshot_mode_text = snapshot_mode_current->name;
6240 }
6241
6242 static void execSetupGame_setNetworkServerText(void)
6243 {
6244   if (strEqual(setup.network_server_hostname, STR_NETWORK_AUTO_DETECT))
6245   {
6246     strcpy(network_server_hostname, STR_NETWORK_AUTO_DETECT_SETUP);
6247   }
6248   else
6249   {
6250     strncpy(network_server_hostname, setup.network_server_hostname,
6251             MAX_SETUP_TEXT_INPUT_LEN);
6252     network_server_hostname[MAX_SETUP_TEXT_INPUT_LEN] = '\0';
6253   }
6254
6255   // needed for displaying network server text instead of identifier
6256   network_server_text = network_server_hostname;
6257 }
6258
6259 static void execSetupGame(void)
6260 {
6261   boolean check_vsync_mode = (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED);
6262
6263   execSetupGame_setGameSpeeds(FALSE);
6264   execSetupGame_setScoresType();
6265   execSetupGame_setScrollDelays();
6266   execSetupGame_setSnapshotModes();
6267
6268   execSetupGame_setNetworkServerText();
6269
6270   if (!setup.provide_uploading_tapes)
6271     setHideSetupEntry(execOfferUploadTapes);
6272
6273   setup_mode = SETUP_MODE_GAME;
6274
6275   DrawSetupScreen();
6276
6277   // check if vsync needs to be disabled for this game speed to work
6278   if (check_vsync_mode)
6279     DisableVsyncIfNeeded();
6280 }
6281
6282 static void execSetupChooseScoresType(void)
6283 {
6284   setup_mode = SETUP_MODE_CHOOSE_SCORES_TYPE;
6285
6286   DrawSetupScreen();
6287 }
6288
6289 static void execSetupChooseGameSpeed(void)
6290 {
6291   setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED;
6292
6293   DrawSetupScreen();
6294 }
6295
6296 static void execSetupChooseScrollDelay(void)
6297 {
6298   setup_mode = SETUP_MODE_CHOOSE_SCROLL_DELAY;
6299
6300   DrawSetupScreen();
6301 }
6302
6303 static void execSetupChooseSnapshotMode(void)
6304 {
6305   setup_mode = SETUP_MODE_CHOOSE_SNAPSHOT_MODE;
6306
6307   DrawSetupScreen();
6308 }
6309
6310 static void execSetupEngines_setGameEngineType(void)
6311 {
6312   if (game_engine_types == NULL)
6313   {
6314     int i;
6315
6316     for (i = 0; game_engine_types_list[i].value != -1; i++)
6317     {
6318       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6319       char identifier[32], name[32];
6320       int value = game_engine_types_list[i].value;
6321       char *text = game_engine_types_list[i].text;
6322
6323       ti->node_top = &game_engine_types;
6324       ti->sort_priority = value;
6325
6326       sprintf(identifier, "%d", value);
6327       sprintf(name, "%s", text);
6328
6329       setString(&ti->identifier, identifier);
6330       setString(&ti->name, name);
6331       setString(&ti->name_sorting, name);
6332       setString(&ti->infotext, STR_SETUP_CHOOSE_GAME_ENGINE_TYPE);
6333
6334       pushTreeInfo(&game_engine_types, ti);
6335     }
6336
6337     // sort game engine type values to start with lowest game engine type value
6338     sortTreeInfo(&game_engine_types);
6339
6340     // set current game engine type value to configured game engine type value
6341     game_engine_type_current =
6342       getTreeInfoFromIdentifier(game_engine_types, i_to_a(setup.default_game_engine_type));
6343
6344     // if that fails, set current game engine type to reliable default value
6345     if (game_engine_type_current == NULL)
6346       game_engine_type_current =
6347         getTreeInfoFromIdentifier(game_engine_types, i_to_a(GAME_ENGINE_TYPE_RND));
6348
6349     // if that also fails, set current game engine type to first available value
6350     if (game_engine_type_current == NULL)
6351       game_engine_type_current = game_engine_types;
6352   }
6353
6354   setup.default_game_engine_type = atoi(game_engine_type_current->identifier);
6355
6356   // needed for displaying game engine type text instead of identifier
6357   game_engine_type_text = game_engine_type_current->name;
6358 }
6359
6360 static void execSetupEngines_setPalettesC64(void)
6361 {
6362   if (bd_palettes_c64 == NULL)
6363   {
6364     int i;
6365
6366     for (i = 0; bd_palettes_c64_list[i].value != -1; i++)
6367     {
6368       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6369       char identifier[32], name[32];
6370       int value = bd_palettes_c64_list[i].value;
6371       char *text = bd_palettes_c64_list[i].text;
6372
6373       ti->node_top = &bd_palettes_c64;
6374       ti->sort_priority = value;
6375
6376       sprintf(identifier, "%d", value);
6377       sprintf(name, "%s", text);
6378
6379       setString(&ti->identifier, identifier);
6380       setString(&ti->name, name);
6381       setString(&ti->name_sorting, name);
6382       setString(&ti->infotext, STR_SETUP_CHOOSE_BD_PALETTE_C64);
6383
6384       pushTreeInfo(&bd_palettes_c64, ti);
6385     }
6386
6387     // sort palette values to start with lowest palette value
6388     sortTreeInfo(&bd_palettes_c64);
6389
6390     // set current palette value to configured palette value
6391     bd_palette_c64_current =
6392       getTreeInfoFromIdentifier(bd_palettes_c64, i_to_a(setup.bd_palette_c64));
6393
6394     // if that fails, set current palette to reliable default value
6395     if (bd_palette_c64_current == NULL)
6396       bd_palette_c64_current =
6397         getTreeInfoFromIdentifier(bd_palettes_c64, i_to_a(GD_DEFAULT_PALETTE_C64));
6398
6399     // if that also fails, set current palette to first available value
6400     if (bd_palette_c64_current == NULL)
6401       bd_palette_c64_current = bd_palettes_c64;
6402   }
6403
6404   setup.bd_palette_c64 = atoi(bd_palette_c64_current->identifier);
6405
6406   // needed for displaying palette text instead of identifier
6407   bd_palette_c64_text = bd_palette_c64_current->name;
6408 }
6409
6410 static void execSetupEngines_setPalettesC64DTV(void)
6411 {
6412   if (bd_palettes_c64dtv == NULL)
6413   {
6414     int i;
6415
6416     for (i = 0; bd_palettes_c64dtv_list[i].value != -1; i++)
6417     {
6418       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6419       char identifier[32], name[32];
6420       int value = bd_palettes_c64dtv_list[i].value;
6421       char *text = bd_palettes_c64dtv_list[i].text;
6422
6423       ti->node_top = &bd_palettes_c64dtv;
6424       ti->sort_priority = value;
6425
6426       sprintf(identifier, "%d", value);
6427       sprintf(name, "%s", text);
6428
6429       setString(&ti->identifier, identifier);
6430       setString(&ti->name, name);
6431       setString(&ti->name_sorting, name);
6432       setString(&ti->infotext, STR_SETUP_CHOOSE_BD_PALETTE_C64DTV);
6433
6434       pushTreeInfo(&bd_palettes_c64dtv, ti);
6435     }
6436
6437     // sort palette values to start with lowest palette value
6438     sortTreeInfo(&bd_palettes_c64dtv);
6439
6440     // set current palette value to configured palette value
6441     bd_palette_c64dtv_current =
6442       getTreeInfoFromIdentifier(bd_palettes_c64dtv, i_to_a(setup.bd_palette_c64dtv));
6443
6444     // if that fails, set current palette to reliable default value
6445     if (bd_palette_c64dtv_current == NULL)
6446       bd_palette_c64dtv_current =
6447         getTreeInfoFromIdentifier(bd_palettes_c64dtv, i_to_a(GD_DEFAULT_PALETTE_C64DTV));
6448
6449     // if that also fails, set current palette to first available value
6450     if (bd_palette_c64dtv_current == NULL)
6451       bd_palette_c64dtv_current = bd_palettes_c64dtv;
6452   }
6453
6454   setup.bd_palette_c64dtv = atoi(bd_palette_c64dtv_current->identifier);
6455
6456   // needed for displaying palette text instead of identifier
6457   bd_palette_c64dtv_text = bd_palette_c64dtv_current->name;
6458 }
6459
6460 static void execSetupEngines_setPalettesAtari(void)
6461 {
6462   if (bd_palettes_atari == NULL)
6463   {
6464     int i;
6465
6466     for (i = 0; bd_palettes_atari_list[i].value != -1; i++)
6467     {
6468       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6469       char identifier[32], name[32];
6470       int value = bd_palettes_atari_list[i].value;
6471       char *text = bd_palettes_atari_list[i].text;
6472
6473       ti->node_top = &bd_palettes_atari;
6474       ti->sort_priority = value;
6475
6476       sprintf(identifier, "%d", value);
6477       sprintf(name, "%s", text);
6478
6479       setString(&ti->identifier, identifier);
6480       setString(&ti->name, name);
6481       setString(&ti->name_sorting, name);
6482       setString(&ti->infotext, STR_SETUP_CHOOSE_BD_PALETTE_ATARI);
6483
6484       pushTreeInfo(&bd_palettes_atari, ti);
6485     }
6486
6487     // sort palette values to start with lowest palette value
6488     sortTreeInfo(&bd_palettes_atari);
6489
6490     // set current palette value to configured palette value
6491     bd_palette_atari_current =
6492       getTreeInfoFromIdentifier(bd_palettes_atari, i_to_a(setup.bd_palette_atari));
6493
6494     // if that fails, set current palette to reliable default value
6495     if (bd_palette_atari_current == NULL)
6496       bd_palette_atari_current =
6497         getTreeInfoFromIdentifier(bd_palettes_atari, i_to_a(GD_DEFAULT_PALETTE_ATARI));
6498
6499     // if that also fails, set current palette to first available value
6500     if (bd_palette_atari_current == NULL)
6501       bd_palette_atari_current = bd_palettes_atari;
6502   }
6503
6504   setup.bd_palette_atari = atoi(bd_palette_atari_current->identifier);
6505
6506   // needed for displaying palette text instead of identifier
6507   bd_palette_atari_text = bd_palette_atari_current->name;
6508 }
6509
6510 static void execSetupEngines_setColorType(void)
6511 {
6512   if (bd_color_types == NULL)
6513   {
6514     int i;
6515
6516     for (i = 0; bd_color_types_list[i].value != -1; i++)
6517     {
6518       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6519       char identifier[32], name[32];
6520       int value = bd_color_types_list[i].value;
6521       char *text = bd_color_types_list[i].text;
6522
6523       ti->node_top = &bd_color_types;
6524       ti->sort_priority = value;
6525
6526       sprintf(identifier, "%d", value);
6527       sprintf(name, "%s", text);
6528
6529       setString(&ti->identifier, identifier);
6530       setString(&ti->name, name);
6531       setString(&ti->name_sorting, name);
6532       setString(&ti->infotext, STR_SETUP_CHOOSE_BD_COLOR_TYPE);
6533
6534       pushTreeInfo(&bd_color_types, ti);
6535     }
6536
6537     // sort color type values to start with lowest color type value
6538     sortTreeInfo(&bd_color_types);
6539
6540     // set current color type value to configured color type value
6541     bd_color_type_current =
6542       getTreeInfoFromIdentifier(bd_color_types, i_to_a(setup.bd_default_color_type));
6543
6544     // if that fails, set current color type to reliable default value
6545     if (bd_color_type_current == NULL)
6546       bd_color_type_current =
6547         getTreeInfoFromIdentifier(bd_color_types, i_to_a(GD_DEFAULT_COLOR_TYPE));
6548
6549     // if that also fails, set current color type to first available value
6550     if (bd_color_type_current == NULL)
6551       bd_color_type_current = bd_color_types;
6552   }
6553
6554   setup.bd_default_color_type = atoi(bd_color_type_current->identifier);
6555
6556   // needed for displaying color type text instead of identifier
6557   bd_color_type_text = bd_color_type_current->name;
6558 }
6559
6560 static void execSetupEngines(void)
6561 {
6562   setup_mode = SETUP_MODE_ENGINES;
6563
6564   execSetupEngines_setGameEngineType();
6565   execSetupEngines_setPalettesC64();
6566   execSetupEngines_setPalettesC64DTV();
6567   execSetupEngines_setPalettesAtari();
6568   execSetupEngines_setColorType();
6569
6570   DrawSetupScreen();
6571 }
6572
6573 static void execSetupChooseGameEngineType(void)
6574 {
6575   setup_mode = SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE;
6576
6577   DrawSetupScreen();
6578 }
6579
6580 static void execSetupChoosePaletteC64(void)
6581 {
6582   setup_mode = SETUP_MODE_CHOOSE_BD_PALETTE_C64;
6583
6584   DrawSetupScreen();
6585 }
6586
6587 static void execSetupChoosePaletteC64DTV(void)
6588 {
6589   setup_mode = SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV;
6590
6591   DrawSetupScreen();
6592 }
6593
6594 static void execSetupChoosePaletteAtari(void)
6595 {
6596   setup_mode = SETUP_MODE_CHOOSE_BD_PALETTE_ATARI;
6597
6598   DrawSetupScreen();
6599 }
6600
6601 static void execSetupChooseColorType(void)
6602 {
6603   setup_mode = SETUP_MODE_CHOOSE_BD_COLOR_TYPE;
6604
6605   DrawSetupScreen();
6606 }
6607
6608 static void execSetupEditor(void)
6609 {
6610   setup_mode = SETUP_MODE_EDITOR;
6611
6612   DrawSetupScreen();
6613 }
6614
6615 static void execSetupGraphics_setWindowSizes(boolean update_list)
6616 {
6617   if (window_sizes != NULL && update_list)
6618   {
6619     freeTreeInfo(window_sizes);
6620
6621     window_sizes = NULL;
6622   }
6623
6624   if (window_sizes == NULL)
6625   {
6626     boolean current_window_size_found = FALSE;
6627     int i;
6628
6629     for (i = 0; window_sizes_list[i].value != -1; i++)
6630     {
6631       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6632       char identifier[32], name[32];
6633       int value = window_sizes_list[i].value;
6634       char *text = window_sizes_list[i].text;
6635
6636       ti->node_top = &window_sizes;
6637       ti->sort_priority = value;
6638
6639       sprintf(identifier, "%d", value);
6640       sprintf(name, "%s", text);
6641
6642       setString(&ti->identifier, identifier);
6643       setString(&ti->name, name);
6644       setString(&ti->name_sorting, name);
6645       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
6646
6647       pushTreeInfo(&window_sizes, ti);
6648
6649       if (value == setup.window_scaling_percent)
6650         current_window_size_found = TRUE;
6651     }
6652
6653     if (!current_window_size_found)
6654     {
6655       // add entry for non-preset window scaling value
6656
6657       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6658       char identifier[32], name[32];
6659       int value = setup.window_scaling_percent;
6660
6661       ti->node_top = &window_sizes;
6662       ti->sort_priority = value;
6663
6664       sprintf(identifier, "%d", value);
6665       sprintf(name, "%d %% (Current)", value);
6666
6667       setString(&ti->identifier, identifier);
6668       setString(&ti->name, name);
6669       setString(&ti->name_sorting, name);
6670       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
6671
6672       pushTreeInfo(&window_sizes, ti);
6673     }
6674
6675     // sort window size values to start with lowest window size value
6676     sortTreeInfo(&window_sizes);
6677
6678     // set current window size value to configured window size value
6679     window_size_current =
6680       getTreeInfoFromIdentifier(window_sizes,
6681                                 i_to_a(setup.window_scaling_percent));
6682
6683     // if that fails, set current window size to reliable default value
6684     if (window_size_current == NULL)
6685       window_size_current =
6686         getTreeInfoFromIdentifier(window_sizes,
6687                                   i_to_a(STD_WINDOW_SCALING_PERCENT));
6688
6689     // if that also fails, set current window size to first available value
6690     if (window_size_current == NULL)
6691       window_size_current = window_sizes;
6692   }
6693
6694   setup.window_scaling_percent = atoi(window_size_current->identifier);
6695
6696   // needed for displaying window size text instead of identifier
6697   window_size_text = window_size_current->name;
6698 }
6699
6700 static void execSetupGraphics_setScalingTypes(void)
6701 {
6702   if (scaling_types == NULL)
6703   {
6704     int i;
6705
6706     for (i = 0; scaling_types_list[i].value != NULL; i++)
6707     {
6708       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6709       char identifier[32], name[32];
6710       char *value = scaling_types_list[i].value;
6711       char *text = scaling_types_list[i].text;
6712
6713       ti->node_top = &scaling_types;
6714       ti->sort_priority = i;
6715
6716       sprintf(identifier, "%s", value);
6717       sprintf(name, "%s", text);
6718
6719       setString(&ti->identifier, identifier);
6720       setString(&ti->name, name);
6721       setString(&ti->name_sorting, name);
6722       setString(&ti->infotext, STR_SETUP_CHOOSE_SCALING_TYPE);
6723
6724       pushTreeInfo(&scaling_types, ti);
6725     }
6726
6727     // sort scaling type values to start with lowest scaling type value
6728     sortTreeInfo(&scaling_types);
6729
6730     // set current scaling type value to configured scaling type value
6731     scaling_type_current =
6732       getTreeInfoFromIdentifier(scaling_types, setup.window_scaling_quality);
6733
6734     // if that fails, set current scaling type to reliable default value
6735     if (scaling_type_current == NULL)
6736       scaling_type_current =
6737         getTreeInfoFromIdentifier(scaling_types, SCALING_QUALITY_DEFAULT);
6738
6739     // if that also fails, set current scaling type to first available value
6740     if (scaling_type_current == NULL)
6741       scaling_type_current = scaling_types;
6742   }
6743
6744   setup.window_scaling_quality = scaling_type_current->identifier;
6745
6746   // needed for displaying scaling type text instead of identifier
6747   scaling_type_text = scaling_type_current->name;
6748 }
6749
6750 static void execSetupGraphics_setRenderingModes(void)
6751 {
6752   if (rendering_modes == NULL)
6753   {
6754     int i;
6755
6756     for (i = 0; rendering_modes_list[i].value != NULL; i++)
6757     {
6758       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6759       char identifier[32], name[32];
6760       char *value = rendering_modes_list[i].value;
6761       char *text = rendering_modes_list[i].text;
6762
6763       ti->node_top = &rendering_modes;
6764       ti->sort_priority = i;
6765
6766       sprintf(identifier, "%s", value);
6767       sprintf(name, "%s", text);
6768
6769       setString(&ti->identifier, identifier);
6770       setString(&ti->name, name);
6771       setString(&ti->name_sorting, name);
6772       setString(&ti->infotext, STR_SETUP_CHOOSE_RENDERING);
6773
6774       pushTreeInfo(&rendering_modes, ti);
6775     }
6776
6777     // sort rendering mode values to start with lowest rendering mode value
6778     sortTreeInfo(&rendering_modes);
6779
6780     // set current rendering mode value to configured rendering mode value
6781     rendering_mode_current =
6782       getTreeInfoFromIdentifier(rendering_modes, setup.screen_rendering_mode);
6783
6784     // if that fails, set current rendering mode to reliable default value
6785     if (rendering_mode_current == NULL)
6786       rendering_mode_current =
6787         getTreeInfoFromIdentifier(rendering_modes,
6788                                   STR_SPECIAL_RENDERING_DEFAULT);
6789
6790     // if that also fails, set current rendering mode to first available one
6791     if (rendering_mode_current == NULL)
6792       rendering_mode_current = rendering_modes;
6793   }
6794
6795   setup.screen_rendering_mode = rendering_mode_current->identifier;
6796
6797   // needed for displaying rendering mode text instead of identifier
6798   rendering_mode_text = rendering_mode_current->name;
6799 }
6800
6801 static void execSetupGraphics_setVsyncModes(boolean update_value)
6802 {
6803   if (vsync_modes == NULL)
6804   {
6805     int i;
6806
6807     for (i = 0; vsync_modes_list[i].value != NULL; i++)
6808     {
6809       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6810       char identifier[32], name[32];
6811       char *value = vsync_modes_list[i].value;
6812       char *text = vsync_modes_list[i].text;
6813
6814       ti->node_top = &vsync_modes;
6815       ti->sort_priority = i;
6816
6817       sprintf(identifier, "%s", value);
6818       sprintf(name, "%s", text);
6819
6820       setString(&ti->identifier, identifier);
6821       setString(&ti->name, name);
6822       setString(&ti->name_sorting, name);
6823       setString(&ti->infotext, STR_SETUP_CHOOSE_VSYNC);
6824
6825       pushTreeInfo(&vsync_modes, ti);
6826     }
6827
6828     // sort vsync mode values to start with lowest vsync mode value
6829     sortTreeInfo(&vsync_modes);
6830
6831     update_value = TRUE;
6832   }
6833
6834   if (update_value)
6835   {
6836     // set current vsync mode value to configured vsync mode value
6837     vsync_mode_current =
6838       getTreeInfoFromIdentifier(vsync_modes, setup.vsync_mode);
6839
6840     // if that fails, set current vsync mode to reliable default value
6841     if (vsync_mode_current == NULL)
6842       vsync_mode_current =
6843         getTreeInfoFromIdentifier(vsync_modes, STR_VSYNC_MODE_DEFAULT);
6844
6845     // if that also fails, set current vsync mode to first available one
6846     if (vsync_mode_current == NULL)
6847       vsync_mode_current = vsync_modes;
6848   }
6849
6850   setup.vsync_mode = vsync_mode_current->identifier;
6851
6852   // needed for displaying vsync mode text instead of identifier
6853   vsync_mode_text = vsync_mode_current->name;
6854 }
6855
6856 static void execSetupGraphics(void)
6857 {
6858   boolean check_game_speed = (setup_mode == SETUP_MODE_CHOOSE_VSYNC);
6859
6860   // update "setup.window_scaling_percent" from list selection
6861   // (in this case, window scaling was changed on setup screen)
6862   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
6863     execSetupGraphics_setWindowSizes(FALSE);
6864
6865   // update "setup.vsync_mode" from list selection
6866   // (in this case, vsync mode was changed on setup screen)
6867   if (setup_mode == SETUP_MODE_CHOOSE_VSYNC)
6868     execSetupGraphics_setVsyncModes(FALSE);
6869
6870   // update list selection from "setup.window_scaling_percent"
6871   // (window scaling may have changed by resizing the window)
6872   execSetupGraphics_setWindowSizes(TRUE);
6873
6874   // update list selection from "setup.vsync_mode"
6875   // (vsync_mode may have changed by re-creating the renderer)
6876   execSetupGraphics_setVsyncModes(TRUE);
6877
6878   execSetupGraphics_setScalingTypes();
6879   execSetupGraphics_setRenderingModes();
6880
6881   setup_mode = SETUP_MODE_GRAPHICS;
6882
6883   DrawSetupScreen();
6884
6885   // check if game speed is high enough for 60 Hz vsync to work
6886   if (check_game_speed)
6887     ModifyGameSpeedIfNeeded();
6888
6889   // window scaling may have changed at this point
6890   ChangeWindowScalingIfNeeded();
6891
6892   // window scaling quality may have changed at this point
6893   if (!strEqual(setup.window_scaling_quality, video.window_scaling_quality))
6894     SDLSetWindowScalingQuality(setup.window_scaling_quality);
6895
6896   // screen rendering mode may have changed at this point
6897   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
6898
6899   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
6900   int video_vsync_mode = video.vsync_mode;
6901
6902   // screen vsync mode may have changed at this point
6903   ChangeVsyncModeIfNeeded();
6904
6905   // check if setting vsync mode to selected value failed
6906   if (setup_vsync_mode != video_vsync_mode &&
6907       setup_vsync_mode != video.vsync_mode)
6908   {
6909     // changing vsync mode to selected value failed -- reset displayed value
6910     execSetupGraphics_setVsyncModes(TRUE);
6911
6912     Request("Setting VSync failed!", REQ_CONFIRM);
6913
6914     DrawSetupScreen();
6915   }
6916 }
6917
6918 static void execSetupChooseWindowSize(void)
6919 {
6920   setup_mode = SETUP_MODE_CHOOSE_WINDOW_SIZE;
6921
6922   DrawSetupScreen();
6923 }
6924
6925 static void execSetupChooseScalingType(void)
6926 {
6927   setup_mode = SETUP_MODE_CHOOSE_SCALING_TYPE;
6928
6929   DrawSetupScreen();
6930 }
6931
6932 static void execSetupChooseRenderingMode(void)
6933 {
6934   setup_mode = SETUP_MODE_CHOOSE_RENDERING;
6935
6936   DrawSetupScreen();
6937 }
6938
6939 static void execSetupChooseVsyncMode(void)
6940 {
6941   setup_mode = SETUP_MODE_CHOOSE_VSYNC;
6942
6943   DrawSetupScreen();
6944 }
6945
6946 static void execSetupChooseVolumeSimple(void)
6947 {
6948   setup_mode = SETUP_MODE_CHOOSE_VOLUME_SIMPLE;
6949
6950   DrawSetupScreen();
6951 }
6952
6953 static void execSetupChooseVolumeLoops(void)
6954 {
6955   setup_mode = SETUP_MODE_CHOOSE_VOLUME_LOOPS;
6956
6957   DrawSetupScreen();
6958 }
6959
6960 static void execSetupChooseVolumeMusic(void)
6961 {
6962   setup_mode = SETUP_MODE_CHOOSE_VOLUME_MUSIC;
6963
6964   DrawSetupScreen();
6965 }
6966
6967 static void execSetupSound(void)
6968 {
6969   if (volumes_simple == NULL)
6970   {
6971     boolean current_volume_simple_found = FALSE;
6972     int i;
6973
6974     for (i = 0; volumes_list[i].value != -1; i++)
6975     {
6976       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
6977       char identifier[32], name[32];
6978       int value = volumes_list[i].value;
6979       char *text = volumes_list[i].text;
6980
6981       ti->node_top = &volumes_simple;
6982       ti->sort_priority = value;
6983
6984       sprintf(identifier, "%d", value);
6985       sprintf(name, "%s", text);
6986
6987       setString(&ti->identifier, identifier);
6988       setString(&ti->name, name);
6989       setString(&ti->name_sorting, name);
6990       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
6991
6992       pushTreeInfo(&volumes_simple, ti);
6993
6994       if (value == setup.volume_simple)
6995         current_volume_simple_found = TRUE;
6996     }
6997
6998     if (!current_volume_simple_found)
6999     {
7000       // add entry for non-preset volume value
7001
7002       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7003       char identifier[32], name[32];
7004       int value = setup.volume_simple;
7005
7006       ti->node_top = &volumes_simple;
7007       ti->sort_priority = value;
7008
7009       sprintf(identifier, "%d", value);
7010       sprintf(name, "%d %% (Current)", value);
7011
7012       setString(&ti->identifier, identifier);
7013       setString(&ti->name, name);
7014       setString(&ti->name_sorting, name);
7015       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
7016
7017       pushTreeInfo(&volumes_simple, ti);
7018     }
7019
7020     // sort volume values to start with lowest volume value
7021     sortTreeInfo(&volumes_simple);
7022
7023     // set current volume value to configured volume value
7024     volume_simple_current =
7025       getTreeInfoFromIdentifier(volumes_simple, i_to_a(setup.volume_simple));
7026
7027     // if that fails, set current volume to reliable default value
7028     if (volume_simple_current == NULL)
7029       volume_simple_current =
7030         getTreeInfoFromIdentifier(volumes_simple, i_to_a(100));
7031
7032     // if that also fails, set current volume to first available value
7033     if (volume_simple_current == NULL)
7034       volume_simple_current = volumes_simple;
7035   }
7036
7037   if (volumes_loops == NULL)
7038   {
7039     boolean current_volume_loops_found = FALSE;
7040     int i;
7041
7042     for (i = 0; volumes_list[i].value != -1; i++)
7043     {
7044       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7045       char identifier[32], name[32];
7046       int value = volumes_list[i].value;
7047       char *text = volumes_list[i].text;
7048
7049       ti->node_top = &volumes_loops;
7050       ti->sort_priority = value;
7051
7052       sprintf(identifier, "%d", value);
7053       sprintf(name, "%s", text);
7054
7055       setString(&ti->identifier, identifier);
7056       setString(&ti->name, name);
7057       setString(&ti->name_sorting, name);
7058       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
7059
7060       pushTreeInfo(&volumes_loops, ti);
7061
7062       if (value == setup.volume_loops)
7063         current_volume_loops_found = TRUE;
7064     }
7065
7066     if (!current_volume_loops_found)
7067     {
7068       // add entry for non-preset volume value
7069
7070       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7071       char identifier[32], name[32];
7072       int value = setup.volume_loops;
7073
7074       ti->node_top = &volumes_loops;
7075       ti->sort_priority = value;
7076
7077       sprintf(identifier, "%d", value);
7078       sprintf(name, "%d %% (Current)", value);
7079
7080       setString(&ti->identifier, identifier);
7081       setString(&ti->name, name);
7082       setString(&ti->name_sorting, name);
7083       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
7084
7085       pushTreeInfo(&volumes_loops, ti);
7086     }
7087
7088     // sort volume values to start with lowest volume value
7089     sortTreeInfo(&volumes_loops);
7090
7091     // set current volume value to configured volume value
7092     volume_loops_current =
7093       getTreeInfoFromIdentifier(volumes_loops, i_to_a(setup.volume_loops));
7094
7095     // if that fails, set current volume to reliable default value
7096     if (volume_loops_current == NULL)
7097       volume_loops_current =
7098         getTreeInfoFromIdentifier(volumes_loops, i_to_a(100));
7099
7100     // if that also fails, set current volume to first available value
7101     if (volume_loops_current == NULL)
7102       volume_loops_current = volumes_loops;
7103   }
7104
7105   if (volumes_music == NULL)
7106   {
7107     boolean current_volume_music_found = FALSE;
7108     int i;
7109
7110     for (i = 0; volumes_list[i].value != -1; i++)
7111     {
7112       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7113       char identifier[32], name[32];
7114       int value = volumes_list[i].value;
7115       char *text = volumes_list[i].text;
7116
7117       ti->node_top = &volumes_music;
7118       ti->sort_priority = value;
7119
7120       sprintf(identifier, "%d", value);
7121       sprintf(name, "%s", text);
7122
7123       setString(&ti->identifier, identifier);
7124       setString(&ti->name, name);
7125       setString(&ti->name_sorting, name);
7126       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
7127
7128       pushTreeInfo(&volumes_music, ti);
7129
7130       if (value == setup.volume_music)
7131         current_volume_music_found = TRUE;
7132     }
7133
7134     if (!current_volume_music_found)
7135     {
7136       // add entry for non-preset volume value
7137
7138       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7139       char identifier[32], name[32];
7140       int value = setup.volume_music;
7141
7142       ti->node_top = &volumes_music;
7143       ti->sort_priority = value;
7144
7145       sprintf(identifier, "%d", value);
7146       sprintf(name, "%d %% (Current)", value);
7147
7148       setString(&ti->identifier, identifier);
7149       setString(&ti->name, name);
7150       setString(&ti->name_sorting, name);
7151       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
7152
7153       pushTreeInfo(&volumes_music, ti);
7154     }
7155
7156     // sort volume values to start with lowest volume value
7157     sortTreeInfo(&volumes_music);
7158
7159     // set current volume value to configured volume value
7160     volume_music_current =
7161       getTreeInfoFromIdentifier(volumes_music, i_to_a(setup.volume_music));
7162
7163     // if that fails, set current volume to reliable default value
7164     if (volume_music_current == NULL)
7165       volume_music_current =
7166         getTreeInfoFromIdentifier(volumes_music, i_to_a(100));
7167
7168     // if that also fails, set current volume to first available value
7169     if (volume_music_current == NULL)
7170       volume_music_current = volumes_music;
7171   }
7172
7173   setup.volume_simple = atoi(volume_simple_current->identifier);
7174   setup.volume_loops  = atoi(volume_loops_current->identifier);
7175   setup.volume_music  = atoi(volume_music_current->identifier);
7176
7177   // needed for displaying volume text instead of identifier
7178   volume_simple_text = volume_simple_current->name;
7179   volume_loops_text = volume_loops_current->name;
7180   volume_music_text = volume_music_current->name;
7181
7182   setup_mode = SETUP_MODE_SOUND;
7183
7184   DrawSetupScreen();
7185 }
7186
7187 static void execSetupChooseTouchControls(void)
7188 {
7189   setup_mode = SETUP_MODE_CHOOSE_TOUCH_CONTROL;
7190
7191   DrawSetupScreen();
7192 }
7193
7194 static void execSetupChooseMoveDistance(void)
7195 {
7196   setup_mode = SETUP_MODE_CHOOSE_MOVE_DISTANCE;
7197
7198   DrawSetupScreen();
7199 }
7200
7201 static void execSetupChooseDropDistance(void)
7202 {
7203   setup_mode = SETUP_MODE_CHOOSE_DROP_DISTANCE;
7204
7205   DrawSetupScreen();
7206 }
7207
7208 static void execSetupChooseTransparency(void)
7209 {
7210   setup_mode = SETUP_MODE_CHOOSE_TRANSPARENCY;
7211
7212   DrawSetupScreen();
7213 }
7214
7215 static void execSetupChooseGridXSize_0(void)
7216 {
7217   setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_0;
7218
7219   DrawSetupScreen();
7220 }
7221
7222 static void execSetupChooseGridYSize_0(void)
7223 {
7224   setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_0;
7225
7226   DrawSetupScreen();
7227 }
7228
7229 static void execSetupChooseGridXSize_1(void)
7230 {
7231   setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_1;
7232
7233   DrawSetupScreen();
7234 }
7235
7236 static void execSetupChooseGridYSize_1(void)
7237 {
7238   setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_1;
7239
7240   DrawSetupScreen();
7241 }
7242
7243 static void execSetupConfigureVirtualButtons(void)
7244 {
7245   setup_mode = SETUP_MODE_CONFIG_VIRT_BUTTONS;
7246
7247   ConfigureVirtualButtons();
7248
7249   setup_mode = SETUP_MODE_TOUCH;
7250
7251   DrawSetupScreen();
7252 }
7253
7254 static void execSetupTouch(void)
7255 {
7256   int i, j, k;
7257
7258   if (touch_controls == NULL)
7259   {
7260     for (i = 0; touch_controls_list[i].value != NULL; i++)
7261     {
7262       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7263       char identifier[32], name[32];
7264       char *value = touch_controls_list[i].value;
7265       char *text = touch_controls_list[i].text;
7266
7267       ti->node_top = &touch_controls;
7268       ti->sort_priority = i;
7269
7270       sprintf(identifier, "%s", value);
7271       sprintf(name, "%s", text);
7272
7273       setString(&ti->identifier, identifier);
7274       setString(&ti->name, name);
7275       setString(&ti->name_sorting, name);
7276       setString(&ti->infotext, STR_SETUP_CHOOSE_TOUCH_CONTROL);
7277
7278       pushTreeInfo(&touch_controls, ti);
7279     }
7280
7281     // sort touch control values to start with lowest touch control value
7282     sortTreeInfo(&touch_controls);
7283
7284     // set current touch control value to configured touch control value
7285     touch_control_current =
7286       getTreeInfoFromIdentifier(touch_controls, setup.touch.control_type);
7287
7288     // if that fails, set current touch control to reliable default value
7289     if (touch_control_current == NULL)
7290       touch_control_current =
7291         getTreeInfoFromIdentifier(touch_controls, TOUCH_CONTROL_DEFAULT);
7292
7293     // if that also fails, set current touch control to first available value
7294     if (touch_control_current == NULL)
7295       touch_control_current = touch_controls;
7296   }
7297
7298   if (move_distances == NULL)
7299   {
7300     for (i = 0; distances_list[i].value != -1; i++)
7301     {
7302       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7303       char identifier[32], name[32];
7304       int value = distances_list[i].value;
7305       char *text = distances_list[i].text;
7306
7307       ti->node_top = &move_distances;
7308       ti->sort_priority = value;
7309
7310       sprintf(identifier, "%d", value);
7311       sprintf(name, "%s", text);
7312
7313       setString(&ti->identifier, identifier);
7314       setString(&ti->name, name);
7315       setString(&ti->name_sorting, name);
7316       setString(&ti->infotext, STR_SETUP_CHOOSE_MOVE_DISTANCE);
7317
7318       pushTreeInfo(&move_distances, ti);
7319     }
7320
7321     // sort distance values to start with lowest distance value
7322     sortTreeInfo(&move_distances);
7323
7324     // set current distance value to configured distance value
7325     move_distance_current =
7326       getTreeInfoFromIdentifier(move_distances,
7327                                 i_to_a(setup.touch.move_distance));
7328
7329     // if that fails, set current distance to reliable default value
7330     if (move_distance_current == NULL)
7331       move_distance_current =
7332         getTreeInfoFromIdentifier(move_distances,
7333                                   i_to_a(TOUCH_MOVE_DISTANCE_DEFAULT));
7334
7335     // if that also fails, set current distance to first available value
7336     if (move_distance_current == NULL)
7337       move_distance_current = move_distances;
7338   }
7339
7340   if (drop_distances == NULL)
7341   {
7342     for (i = 0; distances_list[i].value != -1; i++)
7343     {
7344       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7345       char identifier[32], name[32];
7346       int value = distances_list[i].value;
7347       char *text = distances_list[i].text;
7348
7349       ti->node_top = &drop_distances;
7350       ti->sort_priority = value;
7351
7352       sprintf(identifier, "%d", value);
7353       sprintf(name, "%s", text);
7354
7355       setString(&ti->identifier, identifier);
7356       setString(&ti->name, name);
7357       setString(&ti->name_sorting, name);
7358       setString(&ti->infotext, STR_SETUP_CHOOSE_DROP_DISTANCE);
7359
7360       pushTreeInfo(&drop_distances, ti);
7361     }
7362
7363     // sort distance values to start with lowest distance value
7364     sortTreeInfo(&drop_distances);
7365
7366     // set current distance value to configured distance value
7367     drop_distance_current =
7368       getTreeInfoFromIdentifier(drop_distances,
7369                                 i_to_a(setup.touch.drop_distance));
7370
7371     // if that fails, set current distance to reliable default value
7372     if (drop_distance_current == NULL)
7373       drop_distance_current =
7374         getTreeInfoFromIdentifier(drop_distances,
7375                                   i_to_a(TOUCH_DROP_DISTANCE_DEFAULT));
7376
7377     // if that also fails, set current distance to first available value
7378     if (drop_distance_current == NULL)
7379       drop_distance_current = drop_distances;
7380   }
7381
7382   if (transparencies == NULL)
7383   {
7384     for (i = 0; transparencies_list[i].value != -1; i++)
7385     {
7386       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7387       char identifier[32], name[32];
7388       int value = transparencies_list[i].value;
7389       char *text = transparencies_list[i].text;
7390
7391       ti->node_top = &transparencies;
7392       ti->sort_priority = value;
7393
7394       sprintf(identifier, "%d", value);
7395       sprintf(name, "%s", text);
7396
7397       setString(&ti->identifier, identifier);
7398       setString(&ti->name, name);
7399       setString(&ti->name_sorting, name);
7400       setString(&ti->infotext, STR_SETUP_CHOOSE_TRANSPARENCY);
7401
7402       pushTreeInfo(&transparencies, ti);
7403     }
7404
7405     // sort transparency values to start with lowest transparency value
7406     sortTreeInfo(&transparencies);
7407
7408     // set current transparency value to configured transparency value
7409     transparency_current =
7410       getTreeInfoFromIdentifier(transparencies,
7411                                 i_to_a(setup.touch.transparency));
7412
7413     // if that fails, set current transparency to reliable default value
7414     if (transparency_current == NULL)
7415       transparency_current =
7416         getTreeInfoFromIdentifier(transparencies,
7417                                   i_to_a(TOUCH_TRANSPARENCY_DEFAULT));
7418
7419     // if that also fails, set current transparency to first available value
7420     if (transparency_current == NULL)
7421       transparency_current = transparencies;
7422   }
7423
7424   for (i = 0; i < 2; i++)
7425   {
7426     for (j = 0; j < 2; j++)
7427     {
7428       if (grid_sizes[i][j] == NULL)
7429       {
7430         for (k = 0; grid_sizes_list[k].value != -1; k++)
7431         {
7432           TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
7433           char identifier[32], name[32];
7434           int value = grid_sizes_list[k].value;
7435           char *text = grid_sizes_list[k].text;
7436
7437           ti->node_top = &grid_sizes[i][j];
7438           ti->sort_priority = value;
7439
7440           sprintf(identifier, "%d", value);
7441           sprintf(name, "%s", text);
7442
7443           setString(&ti->identifier, identifier);
7444           setString(&ti->name, name);
7445           setString(&ti->name_sorting, name);
7446           setString(&ti->infotext,
7447                     (i == 0 ?
7448                      (j == 0 ?
7449                       STR_SETUP_CHOOSE_GRID_XSIZE_0 :
7450                       STR_SETUP_CHOOSE_GRID_YSIZE_0) :
7451                      (j == 0 ?
7452                       STR_SETUP_CHOOSE_GRID_XSIZE_1 :
7453                       STR_SETUP_CHOOSE_GRID_YSIZE_1)));
7454
7455           pushTreeInfo(&grid_sizes[i][j], ti);
7456         }
7457
7458         // sort grid size values to start with lowest grid size value
7459         sortTreeInfo(&grid_sizes[i][j]);
7460
7461         // set current grid size value to configured grid size value
7462         grid_size_current[i][j] =
7463           getTreeInfoFromIdentifier(grid_sizes[i][j],
7464                                     i_to_a(j == 0 ?
7465                                            setup.touch.grid_xsize[i] :
7466                                            setup.touch.grid_ysize[i]));
7467
7468         // if that fails, set current grid size to reliable default value
7469         if (grid_size_current[i][j] == NULL)
7470           grid_size_current[i][j] =
7471             getTreeInfoFromIdentifier(grid_sizes[i][j],
7472                                       i_to_a(j == 0 ?
7473                                              DEFAULT_GRID_XSIZE(i) :
7474                                              DEFAULT_GRID_YSIZE(i)));
7475
7476         // if that also fails, set current grid size to first available value
7477         if (grid_size_current[i][j] == NULL)
7478           grid_size_current[i][j] = grid_sizes[i][j];
7479       }
7480     }
7481   }
7482
7483   setup.touch.control_type = touch_control_current->identifier;
7484   setup.touch.move_distance = atoi(move_distance_current->identifier);
7485   setup.touch.drop_distance = atoi(drop_distance_current->identifier);
7486   setup.touch.transparency = atoi(transparency_current->identifier);
7487
7488   for (i = 0; i < 2; i++)
7489   {
7490     setup.touch.grid_xsize[i] = atoi(grid_size_current[i][0]->identifier);
7491     setup.touch.grid_ysize[i] = atoi(grid_size_current[i][1]->identifier);
7492
7493     if (i == GRID_ACTIVE_NR())
7494     {
7495       overlay.grid_xsize = setup.touch.grid_xsize[i];
7496       overlay.grid_ysize = setup.touch.grid_ysize[i];
7497     }
7498   }
7499
7500   // needed for displaying value text instead of identifier
7501   touch_controls_text = touch_control_current->name;
7502   move_distance_text = move_distance_current->name;
7503   drop_distance_text = drop_distance_current->name;
7504   transparency_text = transparency_current->name;
7505
7506   for (i = 0; i < 2; i++)
7507     for (j = 0; j < 2; j++)
7508       grid_size_text[i][j] = grid_size_current[i][j]->name;
7509
7510   setup_mode = SETUP_MODE_TOUCH;
7511
7512   DrawSetupScreen();
7513 }
7514
7515 static void execSetupArtwork(void)
7516 {
7517   static ArtworkDirTree *gfx_last_valid = NULL;
7518   static ArtworkDirTree *snd_last_valid = NULL;
7519   static ArtworkDirTree *mus_last_valid = NULL;
7520
7521   // current artwork directory may be invalid (level group, parent link)
7522   if (!validLevelSeries(artwork.gfx_current))
7523     artwork.gfx_current = getFirstValidTreeInfoEntry(gfx_last_valid);
7524   if (!validLevelSeries(artwork.snd_current))
7525     artwork.snd_current = getFirstValidTreeInfoEntry(snd_last_valid);
7526   if (!validLevelSeries(artwork.mus_current))
7527     artwork.mus_current = getFirstValidTreeInfoEntry(mus_last_valid);
7528
7529   // store valid artwork directory information
7530   gfx_last_valid = artwork.gfx_current;
7531   snd_last_valid = artwork.snd_current;
7532   mus_last_valid = artwork.mus_current;
7533
7534 #if 0
7535   Debug("screens:execSetupArtwork", "'%s', '%s', '%s'",
7536         artwork.gfx_current->subdir,
7537         artwork.gfx_current->fullpath,
7538         artwork.gfx_current->basepath);
7539 #endif
7540
7541   setup.graphics_set = artwork.gfx_current->identifier;
7542   setup.sounds_set = artwork.snd_current->identifier;
7543   setup.music_set = artwork.mus_current->identifier;
7544
7545   // needed if last screen (setup choice) changed graphics, sounds or music
7546   ReloadCustomArtwork(0);
7547
7548   // needed for displaying artwork name instead of artwork identifier
7549   graphics_set_name = artwork.gfx_current->name;
7550   sounds_set_name = artwork.snd_current->name;
7551   music_set_name = artwork.mus_current->name;
7552
7553   setup_mode = SETUP_MODE_ARTWORK;
7554
7555   DrawSetupScreen();
7556 }
7557
7558 static void execSetupChooseGraphics(void)
7559 {
7560   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
7561
7562   DrawSetupScreen();
7563 }
7564
7565 static void execSetupChooseSounds(void)
7566 {
7567   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
7568
7569   DrawSetupScreen();
7570 }
7571
7572 static void execSetupChooseMusic(void)
7573 {
7574   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
7575
7576   DrawSetupScreen();
7577 }
7578
7579 static void execSetupInput(void)
7580 {
7581   setup_mode = SETUP_MODE_INPUT;
7582
7583   DrawSetupScreen();
7584 }
7585
7586 static void execSetupShortcuts(void)
7587 {
7588   setup_mode = SETUP_MODE_SHORTCUTS;
7589
7590   DrawSetupScreen();
7591 }
7592
7593 static void execSetupShortcuts1(void)
7594 {
7595   setup_mode = SETUP_MODE_SHORTCUTS_1;
7596
7597   DrawSetupScreen();
7598 }
7599
7600 static void execSetupShortcuts2(void)
7601 {
7602   setup_mode = SETUP_MODE_SHORTCUTS_2;
7603
7604   DrawSetupScreen();
7605 }
7606
7607 static void execSetupShortcuts3(void)
7608 {
7609   setup_mode = SETUP_MODE_SHORTCUTS_3;
7610
7611   DrawSetupScreen();
7612 }
7613
7614 static void execSetupShortcuts4(void)
7615 {
7616   setup_mode = SETUP_MODE_SHORTCUTS_4;
7617
7618   DrawSetupScreen();
7619 }
7620
7621 static void execSetupShortcuts5(void)
7622 {
7623   setup_mode = SETUP_MODE_SHORTCUTS_5;
7624
7625   DrawSetupScreen();
7626 }
7627
7628 static void execExitSetup(void)
7629 {
7630   SetGameStatus(GAME_MODE_MAIN);
7631
7632   DrawMainMenu();
7633 }
7634
7635 static void execSaveAndExitSetup(void)
7636 {
7637   SaveSetup();
7638   execExitSetup();
7639 }
7640
7641 static void execGadgetNetworkServer(void)
7642 {
7643   int gadget_id = SCREEN_CTRL_ID_NETWORK_SERVER;
7644   struct GadgetInfo *gi = screen_gadget[gadget_id];
7645
7646   if (strEqual(setup.network_server_hostname, STR_NETWORK_AUTO_DETECT))
7647     network_server_hostname[0] = '\0';
7648
7649   ModifyGadget(gi, GDI_TEXT_VALUE, network_server_hostname, GDI_END);
7650
7651   MapGadget(gi);
7652
7653   ClickOnGadget(gi, MB_LEFTBUTTON);
7654 }
7655
7656 static void execOfferUploadTapes(void)
7657 {
7658   OfferUploadTapes();
7659 }
7660
7661 static void ToggleNetworkModeIfNeeded(void)
7662 {
7663   int font_title = FONT_TITLE_1;
7664   int font_foot = FC_BLUE;
7665   int ystart  = mSY - SY + 16;
7666   int ybottom = mSY - SY + SYSIZE - 20;
7667   char *text = (setup.network_mode ? "Start Network" : "Stop Network");
7668
7669   if (setup.network_mode == network.enabled)
7670     return;
7671
7672   network.enabled = setup.network_mode;
7673
7674   FadeOut(REDRAW_ALL);
7675
7676   ClearField();
7677
7678   DrawTextSCentered(ystart, font_title, text);
7679
7680   FadeIn(REDRAW_ALL);
7681
7682   if (network.enabled)
7683     InitNetworkServer();
7684   else
7685     DisconnectFromNetworkServer();
7686
7687   DrawTextSCentered(ybottom, font_foot,
7688                     "Press any key or button for setup menu");
7689
7690   WaitForEventToContinue();
7691
7692   DrawSetupScreen();
7693 }
7694
7695 static void ToggleGameSpeedsListIfNeeded(void)
7696 {
7697   boolean using_game_speeds_extended = (game_speeds == game_speeds_extended);
7698
7699   if (setup.game_speed_extended == using_game_speeds_extended)
7700     return;
7701
7702   // try to match similar values when changing game speeds list
7703   if (setup.game_speed_extended)
7704     setup.game_frame_delay = (setup.game_frame_delay == 15 ? 16 :
7705                               setup.game_frame_delay == 30 ? 29 :
7706                               setup.game_frame_delay);
7707   else
7708     setup.game_frame_delay = (setup.game_frame_delay == 14 ? 15 :
7709                               setup.game_frame_delay == 16 ? 15 :
7710                               setup.game_frame_delay >= 29 ? 30 :
7711                               setup.game_frame_delay <= 10 ? 10 :
7712                               setup.game_frame_delay);
7713
7714   execSetupGame_setGameSpeeds(TRUE);
7715
7716   DrawSetupScreen();
7717 }
7718
7719 static void ToggleUseApiServerIfNeeded(void)
7720 {
7721   if (runtime.use_api_server == setup.use_api_server)
7722     return;
7723
7724   runtime.use_api_server = setup.use_api_server;
7725
7726   if (runtime.use_api_server)
7727   {
7728     if (setup.has_remaining_tapes)
7729       setup.ask_for_uploading_tapes = TRUE;
7730
7731     CheckApiServerTasks();
7732   }
7733 }
7734
7735 static void ModifyGameSpeedIfNeeded(void)
7736 {
7737   if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) ||
7738       setup.game_frame_delay <= MAX_VSYNC_FRAME_DELAY)
7739     return;
7740
7741   char message[100];
7742   char *game_speed_text = "Fast";
7743   int game_speed_value = 15;
7744
7745   if (setup.game_speed_extended)
7746   {
7747     game_speed_text = "60 fps";
7748     game_speed_value = 16;
7749   }
7750
7751   sprintf(message, "Game speed set to %s for VSync to work!", game_speed_text);
7752
7753   // set game speed to existing list value that is fast enough for vsync
7754   setup.game_frame_delay = game_speed_value;
7755
7756   execSetupGame_setGameSpeeds(TRUE);
7757
7758   Request(message, REQ_CONFIRM);
7759 }
7760
7761 static void DisableVsyncIfNeeded(void)
7762 {
7763   if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) ||
7764       (setup.game_frame_delay >= MIN_VSYNC_FRAME_DELAY &&
7765        setup.game_frame_delay <= MAX_VSYNC_FRAME_DELAY))
7766     return;
7767
7768   // disable vsync for the selected game speed to work
7769   setup.vsync_mode = STR_VSYNC_MODE_OFF;
7770
7771   execSetupGraphics_setVsyncModes(TRUE);
7772
7773   Request("VSync disabled for this game speed to work!", REQ_CONFIRM);
7774 }
7775
7776 static struct
7777 {
7778   void *value;
7779   void *related_value;
7780 } hide_related_entry_list[] =
7781 {
7782   { &setup.network_server_hostname,             execGadgetNetworkServer         },
7783   { &setup.network_server_hostname,             &network_server_text            },
7784
7785   { &setup.scores_in_highscore_list,            execSetupChooseScoresType       },
7786   { &setup.scores_in_highscore_list,            &scores_type_text               },
7787
7788   { &setup.game_frame_delay,                    execSetupChooseGameSpeed        },
7789   { &setup.game_frame_delay,                    &game_speed_text                },
7790
7791   { &setup.scroll_delay_value,                  execSetupChooseScrollDelay      },
7792   { &setup.scroll_delay_value,                  &scroll_delay_text              },
7793
7794   { &setup.engine_snapshot_mode,                execSetupChooseSnapshotMode     },
7795   { &setup.engine_snapshot_mode,                &snapshot_mode_text             },
7796
7797   { &setup.default_game_engine_type,            execSetupChooseGameEngineType   },
7798   { &setup.default_game_engine_type,            &game_engine_type_text          },
7799
7800   { &setup.bd_palette_c64,                      execSetupChoosePaletteC64       },
7801   { &setup.bd_palette_c64,                      &bd_palette_c64_text            },
7802
7803   { &setup.bd_palette_c64dtv,                   execSetupChoosePaletteC64DTV    },
7804   { &setup.bd_palette_c64dtv,                   &bd_palette_c64dtv_text         },
7805
7806   { &setup.bd_palette_atari,                    execSetupChoosePaletteAtari     },
7807   { &setup.bd_palette_atari,                    &bd_palette_atari_text          },
7808
7809   { &setup.bd_default_color_type,               execSetupChooseColorType        },
7810   { &setup.bd_default_color_type,               &bd_color_type_text             },
7811
7812   { &setup.window_scaling_percent,              execSetupChooseWindowSize       },
7813   { &setup.window_scaling_percent,              &window_size_text               },
7814
7815   { &setup.window_scaling_quality,              execSetupChooseScalingType      },
7816   { &setup.window_scaling_quality,              &scaling_type_text              },
7817
7818   { &setup.screen_rendering_mode,               execSetupChooseRenderingMode    },
7819   { &setup.screen_rendering_mode,               &rendering_mode_text            },
7820
7821   { &setup.vsync_mode,                          execSetupChooseVsyncMode        },
7822   { &setup.vsync_mode,                          &vsync_mode_text                },
7823
7824   { &setup.graphics_set,                        execSetupChooseGraphics         },
7825   { &setup.graphics_set,                        &graphics_set_name              },
7826
7827   { &setup.sounds_set,                          execSetupChooseSounds           },
7828   { &setup.sounds_set,                          &sounds_set_name                },
7829
7830   { &setup.music_set,                           execSetupChooseMusic            },
7831   { &setup.music_set,                           &music_set_name                 },
7832
7833   { &setup.volume_simple,                       execSetupChooseVolumeSimple     },
7834   { &setup.volume_simple,                       &volume_simple_text             },
7835
7836   { &setup.volume_loops,                        execSetupChooseVolumeLoops      },
7837   { &setup.volume_loops,                        &volume_loops_text              },
7838
7839   { &setup.volume_music,                        execSetupChooseVolumeMusic      },
7840   { &setup.volume_music,                        &volume_music_text              },
7841
7842   { &setup.touch.control_type,                  execSetupChooseTouchControls    },
7843   { &setup.touch.control_type,                  &touch_controls_text            },
7844
7845   { &setup.touch.move_distance,                 execSetupChooseMoveDistance     },
7846   { &setup.touch.move_distance,                 &move_distance_text             },
7847
7848   { &setup.touch.drop_distance,                 execSetupChooseDropDistance     },
7849   { &setup.touch.drop_distance,                 &drop_distance_text             },
7850
7851   { &setup.touch.transparency,                  execSetupChooseTransparency     },
7852   { &setup.touch.transparency,                  &transparency_text              },
7853
7854   { &setup.touch.grid_xsize[0],                 execSetupChooseGridXSize_0      },
7855   { &setup.touch.grid_xsize[0],                 &grid_size_text[0][0]           },
7856
7857   { &setup.touch.grid_ysize[0],                 execSetupChooseGridYSize_0      },
7858   { &setup.touch.grid_ysize[0],                 &grid_size_text[0][1]           },
7859
7860   { &setup.touch.grid_xsize[1],                 execSetupChooseGridXSize_1      },
7861   { &setup.touch.grid_xsize[1],                 &grid_size_text[1][0]           },
7862
7863   { &setup.touch.grid_ysize[1],                 execSetupChooseGridYSize_1      },
7864   { &setup.touch.grid_ysize[1],                 &grid_size_text[1][1]           },
7865
7866   { &setup.internal.menu_game,                  execSetupGame                   },
7867   { &setup.internal.menu_engines,               execSetupEngines                },
7868   { &setup.internal.menu_editor,                execSetupEditor                 },
7869   { &setup.internal.menu_graphics,              execSetupGraphics               },
7870   { &setup.internal.menu_sound,                 execSetupSound                  },
7871   { &setup.internal.menu_artwork,               execSetupArtwork                },
7872   { &setup.internal.menu_input,                 execSetupInput                  },
7873   { &setup.internal.menu_touch,                 execSetupTouch                  },
7874   { &setup.internal.menu_shortcuts,             execSetupShortcuts              },
7875   { &setup.internal.menu_exit,                  execExitSetup                   },
7876   { &setup.internal.menu_save_and_exit,         execSaveAndExitSetup            },
7877
7878   { &setup.internal.menu_shortcuts_various,     execSetupShortcuts1             },
7879   { &setup.internal.menu_shortcuts_focus,       execSetupShortcuts2             },
7880   { &setup.internal.menu_shortcuts_tape,        execSetupShortcuts3             },
7881   { &setup.internal.menu_shortcuts_sound,       execSetupShortcuts4             },
7882   { &setup.internal.menu_shortcuts_snap,        execSetupShortcuts5             },
7883
7884   { &setup.internal.info_title,                 execInfoTitleScreen             },
7885   { &setup.internal.info_elements,              execInfoElements                },
7886   { &setup.internal.info_music,                 execInfoMusic                   },
7887   { &setup.internal.info_credits,               execInfoCredits                 },
7888   { &setup.internal.info_program,               execInfoProgram                 },
7889   { &setup.internal.info_version,               execInfoVersion                 },
7890   { &setup.internal.info_levelset,              execInfoLevelSet                },
7891   { &setup.internal.info_exit,                  execExitInfo                    },
7892
7893   { NULL,                                       NULL                            }
7894 };
7895
7896 void setHideRelatedSetupEntries(void)
7897 {
7898   int i;
7899
7900   for (i = 0; hide_related_entry_list[i].value != NULL; i++)
7901     if (hideSetupEntry(hide_related_entry_list[i].value))
7902       setHideSetupEntry(hide_related_entry_list[i].related_value);
7903 }
7904
7905 static struct TokenInfo setup_info_main[] =
7906 {
7907   { TYPE_ENTER_MENU,    execSetupGame,                  STR_SETUP_GAME                  },
7908   { TYPE_ENTER_MENU,    execSetupEngines,               STR_SETUP_ENGINES               },
7909   { TYPE_ENTER_MENU,    execSetupEditor,                STR_SETUP_EDITOR                },
7910   { TYPE_ENTER_MENU,    execSetupGraphics,              STR_SETUP_GRAPHICS              },
7911   { TYPE_ENTER_MENU,    execSetupSound,                 STR_SETUP_SOUND                 },
7912   { TYPE_ENTER_MENU,    execSetupArtwork,               STR_SETUP_ARTWORK               },
7913   { TYPE_ENTER_MENU,    execSetupInput,                 STR_SETUP_INPUT                 },
7914   { TYPE_ENTER_MENU,    execSetupTouch,                 STR_SETUP_TOUCH                 },
7915   { TYPE_ENTER_MENU,    execSetupShortcuts,             STR_SETUP_SHORTCUTS             },
7916   { TYPE_EMPTY,         NULL,                           ""                              },
7917   { TYPE_LEAVE_MENU,    execExitSetup,                  STR_SETUP_EXIT                  },
7918   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,           STR_SETUP_SAVE_AND_EXIT         },
7919
7920   { 0,                  NULL,                           NULL                            }
7921 };
7922
7923 static struct TokenInfo setup_info_game[] =
7924 {
7925   { TYPE_SWITCH,        &setup.team_mode,               "Team-Mode (Multi-Player):"     },
7926   { TYPE_SWITCH,        &setup.network_mode,            "Network Multi-Player Mode:"    },
7927   { TYPE_PLAYER,        &setup.network_player_nr,       "Preferred Network Player:"     },
7928   { TYPE_TEXT_INPUT,    execGadgetNetworkServer,        "Network Server Hostname:"      },
7929   { TYPE_STRING,        &network_server_text,           ""                              },
7930   { TYPE_SWITCH,        &setup.use_api_server,          "Use Highscore Server:"         },
7931   { TYPE_ENTER_LIST,    execSetupChooseScoresType,      "Scores in Highscore List:"     },
7932   { TYPE_STRING,        &scores_type_text,              ""                              },
7933   { TYPE_ENTER_LIST,    execOfferUploadTapes,           "Upload Tapes to Server"        },
7934   { TYPE_SWITCH,        &setup.multiple_users,          "Multiple Users/Teams:"         },
7935   { TYPE_YES_NO,        &setup.input_on_focus,          "Only Move Focussed Player:"    },
7936   { TYPE_SWITCH,        &setup.time_limit,              "Time Limit:"                   },
7937   { TYPE_SWITCH,        &setup.handicap,                "Force Solving Levels:"         },
7938   { TYPE_SWITCH,        &setup.skip_levels,             "Allow Skipping Levels:"        },
7939   { TYPE_SWITCH,        &setup.increment_levels,        "Increment Solved Levels:"      },
7940   { TYPE_SWITCH,        &setup.auto_play_next_level,    "Auto-play Next Level:"         },
7941   { TYPE_SWITCH,        &setup.count_score_after_game,  "Count Score After Game:"       },
7942   { TYPE_SWITCH,        &setup.show_scores_after_game,  "Show Scores After Game:"       },
7943   { TYPE_YES_NO,        &setup.ask_on_game_over,        "Ask on Game Over:"             },
7944   { TYPE_YES_NO,        &setup.ask_on_quit_game,        "Ask on Quit Game:"             },
7945   { TYPE_YES_NO,        &setup.ask_on_quit_program,     "Ask on Quit Program:"          },
7946   { TYPE_SWITCH,        &setup.autorecord,              "Auto-Record When Playing:"     },
7947   { TYPE_SWITCH,        &setup.autorecord_after_replay, "Auto-Record After Replay:"     },
7948   { TYPE_SWITCH,        &setup.auto_pause_on_start,     "Start Game in Pause Mode:"     },
7949   { TYPE_ENTER_LIST,    execSetupChooseGameSpeed,       "Game Speed:"                   },
7950   { TYPE_STRING,        &game_speed_text,               ""                              },
7951   { TYPE_SWITCH,        &setup.game_speed_extended,     "Game Speed Extended List:"     },
7952 #if 1
7953   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay,     "Scroll Delay:"                 },
7954   { TYPE_STRING,        &scroll_delay_text,             ""                              },
7955 #endif
7956   { TYPE_ENTER_LIST,    execSetupChooseSnapshotMode,    "Game Engine Snapshot Mode:"    },
7957   { TYPE_STRING,        &snapshot_mode_text,            ""                              },
7958   { TYPE_SWITCH,        &setup.show_load_save_buttons,  "Show Load/Save Buttons:"       },
7959   { TYPE_SWITCH,        &setup.show_undo_redo_buttons,  "Show Undo/Redo Buttons:"       },
7960   { TYPE_EMPTY,         NULL,                           ""                              },
7961   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
7962
7963   { 0,                  NULL,                           NULL                            }
7964 };
7965
7966 static struct TokenInfo setup_info_engines[] =
7967 {
7968   { TYPE_ENTER_LIST,    &execSetupChooseGameEngineType, "Default Game Engine:"          },
7969   { TYPE_STRING,        &game_engine_type_text,         ""                              },
7970   { TYPE_EMPTY,         NULL,                           ""                              },
7971   { TYPE_HEADLINE,      NULL,                           "Boulder Dash"                  },
7972   { TYPE_SWITCH,        &setup.bd_skip_uncovering,      "Skip (un)covering screen:"     },
7973   { TYPE_SWITCH,        &setup.bd_skip_hatching,        "Skip hatching player:"         },
7974   { TYPE_SWITCH,        &setup.bd_scroll_delay,         "Scroll Delay:"                 },
7975   { TYPE_YES_NO_AUTO,   &setup.bd_smooth_movements,     "Smooth Element Movement:"      },
7976   { TYPE_ENTER_LIST,    &execSetupChoosePaletteC64,     "Color Palette (C64):"          },
7977   { TYPE_STRING,        &bd_palette_c64_text,           ""                              },
7978   { TYPE_ENTER_LIST,    &execSetupChoosePaletteC64DTV,  "Color Palette (C64DTV):"       },
7979   { TYPE_STRING,        &bd_palette_c64dtv_text,        ""                              },
7980   { TYPE_ENTER_LIST,    &execSetupChoosePaletteAtari,   "Color Palette (Atari):"        },
7981   { TYPE_STRING,        &bd_palette_atari_text,         ""                              },
7982   { TYPE_ENTER_LIST,    &execSetupChooseColorType,      "Preferred Color Type:"         },
7983   { TYPE_STRING,        &bd_color_type_text,            ""                              },
7984   { TYPE_SWITCH,        &setup.bd_random_colors,        "Random Colors:"                },
7985   { TYPE_EMPTY,         NULL,                           ""                              },
7986   { TYPE_HEADLINE,      NULL,                           "Emerald Mine"                  },
7987   { TYPE_SWITCH,        &setup.forced_scroll_delay,     "Scroll Delay:"                 },
7988   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,     "Amiga Graphics Chipset:"       },
7989   { TYPE_SWITCH,        &setup.prefer_lowpass_sounds,   "Low-Pass Filter Sounds:"       },
7990   { TYPE_SWITCH,        &setup.prefer_extra_panel_items,"Show Dynamite and Keys:"       },
7991   { TYPE_EMPTY,         NULL,                           ""                              },
7992   { TYPE_HEADLINE,      NULL,                           "Supaplex"                      },
7993   { TYPE_SWITCH,        &setup.sp_show_border_elements, "Border Elements:"              },
7994   { TYPE_EMPTY,         NULL,                           ""                              },
7995   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
7996
7997   { 0,                  NULL,                           NULL                            }
7998 };
7999
8000 static struct TokenInfo setup_info_editor[] =
8001 {
8002 #if 0
8003   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:"                 },
8004   { TYPE_SWITCH,        &setup.editor.el_boulderdash_native, "Boulder Dash Native:"     },
8005   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:"                 },
8006   { TYPE_SWITCH,        &setup.editor.el_emerald_mine_club, "Emerald Mine Club:"        },
8007   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:"             },
8008   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"                      },
8009   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"                     },
8010   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:"             },
8011   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:"               },
8012   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:"              },
8013   { TYPE_SWITCH,        &setup.editor.el_steel_chars,   "Text Characters (Steel):"      },
8014 #endif
8015   { TYPE_SWITCH,        &setup.editor.el_classic,       "Classic Elements:"             },
8016   { TYPE_SWITCH,        &setup.editor.el_custom,        "Custom & Group Elements:"      },
8017 #if 0
8018   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"                    },
8019 #endif
8020   { TYPE_SWITCH, &setup.editor.el_user_defined,         "User defined element list:"    },
8021   { TYPE_SWITCH,        &setup.editor.el_dynamic,       "Dynamic level elements:"       },
8022   { TYPE_EMPTY,         NULL,                           ""                              },
8023 #if 0
8024   { TYPE_SWITCH,        &setup.editor.el_by_game,       "Show elements by game:"        },
8025   { TYPE_SWITCH,        &setup.editor.el_by_type,       "Show elements by type:"        },
8026   { TYPE_EMPTY,         NULL,                           ""                              },
8027 #endif
8028   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:"           },
8029   { TYPE_EMPTY,         NULL,                           ""                              },
8030   { TYPE_SWITCH, &setup.editor.show_read_only_warning,  "Show read-only warning:"       },
8031   { TYPE_EMPTY,         NULL,                           ""                              },
8032   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8033
8034   { 0,                  NULL,                           NULL                            }
8035 };
8036
8037 static struct TokenInfo setup_info_graphics[] =
8038 {
8039 #if !defined(PLATFORM_ANDROID) && !defined(PLATFORM_EMSCRIPTEN)
8040   { TYPE_SWITCH,        &setup.fullscreen,              "Fullscreen:"                   },
8041   { TYPE_ENTER_LIST,    execSetupChooseWindowSize,      "Window Scaling:"               },
8042   { TYPE_STRING,        &window_size_text,              ""                              },
8043   { TYPE_ENTER_LIST,    execSetupChooseScalingType,     "Anti-Aliasing:"                },
8044   { TYPE_STRING,        &scaling_type_text,             ""                              },
8045   { TYPE_ENTER_LIST,    execSetupChooseRenderingMode,   "Special Rendering:"            },
8046   { TYPE_STRING,        &rendering_mode_text,           ""                              },
8047 #endif
8048 #if 0
8049   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay,     "Scroll Delay:"                 },
8050   { TYPE_STRING,        &scroll_delay_text,             ""                              },
8051 #endif
8052 #if !defined(PLATFORM_EMSCRIPTEN)
8053   { TYPE_ENTER_LIST,    execSetupChooseVsyncMode,       "Vertical Sync (VSync):"        },
8054   { TYPE_STRING,        &vsync_mode_text,               ""                              },
8055 #endif
8056   { TYPE_SWITCH,        &setup.fade_screens,            "Fade Screens:"                 },
8057   { TYPE_SWITCH,        &setup.quick_switch,            "Quick Player Focus Switch:"    },
8058   { TYPE_SWITCH,        &setup.quick_doors,             "Quick Menu Doors:"             },
8059   { TYPE_SWITCH,        &setup.show_titlescreen,        "Show Title Screens:"           },
8060   { TYPE_SWITCH,        &setup.toons,                   "Show Toons:"                   },
8061   { TYPE_SWITCH,        &setup.small_game_graphics,     "Small Game Graphics:"          },
8062   { TYPE_YES_NO_AUTO,   &setup.debug.xsn_mode,          debug_xsn_mode                  },
8063   { TYPE_EMPTY,         NULL,                           ""                              },
8064   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8065
8066   { 0,                  NULL,                           NULL                            }
8067 };
8068
8069 static struct TokenInfo setup_info_sound[] =
8070 {
8071   { TYPE_SWITCH,        &setup.sound_simple,            "Sound Effects (Normal):"       },
8072   { TYPE_SWITCH,        &setup.sound_loops,             "Sound Effects (Looping):"      },
8073   { TYPE_SWITCH,        &setup.sound_music,             "Music:"                        },
8074   { TYPE_EMPTY,         NULL,                           ""                              },
8075   { TYPE_ENTER_LIST,    execSetupChooseVolumeSimple,    "Sound Volume (Normal):"        },
8076   { TYPE_STRING,        &volume_simple_text,            ""                              },
8077   { TYPE_ENTER_LIST,    execSetupChooseVolumeLoops,     "Sound Volume (Looping):"       },
8078   { TYPE_STRING,        &volume_loops_text,             ""                              },
8079   { TYPE_ENTER_LIST,    execSetupChooseVolumeMusic,     "Music Volume:"                 },
8080   { TYPE_STRING,        &volume_music_text,             ""                              },
8081   { TYPE_EMPTY,         NULL,                           ""                              },
8082   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8083
8084   { 0,                  NULL,                           NULL                            }
8085 };
8086
8087 static struct TokenInfo setup_info_artwork[] =
8088 {
8089   { TYPE_ENTER_LIST,    execSetupChooseGraphics,        "Custom Graphics:"              },
8090   { TYPE_STRING,        &graphics_set_name,             ""                              },
8091   { TYPE_ENTER_LIST,    execSetupChooseSounds,          "Custom Sounds:"                },
8092   { TYPE_STRING,        &sounds_set_name,               ""                              },
8093   { TYPE_ENTER_LIST,    execSetupChooseMusic,           "Custom Music:"                 },
8094   { TYPE_STRING,        &music_set_name,                ""                              },
8095   { TYPE_EMPTY,         NULL,                           ""                              },
8096   { TYPE_YES_NO_AUTO,   &setup.override_level_graphics,"Override Level Graphics:"       },
8097   { TYPE_YES_NO_AUTO,   &setup.override_level_sounds,   "Override Level Sounds:"        },
8098   { TYPE_YES_NO_AUTO,   &setup.override_level_music,    "Override Level Music:"         },
8099   { TYPE_EMPTY,         NULL,                           ""                              },
8100   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8101
8102   { 0,                  NULL,                           NULL                            }
8103 };
8104
8105 static struct TokenInfo setup_info_input[] =
8106 {
8107   { TYPE_SWITCH,        NULL,                           "Player:"                       },
8108   { TYPE_SWITCH,        NULL,                           "Device:"                       },
8109   { TYPE_SWITCH,        NULL,                           ""                              },
8110   { TYPE_SKIPPABLE,     NULL,                           ""                              },
8111   { TYPE_EMPTY,         NULL,                           ""                              },
8112   { TYPE_EMPTY,         NULL,                           ""                              },
8113   { TYPE_EMPTY,         NULL,                           ""                              },
8114   { TYPE_EMPTY,         NULL,                           ""                              },
8115   { TYPE_EMPTY,         NULL,                           ""                              },
8116   { TYPE_EMPTY,         NULL,                           ""                              },
8117   { TYPE_EMPTY,         NULL,                           ""                              },
8118   { TYPE_EMPTY,         NULL,                           ""                              },
8119   { TYPE_SKIPPABLE,     NULL,                           ""                              },
8120   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8121
8122   { 0,                  NULL,                           NULL                            }
8123 };
8124
8125 static struct TokenInfo setup_info_touch[] =
8126 {
8127   { TYPE_ENTER_LIST,    execSetupChooseTouchControls,   "Touch Control Type:"           },
8128   { TYPE_STRING,        &touch_controls_text,           ""                              },
8129   { TYPE_EMPTY,         NULL,                           ""                              },
8130   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8131
8132   { 0,                  NULL,                           NULL                            }
8133 };
8134
8135 static struct TokenInfo setup_info_touch_virtual_buttons_0[] =
8136 {
8137   { TYPE_ENTER_LIST,    execSetupChooseTouchControls,   "Touch Control Type:"           },
8138   { TYPE_STRING,        &touch_controls_text,           ""                              },
8139   { TYPE_EMPTY,         NULL,                           ""                              },
8140   { TYPE_ENTER_LIST,    execSetupChooseGridXSize_0,     "Horizontal Buttons (Landscape):" },
8141   { TYPE_STRING,        &grid_size_text[0][0],          ""                              },
8142   { TYPE_ENTER_LIST,    execSetupChooseGridYSize_0,     "Vertical Buttons (Landscape):" },
8143   { TYPE_STRING,        &grid_size_text[0][1],          ""                              },
8144   { TYPE_ENTER_LIST,    execSetupChooseTransparency,    "Button Transparency:"          },
8145   { TYPE_STRING,        &transparency_text,             ""                              },
8146   { TYPE_SWITCH,        &setup.touch.draw_outlined,     "Draw Buttons Outlined:"        },
8147   { TYPE_SWITCH,        &setup.touch.draw_pressed,      "Highlight Pressed Buttons:"    },
8148   { TYPE_EMPTY,         NULL,                           ""                              },
8149   { TYPE_ENTER_LIST,    execSetupConfigureVirtualButtons, "Configure Virtual Buttons"   },
8150   { TYPE_EMPTY,         NULL,                           ""                              },
8151   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8152
8153   { 0,                  NULL,                           NULL                            }
8154 };
8155
8156 static struct TokenInfo setup_info_touch_virtual_buttons_1[] =
8157 {
8158   { TYPE_ENTER_LIST,    execSetupChooseTouchControls,   "Touch Control Type:"           },
8159   { TYPE_STRING,        &touch_controls_text,           ""                              },
8160   { TYPE_EMPTY,         NULL,                           ""                              },
8161   { TYPE_ENTER_LIST,    execSetupChooseGridXSize_1,     "Horizontal Buttons (Portrait):" },
8162   { TYPE_STRING,        &grid_size_text[1][0],          ""                              },
8163   { TYPE_ENTER_LIST,    execSetupChooseGridYSize_1,     "Vertical Buttons (Portrait):"  },
8164   { TYPE_STRING,        &grid_size_text[1][1],          ""                              },
8165   { TYPE_ENTER_LIST,    execSetupChooseTransparency,    "Button Transparency:"          },
8166   { TYPE_STRING,        &transparency_text,             ""                              },
8167   { TYPE_SWITCH,        &setup.touch.draw_outlined,     "Draw Buttons Outlined:"        },
8168   { TYPE_SWITCH,        &setup.touch.draw_pressed,      "Highlight Pressed Buttons:"    },
8169   { TYPE_EMPTY,         NULL,                           ""                              },
8170   { TYPE_ENTER_LIST,    execSetupConfigureVirtualButtons, "Configure Virtual Buttons"   },
8171   { TYPE_EMPTY,         NULL,                           ""                              },
8172   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8173
8174   { 0,                  NULL,                           NULL                            }
8175 };
8176
8177 static struct TokenInfo *setup_info_touch_virtual_buttons[] =
8178 {
8179   setup_info_touch_virtual_buttons_0,
8180   setup_info_touch_virtual_buttons_1
8181 };
8182
8183 static struct TokenInfo setup_info_touch_wipe_gestures[] =
8184 {
8185   { TYPE_ENTER_LIST,    execSetupChooseTouchControls,   "Touch Control Type:"           },
8186   { TYPE_STRING,        &touch_controls_text,           ""                              },
8187   { TYPE_EMPTY,         NULL,                           ""                              },
8188   { TYPE_ENTER_LIST,    execSetupChooseMoveDistance,    "Move Trigger Distance:"        },
8189   { TYPE_STRING,        &move_distance_text,            ""                              },
8190   { TYPE_ENTER_LIST,    execSetupChooseDropDistance,    "Drop Trigger Distance:"        },
8191   { TYPE_STRING,        &drop_distance_text,            ""                              },
8192   { TYPE_EMPTY,         NULL,                           ""                              },
8193   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8194
8195   { 0,                  NULL,                           NULL                            }
8196 };
8197
8198 static struct TokenInfo setup_info_shortcuts[] =
8199 {
8200   { TYPE_ENTER_MENU,    execSetupShortcuts1,            "Various Keys"                  },
8201   { TYPE_ENTER_MENU,    execSetupShortcuts2,            "Player Focus"                  },
8202   { TYPE_ENTER_MENU,    execSetupShortcuts3,            "Tape Buttons"                  },
8203   { TYPE_ENTER_MENU,    execSetupShortcuts4,            "Sound & Music"                 },
8204   { TYPE_ENTER_MENU,    execSetupShortcuts5,            "TAS Snap Keys"                 },
8205   { TYPE_EMPTY,         NULL,                           ""                              },
8206   { TYPE_LEAVE_MENU,    execSetupMain,                  "Back"                          },
8207
8208   { 0,                  NULL,                           NULL                            }
8209 };
8210
8211 static struct TokenInfo setup_info_shortcuts_1[] =
8212 {
8213   { TYPE_KEYTEXT,       NULL,                           "Quick Save Game to Tape:"      },
8214   { TYPE_KEY,           &setup.shortcut.save_game,      ""                              },
8215   { TYPE_KEYTEXT,       NULL,                           "Quick Load Game from Tape:"    },
8216   { TYPE_KEY,           &setup.shortcut.load_game,      ""                              },
8217   { TYPE_KEYTEXT,       NULL,                           "Restart Game:"                 },
8218   { TYPE_KEY,           &setup.shortcut.restart_game,   ""                              },
8219   { TYPE_KEYTEXT,       NULL,                           "Replay & Pause Before End:"    },
8220   { TYPE_KEY,           &setup.shortcut.pause_before_end, ""                            },
8221   { TYPE_KEYTEXT,       NULL,                           "Start Game & Toggle Pause:"    },
8222   { TYPE_KEY,           &setup.shortcut.toggle_pause,   ""                              },
8223   { TYPE_EMPTY,         NULL,                           ""                              },
8224   { TYPE_YES_NO,        &setup.ask_on_escape,           "Ask on 'Esc' Key:"             },
8225   { TYPE_YES_NO, &setup.ask_on_escape_editor,           "Ask on 'Esc' Key (Editor):"    },
8226   { TYPE_EMPTY,         NULL,                           ""                              },
8227   { TYPE_LEAVE_MENU,    execSetupShortcuts,             "Back"                          },
8228
8229   { 0,                  NULL,                           NULL                            }
8230 };
8231
8232 static struct TokenInfo setup_info_shortcuts_2[] =
8233 {
8234   { TYPE_KEYTEXT,       NULL,                           "Set Focus to Player 1:"        },
8235   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""                             },
8236   { TYPE_KEYTEXT,       NULL,                           "Set Focus to Player 2:"        },
8237   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""                             },
8238   { TYPE_KEYTEXT,       NULL,                           "Set Focus to Player 3:"        },
8239   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""                             },
8240   { TYPE_KEYTEXT,       NULL,                           "Set Focus to Player 4:"        },
8241   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""                             },
8242   { TYPE_KEYTEXT,       NULL,                           "Set Focus to All Players:"     },
8243   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""                            },
8244   { TYPE_EMPTY,         NULL,                           ""                              },
8245   { TYPE_LEAVE_MENU,    execSetupShortcuts,             "Back"                          },
8246
8247   { 0,                  NULL,                           NULL                            }
8248 };
8249
8250 static struct TokenInfo setup_info_shortcuts_3[] =
8251 {
8252   { TYPE_KEYTEXT,       NULL,                           "Eject Tape:"                   },
8253   { TYPE_KEY,           &setup.shortcut.tape_eject,     ""                              },
8254   { TYPE_KEYTEXT,       NULL,                           "Warp / Single Step:"           },
8255   { TYPE_KEY,           &setup.shortcut.tape_extra,     ""                              },
8256   { TYPE_KEYTEXT,       NULL,                           "Stop Tape:"                    },
8257   { TYPE_KEY,           &setup.shortcut.tape_stop,      ""                              },
8258   { TYPE_KEYTEXT,       NULL,                           "Pause / Unpause Tape:"         },
8259   { TYPE_KEY,           &setup.shortcut.tape_pause,     ""                              },
8260   { TYPE_KEYTEXT,       NULL,                           "Record Tape:"                  },
8261   { TYPE_KEY,           &setup.shortcut.tape_record,    ""                              },
8262   { TYPE_KEYTEXT,       NULL,                           "Play Tape:"                    },
8263   { TYPE_KEY,           &setup.shortcut.tape_play,      ""                              },
8264   { TYPE_EMPTY,         NULL,                           ""                              },
8265   { TYPE_LEAVE_MENU,    execSetupShortcuts,             "Back"                          },
8266
8267   { 0,                  NULL,                           NULL                            }
8268 };
8269
8270 static struct TokenInfo setup_info_shortcuts_4[] =
8271 {
8272   { TYPE_KEYTEXT,       NULL,                           "Toggle Sound Effects (Normal):" },
8273   { TYPE_KEY,           &setup.shortcut.sound_simple,   ""                              },
8274   { TYPE_KEYTEXT,       NULL,                           "Toggle Sound Effects (Looping):" },
8275   { TYPE_KEY,           &setup.shortcut.sound_loops,    ""                              },
8276   { TYPE_KEYTEXT,       NULL,                           "Toggle Music:"                 },
8277   { TYPE_KEY,           &setup.shortcut.sound_music,    ""                              },
8278   { TYPE_EMPTY,         NULL,                           ""                              },
8279   { TYPE_LEAVE_MENU,    execSetupShortcuts,             "Back"                          },
8280
8281   { 0,                  NULL,                           NULL                            }
8282 };
8283
8284 static struct TokenInfo setup_info_shortcuts_5[] =
8285 {
8286   { TYPE_KEYTEXT,       NULL,                           "Snap Left:"                    },
8287   { TYPE_KEY,           &setup.shortcut.snap_left,      ""                              },
8288   { TYPE_KEYTEXT,       NULL,                           "Snap Right:"                   },
8289   { TYPE_KEY,           &setup.shortcut.snap_right,     ""                              },
8290   { TYPE_KEYTEXT,       NULL,                           "Snap Up:"                      },
8291   { TYPE_KEY,           &setup.shortcut.snap_up,        ""                              },
8292   { TYPE_KEYTEXT,       NULL,                           "Snap Down:"                    },
8293   { TYPE_KEY,           &setup.shortcut.snap_down,      ""                              },
8294   { TYPE_EMPTY,         NULL,                           ""                              },
8295   { TYPE_LEAVE_MENU,    execSetupShortcuts,             "Back"                          },
8296
8297   { 0,                  NULL,                           NULL                            }
8298 };
8299
8300 static Key getSetupKey(void)
8301 {
8302   Key key = KSYM_UNDEFINED;
8303   boolean got_key_event = FALSE;
8304
8305   while (!got_key_event)
8306   {
8307     Event event;
8308
8309     if (NextValidEvent(&event))
8310     {
8311       switch (event.type)
8312       {
8313         case EVENT_KEYPRESS:
8314           {
8315             key = GetEventKey((KeyEvent *)&event);
8316
8317             // press 'Escape' or 'Enter' to keep the existing key binding
8318             if (key == KSYM_Escape || key == KSYM_Return)
8319               key = KSYM_UNDEFINED;     // keep old value
8320
8321             got_key_event = TRUE;
8322           }
8323           break;
8324
8325         case EVENT_KEYRELEASE:
8326           key_joystick_mapping = 0;
8327           break;
8328
8329         default:
8330           HandleOtherEvents(&event);
8331           break;
8332       }
8333     }
8334
8335     BackToFront();
8336   }
8337
8338   return key;
8339 }
8340
8341 static int getSetupValueFont(int type, void *value)
8342 {
8343   if (type & TYPE_GHOSTED)
8344     return FONT_OPTION_OFF;
8345   else if (type & TYPE_KEY)
8346     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
8347   else if (type & TYPE_STRING)
8348     return FONT_VALUE_2;
8349   else if (type & TYPE_ECS_AGA)
8350     return FONT_VALUE_1;
8351   else if (type & TYPE_BOOLEAN_STYLE)
8352     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
8353   else if (type & TYPE_YES_NO_AUTO)
8354     return (*(int *)value == AUTO  ? FONT_OPTION_ON :
8355             *(int *)value == FALSE ? FONT_OPTION_OFF : FONT_OPTION_ON);
8356   else if (type & TYPE_PLAYER)
8357     return FONT_VALUE_1;
8358   else
8359     return FONT_VALUE_1;
8360 }
8361
8362 static int getSetupValueFontNarrow(int type, int font_nr)
8363 {
8364   return (font_nr == FONT_VALUE_1    ? FONT_VALUE_NARROW :
8365           font_nr == FONT_OPTION_ON  ? FONT_OPTION_ON_NARROW :
8366           font_nr == FONT_OPTION_OFF ? FONT_OPTION_OFF_NARROW :
8367           font_nr);
8368 }
8369
8370 static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
8371 {
8372   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
8373   struct TokenInfo *si = &setup_info[si_pos];
8374   boolean font_draw_xoffset_modified = FALSE;
8375   boolean scrollbar_needed = (num_setup_info < max_setup_info);
8376   int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
8377   int mx_right_border = (scrollbar_needed ? mx_scrollbar : SX + SXSIZE);
8378   int font_draw_xoffset_old = -1;
8379   int xoffset = (scrollbar_needed ? 0 : 1);
8380   int menu_screen_value_xpos = MENU_SCREEN_VALUE_XPOS + xoffset;
8381   int xpos = menu_screen_value_xpos;
8382   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
8383   int startx = mSX + xpos * 32;
8384   int starty = mSY + ypos * 32;
8385   int type = si->type;
8386   void *value = si->value;
8387   char *value_string = getSetupValue(type, value);
8388   int font_nr_default = getSetupValueFont(type, value);
8389   int font_width_default = getFontWidth(font_nr_default);
8390   int font_nr = font_nr_default;
8391
8392   if (value_string == NULL)
8393     return;
8394
8395   if (type & TYPE_KEY)
8396   {
8397     xpos = MENU_SCREEN_START_XPOS;
8398
8399     if (type & TYPE_QUERY)
8400       value_string = "<press key>";
8401   }
8402   else if (type & TYPE_STRING)
8403   {
8404     int max_value_len = (SXSIZE - 2 * TILEX) / font_width_default;
8405
8406     xpos = MENU_SCREEN_START_XPOS;
8407
8408     if (strlen(value_string) > max_value_len)
8409       value_string[max_value_len] = '\0';
8410   }
8411   else if (type & TYPE_PLAYER)
8412   {
8413     int displayed_player_nr = *(int *)value + 1;
8414
8415     value_string = getSetupValue(TYPE_INTEGER, (void *)&displayed_player_nr);
8416   }
8417
8418   startx = mSX + xpos * 32;
8419   starty = mSY + ypos * 32;
8420
8421   // always use narrow font for setup values on right screen side
8422   if (xpos > MENU_SCREEN_START_XPOS)
8423     font_nr = getSetupValueFontNarrow(type, font_nr);
8424
8425   // downward compatibility correction for Juergen Bonhagen's menu settings
8426   if (setup_mode != SETUP_MODE_INPUT)
8427   {
8428     int max_menu_text_length_big = (menu_screen_value_xpos -
8429                                     MENU_SCREEN_START_XPOS);
8430     int max_menu_text_length_medium = max_menu_text_length_big * 2;
8431     int check_font_nr = FONT_OPTION_ON; // known font that needs correction
8432     int font1_xoffset = getFontDrawOffsetX(font_nr);
8433     int font2_xoffset = getFontDrawOffsetX(check_font_nr);
8434     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
8435     int text_font_nr = getMenuTextFont(FONT_MENU_2);
8436     int text_font_xoffset = getFontDrawOffsetX(text_font_nr);
8437     int text_width = max_menu_text_length_medium * getFontWidth(text_font_nr);
8438     boolean correct_font_draw_xoffset = FALSE;
8439
8440     if (xpos == MENU_SCREEN_START_XPOS &&
8441         startx + font1_xoffset < text_startx + text_font_xoffset)
8442       correct_font_draw_xoffset = TRUE;
8443
8444     if (xpos == menu_screen_value_xpos &&
8445         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
8446       correct_font_draw_xoffset = TRUE;
8447
8448     // check if setup value would overlap with setup text when printed
8449     // (this can happen for extreme/wrong values for font draw offset)
8450     if (correct_font_draw_xoffset)
8451     {
8452       font_draw_xoffset_old = getFontDrawOffsetX(font_nr);
8453       font_draw_xoffset_modified = TRUE;
8454
8455       if (type & TYPE_KEY)
8456         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
8457       else if (!(type & TYPE_STRING))
8458         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
8459           max_menu_text_length_medium * (16 - getFontWidth(text_font_nr));
8460     }
8461   }
8462
8463   DrawBackground(startx, starty, mx_right_border - startx, getFontHeight(font_nr));
8464   DrawText(startx, starty, value_string, font_nr);
8465
8466   if (type & TYPE_PLAYER)
8467   {
8468     struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
8469     int player_nr = *(int *)value;
8470     int xoff = font->draw_xoffset + getFontWidth(font_nr);
8471     int yoff = font->draw_yoffset + (getFontHeight(font_nr) - TILEY) / 2;
8472     int startx2 = startx + xoff;
8473     int starty2 = starty + yoff;
8474
8475     if (DrawingOnBackground(startx2, starty2))
8476       ClearRectangleOnBackground(drawto, startx2, starty2, TILEX, TILEY);
8477
8478     DrawFixedGraphicThruMaskExt(drawto, startx2, starty2,
8479                                 PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
8480   }
8481
8482   if (font_draw_xoffset_modified)
8483     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
8484 }
8485
8486 static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
8487 {
8488   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
8489   struct TokenInfo *si = &setup_info[si_pos];
8490
8491   if (si->type & TYPE_BOOLEAN_STYLE)
8492   {
8493     *(boolean *)si->value ^= TRUE;
8494   }
8495   else if (si->type & TYPE_YES_NO_AUTO)
8496   {
8497     *(int *)si->value =
8498       (dx == -1 ?
8499        (*(int *)si->value == AUTO ? TRUE :
8500         *(int *)si->value == TRUE ? FALSE : AUTO) :
8501        (*(int *)si->value == TRUE ? AUTO :
8502         *(int *)si->value == AUTO ? FALSE : TRUE));
8503   }
8504   else if (si->type & TYPE_KEY)
8505   {
8506     Key key;
8507
8508     si->type |= TYPE_QUERY;
8509     drawSetupValue(screen_pos, setup_info_pos_raw);
8510     si->type &= ~TYPE_QUERY;
8511
8512     key = getSetupKey();
8513     if (key != KSYM_UNDEFINED)
8514       *(Key *)si->value = key;
8515   }
8516   else if (si->type & TYPE_PLAYER)
8517   {
8518     int player_nr = *(int *)si->value;
8519
8520     if (dx)
8521       player_nr += dx;
8522     else
8523       player_nr = Request("Choose player", REQ_PLAYER) - 1;
8524
8525     *(int *)si->value = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
8526   }
8527
8528   drawSetupValue(screen_pos, setup_info_pos_raw);
8529
8530   // fullscreen state may have changed at this point
8531   if (si->value == &setup.fullscreen)
8532     ToggleFullscreenIfNeeded();
8533
8534   // network mode may have changed at this point
8535   if (si->value == &setup.network_mode)
8536     ToggleNetworkModeIfNeeded();
8537
8538   // API server mode may have changed at this point
8539   if (si->value == &setup.use_api_server)
8540     ToggleUseApiServerIfNeeded();
8541
8542   // game speed list may have changed at this point
8543   if (si->value == &setup.game_speed_extended)
8544     ToggleGameSpeedsListIfNeeded();
8545 }
8546
8547 static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *setup_info_orig)
8548 {
8549   static struct TokenInfo *setup_info_final = NULL;
8550   int list_size = 0;
8551   int list_pos = 0;
8552   int i;
8553
8554   // determine maximum list size of target list
8555   while (setup_info_orig[list_size++].type != 0);
8556
8557   // free, allocate and clear memory for target list
8558   checked_free(setup_info_final);
8559   setup_info_final = checked_calloc(list_size * sizeof(struct TokenInfo));
8560
8561   // copy setup info list without setup entries marked as hidden
8562   for (i = 0; setup_info_orig[i].type != 0; i++)
8563   {
8564     // skip setup entries configured to be hidden
8565     if (hideSetupEntry(setup_info_orig[i].value))
8566       continue;
8567
8568     // skip skippable setup entries if screen is lower than usual
8569     if (SCR_FIELDY < SCR_FIELDY_DEFAULT &&
8570         setup_info_orig[i].type == TYPE_SKIPPABLE)
8571       continue;
8572
8573     setup_info_final[list_pos++] = setup_info_orig[i];
8574   }
8575
8576   return setup_info_final;
8577 }
8578
8579 static void DrawSetupScreen_Generic(void)
8580 {
8581   int fade_mask = REDRAW_FIELD;
8582   boolean redraw_all = FALSE;
8583   char *title_string = NULL;
8584   int i;
8585
8586   if (CheckFadeAll())
8587     fade_mask = REDRAW_ALL;
8588
8589   UnmapAllGadgets();
8590   FadeMenuSoundsAndMusic();
8591
8592   FreeScreenGadgets();
8593   CreateScreenGadgets();
8594
8595   if (redraw_mask & REDRAW_ALL)
8596     redraw_all = TRUE;
8597
8598   FadeOut(fade_mask);
8599
8600   // needed if different viewport properties defined for setup screen
8601   ChangeViewportPropertiesIfNeeded();
8602
8603   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
8604
8605   ClearField();
8606
8607   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
8608
8609   if (setup_mode == SETUP_MODE_MAIN)
8610   {
8611     setup_info = setup_info_main;
8612     title_string = STR_SETUP_MAIN;
8613   }
8614   else if (setup_mode == SETUP_MODE_GAME)
8615   {
8616     setup_info = setup_info_game;
8617     title_string = STR_SETUP_GAME;
8618   }
8619   else if (setup_mode == SETUP_MODE_ENGINES)
8620   {
8621     setup_info = setup_info_engines;
8622     title_string = STR_SETUP_ENGINES;
8623   }
8624   else if (setup_mode == SETUP_MODE_EDITOR)
8625   {
8626     setup_info = setup_info_editor;
8627     title_string = STR_SETUP_EDITOR;
8628   }
8629   else if (setup_mode == SETUP_MODE_GRAPHICS)
8630   {
8631     setup_info = setup_info_graphics;
8632     title_string = STR_SETUP_GRAPHICS;
8633   }
8634   else if (setup_mode == SETUP_MODE_SOUND)
8635   {
8636     setup_info = setup_info_sound;
8637     title_string = STR_SETUP_SOUND;
8638   }
8639   else if (setup_mode == SETUP_MODE_ARTWORK)
8640   {
8641     setup_info = setup_info_artwork;
8642     title_string = STR_SETUP_ARTWORK;
8643   }
8644   else if (setup_mode == SETUP_MODE_TOUCH)
8645   {
8646     setup_info = setup_info_touch;
8647     title_string = STR_SETUP_TOUCH;
8648
8649     if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
8650       setup_info = setup_info_touch_virtual_buttons[GRID_ACTIVE_NR()];
8651     else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
8652       setup_info = setup_info_touch_wipe_gestures;
8653   }
8654   else if (setup_mode == SETUP_MODE_SHORTCUTS)
8655   {
8656     setup_info = setup_info_shortcuts;
8657     title_string = STR_SETUP_SHORTCUTS;
8658   }
8659   else if (setup_mode == SETUP_MODE_SHORTCUTS_1)
8660   {
8661     setup_info = setup_info_shortcuts_1;
8662     title_string = STR_SETUP_SHORTCUTS;
8663   }
8664   else if (setup_mode == SETUP_MODE_SHORTCUTS_2)
8665   {
8666     setup_info = setup_info_shortcuts_2;
8667     title_string = STR_SETUP_SHORTCUTS;
8668   }
8669   else if (setup_mode == SETUP_MODE_SHORTCUTS_3)
8670   {
8671     setup_info = setup_info_shortcuts_3;
8672     title_string = STR_SETUP_SHORTCUTS;
8673   }
8674   else if (setup_mode == SETUP_MODE_SHORTCUTS_4)
8675   {
8676     setup_info = setup_info_shortcuts_4;
8677     title_string = STR_SETUP_SHORTCUTS;
8678   }
8679   else if (setup_mode == SETUP_MODE_SHORTCUTS_5)
8680   {
8681     setup_info = setup_info_shortcuts_5;
8682     title_string = STR_SETUP_SHORTCUTS;
8683   }
8684
8685   // use modified setup info without setup entries marked as hidden
8686   setup_info = getSetupInfoFinal(setup_info);
8687
8688   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
8689
8690   // determine maximal number of setup entries that can be displayed on screen
8691   num_setup_info = 0;
8692   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
8693     num_setup_info++;
8694
8695   // determine maximal number of setup entries available for this setup screen
8696   max_setup_info = 0;
8697   for (i = 0; setup_info[i].type != 0; i++)
8698     max_setup_info++;
8699
8700   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
8701
8702   MapScreenGadgets(max_setup_info);
8703
8704   if (redraw_all)
8705     redraw_mask = fade_mask = REDRAW_ALL;
8706
8707   DrawMaskedBorder(fade_mask);
8708
8709   FadeIn(fade_mask);
8710 }
8711
8712 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
8713 {
8714   menu_info = setup_info;
8715
8716   HandleMenuScreen(mx, my, dx, dy, button,
8717                    setup_mode, num_setup_info, max_setup_info);
8718 }
8719
8720 static void DrawSetupScreen_Input(void)
8721 {
8722   int i;
8723
8724   FadeOut(REDRAW_FIELD);
8725
8726   ClearField();
8727
8728   setup_info = getSetupInfoFinal(setup_info_input);
8729
8730   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, STR_SETUP_INPUT);
8731
8732   for (i = 0; setup_info[i].type != 0; i++)
8733   {
8734     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
8735       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
8736     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
8737       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
8738     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
8739       initCursor(i, IMG_MENU_BUTTON);
8740
8741     DrawCursorAndText_Setup(i, -1, FALSE);
8742   }
8743
8744   // create gadgets for setup input menu screen
8745   FreeScreenGadgets();
8746   CreateScreenGadgets();
8747
8748   // map gadgets for setup input menu screen
8749   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
8750
8751   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
8752
8753   FadeIn(REDRAW_FIELD);
8754 }
8755
8756 static void setJoystickDeviceToNr(char *device_name, int device_nr)
8757 {
8758   if (device_name == NULL)
8759     return;
8760
8761   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
8762     device_nr = 0;
8763
8764   if (strlen(device_name) > 1)
8765   {
8766     char c1 = device_name[strlen(device_name) - 1];
8767     char c2 = device_name[strlen(device_name) - 2];
8768
8769     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
8770       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
8771   }
8772   else
8773     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
8774             strlen(device_name));
8775 }
8776
8777 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
8778 {
8779   int i;
8780   static struct SetupKeyboardInfo custom_key;
8781   static struct
8782   {
8783     Key *key;
8784     char *text;
8785   } custom[] =
8786   {
8787     { &custom_key.left,  "Axis/Pad Left"  },
8788     { &custom_key.right, "Axis/Pad Right" },
8789     { &custom_key.up,    "Axis/Pad Up"    },
8790     { &custom_key.down,  "Axis/Pad Down"  },
8791     { &custom_key.snap,  "Button 1/A/X"   },
8792     { &custom_key.drop,  "Button 2/B/Y"   }
8793   };
8794   static char *joystick_name[MAX_PLAYERS] =
8795   {
8796     "Joystick1",
8797     "Joystick2",
8798     "Joystick3",
8799     "Joystick4"
8800   };
8801   int font_nr_menu = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
8802   int font_nr_info = FONT_MENU_1;
8803   int font_nr_name = FONT_VALUE_OLD;
8804   int font_nr_on   = FONT_VALUE_1;
8805   int font_nr_off  = FONT_VALUE_OLD;
8806   int pos = 4;
8807
8808   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
8809   {
8810     font_nr_info = FONT_MENU_2;
8811     font_nr_on   = FONT_VALUE_NARROW;
8812     font_nr_off  = FONT_VALUE_OLD_NARROW;
8813   }
8814
8815   custom_key = setup.input[player_nr].key;
8816
8817   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
8818            FONT_INPUT_1_ACTIVE);
8819
8820   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
8821                              TILEX, TILEY);
8822   DrawFixedGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
8823                               PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
8824
8825   if (setup.input[player_nr].use_joystick)
8826   {
8827     char *device_name = setup.input[player_nr].joy.device_name;
8828     int joystick_nr = getJoystickNrFromDeviceName(device_name);
8829     boolean joystick_active = CheckJoystickOpened(joystick_nr);
8830     char *text = joystick_name[joystick_nr];
8831     int font_nr = (joystick_active ? font_nr_on : font_nr_off);
8832
8833     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
8834     DrawText(mSX + 32, mSY + 4 * 32, "Configure", font_nr_menu);
8835   }
8836   else
8837   {
8838     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", font_nr_on);
8839     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", font_nr_menu);
8840   }
8841
8842   if (SCR_FIELDY >= SCR_FIELDY_DEFAULT)
8843     DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", font_nr_info);
8844   else
8845     pos = 3;
8846
8847   drawCursorXY(1, pos + 0, IMG_MENU_BUTTON_LEFT);
8848   drawCursorXY(1, pos + 1, IMG_MENU_BUTTON_RIGHT);
8849   drawCursorXY(1, pos + 2, IMG_MENU_BUTTON_UP);
8850   drawCursorXY(1, pos + 3, IMG_MENU_BUTTON_DOWN);
8851
8852   DrawText(mSX + 2 * 32, mSY + (pos + 2) * 32, ":", font_nr_name);
8853   DrawText(mSX + 2 * 32, mSY + (pos + 3) * 32, ":", font_nr_name);
8854   DrawText(mSX + 2 * 32, mSY + (pos + 4) * 32, ":", font_nr_name);
8855   DrawText(mSX + 2 * 32, mSY + (pos + 5) * 32, ":", font_nr_name);
8856   DrawText(mSX + 1 * 32, mSY + (pos + 6) * 32, "Snap Field:", font_nr_name);
8857   DrawText(mSX + 1 * 32, mSY + (pos + 8) * 32, "Drop Element:", font_nr_name);
8858
8859   for (i = 0; i < 6; i++)
8860   {
8861     int ypos = (pos + 2) + i + (i > 3 ? i - 3 : 0);
8862
8863     DrawText(mSX + 3 * 32, mSY + ypos * 32,
8864              "              ", font_nr_on);
8865     DrawText(mSX + 3 * 32, mSY + ypos * 32,
8866              (setup.input[player_nr].use_joystick ?
8867               custom[i].text :
8868               getKeyNameFromKey(*custom[i].key)), font_nr_on);
8869   }
8870 }
8871
8872 static int input_player_nr = 0;
8873
8874 static void HandleSetupScreen_Input_Player(int step, int direction)
8875 {
8876   int old_player_nr = input_player_nr;
8877   int new_player_nr;
8878
8879   new_player_nr = old_player_nr + step * direction;
8880   if (new_player_nr < 0)
8881     new_player_nr = 0;
8882   if (new_player_nr > MAX_PLAYERS - 1)
8883     new_player_nr = MAX_PLAYERS - 1;
8884
8885   if (new_player_nr != old_player_nr)
8886   {
8887     input_player_nr = new_player_nr;
8888
8889     drawPlayerSetupInputInfo(input_player_nr, FALSE);
8890   }
8891 }
8892
8893 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
8894 {
8895   static int choice = 0;
8896   int x = 0;
8897   int y = choice;
8898   int pos_start  = SETUPINPUT_SCREEN_POS_START;
8899   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
8900   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
8901   int pos_end    = SETUPINPUT_SCREEN_POS_END;
8902
8903   if (SCR_FIELDY < SCR_FIELDY_DEFAULT)
8904   {
8905     int i;
8906
8907     for (i = 0; setup_info_input[i].type != 0; i++)
8908     {
8909       // adjust menu structure according to skipped setup entries
8910       if (setup_info_input[i].type == TYPE_SKIPPABLE)
8911       {
8912         pos_empty2--;
8913         pos_end--;
8914       }
8915     }
8916   }
8917
8918   if (button == MB_MENU_INITIALIZE)
8919   {
8920     // input setup menu may have changed size due to graphics configuration
8921     if (choice >= pos_empty1)
8922       choice = pos_end;
8923
8924     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
8925
8926     DrawCursorAndText_Setup(choice, -1, TRUE);
8927
8928     return;
8929   }
8930   else if (button == MB_MENU_LEAVE)
8931   {
8932     setup_mode = SETUP_MODE_MAIN;
8933     DrawSetupScreen();
8934     InitJoysticks();
8935
8936     return;
8937   }
8938
8939   if (mx || my)         // mouse input
8940   {
8941     x = (mx - mSX) / 32;
8942     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
8943   }
8944   else if (dx || dy)    // keyboard input
8945   {
8946     if (dx && choice == 0)
8947       x = (dx < 0 ? 10 : 12);
8948     else if ((dx && choice == 1) ||
8949              (dx == -1 && choice == pos_end))
8950       button = MB_MENU_CHOICE;
8951     else if (dy)
8952       y = choice + dy;
8953
8954     if (y >= pos_empty1 && y <= pos_empty2)
8955       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
8956   }
8957
8958   if (y == 0 && dx != 0 && button)
8959   {
8960     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
8961   }
8962   else if (IN_VIS_FIELD(x, y) &&        // (does not use "IN_VIS_MENU()" yet)
8963            y >= pos_start && y <= pos_end &&
8964            !(y >= pos_empty1 && y <= pos_empty2))
8965   {
8966     if (button)
8967     {
8968       if (y != choice)
8969       {
8970         DrawCursorAndText_Setup(choice, -1, FALSE);
8971         DrawCursorAndText_Setup(y, -1, TRUE);
8972
8973         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
8974
8975         choice = y;
8976       }
8977     }
8978     else
8979     {
8980       if (y == 1)
8981       {
8982         char *device_name = setup.input[input_player_nr].joy.device_name;
8983
8984         if (!setup.input[input_player_nr].use_joystick)
8985         {
8986           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
8987
8988           setJoystickDeviceToNr(device_name, new_device_nr);
8989           setup.input[input_player_nr].use_joystick = TRUE;
8990         }
8991         else
8992         {
8993           int device_nr = getJoystickNrFromDeviceName(device_name);
8994           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
8995
8996           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
8997             setup.input[input_player_nr].use_joystick = FALSE;
8998           else
8999             setJoystickDeviceToNr(device_name, new_device_nr);
9000         }
9001
9002         drawPlayerSetupInputInfo(input_player_nr, FALSE);
9003       }
9004       else if (y == 2)
9005       {
9006         if (setup.input[input_player_nr].use_joystick)
9007           ConfigureJoystick(input_player_nr);
9008         else
9009           CustomizeKeyboard(input_player_nr);
9010       }
9011       else if (y == pos_end)
9012       {
9013         InitJoysticks();
9014
9015         FadeSetLeaveMenu();
9016
9017         setup_mode = SETUP_MODE_MAIN;
9018         DrawSetupScreen();
9019       }
9020     }
9021   }
9022 }
9023
9024 static boolean CustomizeKeyboardMain(int player_nr)
9025 {
9026   int i;
9027   int step_nr;
9028   boolean finished = FALSE;
9029   static struct SetupKeyboardInfo custom_key;
9030   static struct
9031   {
9032     Key *key;
9033     char *text;
9034   } customize_step[] =
9035   {
9036     { &custom_key.left,  "Move Left"    },
9037     { &custom_key.right, "Move Right"   },
9038     { &custom_key.up,    "Move Up"      },
9039     { &custom_key.down,  "Move Down"    },
9040     { &custom_key.snap,  "Snap Field"   },
9041     { &custom_key.drop,  "Drop Element" }
9042   };
9043   int font_nr_old = FONT_VALUE_OLD;
9044   int font_nr_new = FONT_VALUE_1;
9045   boolean success = FALSE;
9046
9047   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
9048   {
9049     font_nr_old = FONT_VALUE_OLD_NARROW;
9050     font_nr_new = FONT_VALUE_NARROW;
9051   }
9052
9053   // read existing key bindings from player setup
9054   custom_key = setup.input[player_nr].key;
9055
9056   FadeSetEnterMenu();
9057   FadeOut(REDRAW_FIELD);
9058
9059   ClearField();
9060
9061   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
9062
9063   step_nr = 0;
9064   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
9065            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
9066   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
9067            "Key:", FONT_INPUT_1_ACTIVE);
9068   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
9069            getKeyNameFromKey(*customize_step[step_nr].key), font_nr_old);
9070
9071   FadeIn(REDRAW_FIELD);
9072
9073   while (!finished)
9074   {
9075     Event event;
9076     DelayCounter event_frame_delay = { GAME_FRAME_DELAY };
9077
9078     // reset frame delay counter directly after updating screen
9079     ResetDelayCounter(&event_frame_delay);
9080
9081     while (NextValidEvent(&event))
9082     {
9083       switch (event.type)
9084       {
9085         case EVENT_KEYPRESS:
9086           {
9087             Key key = GetEventKey((KeyEvent *)&event);
9088
9089             // press 'Escape' to abort and keep the old key bindings
9090             if (key == KSYM_Escape)
9091             {
9092               FadeSkipNextFadeIn();
9093
9094               finished = TRUE;
9095
9096               break;
9097             }
9098
9099             // press 'Enter' to keep the existing key binding
9100             if (key == KSYM_Return)
9101               key = *customize_step[step_nr].key;
9102
9103             // check if key already used
9104             for (i = 0; i < step_nr; i++)
9105               if (*customize_step[i].key == key)
9106                 break;
9107             if (i < step_nr)
9108               break;
9109
9110             // got new key binding
9111             *customize_step[step_nr].key = key;
9112             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
9113                      "             ", font_nr_new);
9114             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
9115                      getKeyNameFromKey(key), font_nr_new);
9116             step_nr++;
9117
9118             // un-highlight last query
9119             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
9120                      customize_step[step_nr - 1].text, FONT_MENU_1);
9121             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
9122                      "Key:", FONT_MENU_1);
9123
9124             // all keys configured
9125             if (step_nr == 6)
9126             {
9127               finished = TRUE;
9128               success = TRUE;
9129
9130               break;
9131             }
9132
9133             // query next key binding
9134             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
9135                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
9136             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
9137                      "Key:", FONT_INPUT_1_ACTIVE);
9138             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
9139                      getKeyNameFromKey(*customize_step[step_nr].key),
9140                      font_nr_old);
9141           }
9142           break;
9143
9144         case EVENT_KEYRELEASE:
9145           key_joystick_mapping = 0;
9146           break;
9147
9148         default:
9149           HandleOtherEvents(&event);
9150           break;
9151       }
9152
9153       // do not handle events for longer than standard frame delay period
9154       if (DelayReached(&event_frame_delay))
9155         break;
9156     }
9157
9158     BackToFront();
9159   }
9160
9161   // write new key bindings back to player setup, if successfully finished
9162   if (success)
9163     setup.input[player_nr].key = custom_key;
9164
9165   return success;
9166 }
9167
9168 void CustomizeKeyboard(int player_nr)
9169 {
9170   boolean success = CustomizeKeyboardMain(player_nr);
9171
9172   if (success)
9173   {
9174     int font_nr = FONT_TITLE_1;
9175     int font_height = getFontHeight(font_nr);
9176     int ypos1 = SYSIZE / 2 - font_height * 2;
9177     int ypos2 = SYSIZE / 2 - font_height * 1;
9178     DelayCounter wait_frame_delay = { 2000 };
9179
9180     ResetDelayCounter(&wait_frame_delay);
9181
9182     ClearField();
9183
9184     DrawTextSCentered(ypos1, font_nr, "Keyboard");
9185     DrawTextSCentered(ypos2, font_nr, "configured!");
9186
9187     while (!DelayReached(&wait_frame_delay))
9188       BackToFront();
9189
9190     ClearEventQueue();
9191   }
9192
9193   DrawSetupScreen_Input();
9194 }
9195
9196 // game controller mapping generator by Gabriel Jacobo <gabomdq@gmail.com>
9197
9198 #define MARKER_BUTTON           1
9199 #define MARKER_AXIS_X           2
9200 #define MARKER_AXIS_Y           3
9201
9202 static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
9203 {
9204   static boolean bitmaps_initialized = FALSE;
9205   boolean screen_initialized = FALSE;
9206   static Bitmap *controller, *button, *axis_x, *axis_y;
9207   char *name;
9208   boolean success = TRUE;
9209   boolean done = FALSE, next = FALSE;
9210   Event event;
9211   int alpha = 200, alpha_step = -1;
9212   int alpha_ticks = 0;
9213   char mapping[4096], temp[256];
9214   int font_name = MENU_SETUP_FONT_TITLE;
9215   int font_info = MENU_SETUP_FONT_TEXT;
9216   int spacing_name = menu.line_spacing_setup[SETUP_MODE_INPUT];
9217   int spacing_line = menu.line_spacing_setup[SETUP_MODE_INPUT];
9218   int spacing_para = menu.paragraph_spacing_setup[SETUP_MODE_INPUT];
9219   int ystep_name = getMenuTextStep(spacing_name, font_name);
9220   int ystep_line = getMenuTextStep(spacing_line, font_info);
9221   int ystep_para = getMenuTextStep(spacing_para, font_info);
9222   int i, j;
9223
9224   struct
9225   {
9226     int x, y;
9227     int marker;
9228     char *field;
9229     int axis, button, hat, hat_value;
9230     char mapping[4096];
9231   }
9232   *step, *prev_step, steps[] =
9233   {
9234     { 356, 155, MARKER_BUTTON, "a",             },
9235     { 396, 122, MARKER_BUTTON, "b",             },
9236     { 320, 125, MARKER_BUTTON, "x",             },
9237     { 358,  95, MARKER_BUTTON, "y",             },
9238     { 162, 125, MARKER_BUTTON, "back",          },
9239     { 216, 125, MARKER_BUTTON, "guide",         },
9240     { 271, 125, MARKER_BUTTON, "start",         },
9241     { 110, 200, MARKER_BUTTON, "dpleft",        },
9242     { 146, 228, MARKER_BUTTON, "dpdown",        },
9243     { 178, 200, MARKER_BUTTON, "dpright",       },
9244     { 146, 172, MARKER_BUTTON, "dpup",          },
9245     {  50,  40, MARKER_BUTTON, "leftshoulder",  },
9246     {  88, -10, MARKER_AXIS_Y, "lefttrigger",   },
9247     { 382,  40, MARKER_BUTTON, "rightshoulder", },
9248     { 346, -10, MARKER_AXIS_Y, "righttrigger",  },
9249     {  73, 141, MARKER_BUTTON, "leftstick",     },
9250     { 282, 210, MARKER_BUTTON, "rightstick",    },
9251     {  73, 141, MARKER_AXIS_X, "leftx",         },
9252     {  73, 141, MARKER_AXIS_Y, "lefty",         },
9253     { 282, 210, MARKER_AXIS_X, "rightx",        },
9254     { 282, 210, MARKER_AXIS_Y, "righty",        },
9255   };
9256
9257   if (!bitmaps_initialized)
9258   {
9259     controller = LoadCustomImage("joystick/controller.png");
9260     button     = LoadCustomImage("joystick/button.png");
9261     axis_x     = LoadCustomImage("joystick/axis_x.png");
9262     axis_y     = LoadCustomImage("joystick/axis_y.png");
9263
9264     bitmaps_initialized = TRUE;
9265   }
9266
9267   name = getFormattedJoystickName(SDL_JoystickName(joystick));
9268
9269 #if DEBUG_JOYSTICKS
9270   // print info about the joystick we are watching
9271   Debug("joystick", "watching joystick %d: (%s)",
9272         SDL_JoystickInstanceID(joystick), name);
9273   Debug("joystick", "joystick has %d axes, %d hats, %d balls, and %d buttons",
9274         SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
9275         SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
9276 #endif
9277
9278   // initialize mapping with GUID and name
9279   SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, sizeof(temp));
9280
9281   snprintf(mapping, sizeof(mapping), "%s,%s,platform:%s,",
9282            temp, name, SDL_GetPlatform());
9283
9284   // loop through all steps (buttons and axes), getting joystick events
9285   for (i = 0; i < SDL_arraysize(steps) && !done;)
9286   {
9287     Bitmap *marker = button;    // initialize with reliable default value
9288
9289     step = &steps[i];
9290     strcpy(step->mapping, mapping);
9291     step->axis = -1;
9292     step->button = -1;
9293     step->hat = -1;
9294     step->hat_value = -1;
9295
9296     marker = (step->marker == MARKER_BUTTON ? button :
9297               step->marker == MARKER_AXIS_X ? axis_x :
9298               step->marker == MARKER_AXIS_Y ? axis_y : marker);
9299
9300     next = FALSE;
9301
9302     while (!done && !next)
9303     {
9304       alpha += alpha_step * (int)(SDL_GetTicks() - alpha_ticks) / 5;
9305       alpha_ticks = SDL_GetTicks();
9306
9307       if (alpha >= 255)
9308       {
9309         alpha = 255;
9310         alpha_step = -1;
9311       }
9312       else if (alpha < 128)
9313       {
9314         alpha = 127;
9315         alpha_step = 1;
9316       }
9317
9318       int controller_x = SX + (SXSIZE - controller->width) / 2;
9319       int controller_y = SY + ystep_line;
9320
9321       int marker_x = controller_x + step->x;
9322       int marker_y = controller_y + step->y;
9323
9324       int ystart1 = mSY - 2 * SY + controller_y + controller->height;
9325       int ystart2 = ystart1 + ystep_name + ystep_line;
9326
9327       ClearField();
9328
9329       DrawTextSCentered(ystart1, font_name, name);
9330
9331       DrawTextSCentered(ystart2, font_info,
9332                         "Press buttons and move axes on");
9333       ystart2 += ystep_line;
9334       DrawTextSCentered(ystart2, font_info,
9335                         "your controller when indicated.");
9336       ystart2 += ystep_line;
9337       DrawTextSCentered(ystart2, font_info,
9338                         "(Your controller may look different.)");
9339       ystart2 += ystep_para;
9340
9341 #if defined(PLATFORM_ANDROID)
9342       DrawTextSCentered(ystart2, font_info,
9343                         "To correct a mistake,");
9344       ystart2 += ystep_line;
9345       DrawTextSCentered(ystart2, font_info,
9346                         "press the 'back' button.");
9347       ystart2 += ystep_line;
9348       DrawTextSCentered(ystart2, font_info,
9349                         "To skip a button or axis,");
9350       ystart2 += ystep_line;
9351       DrawTextSCentered(ystart2, font_info,
9352                         "press the 'menu' button.");
9353 #else
9354       DrawTextSCentered(ystart2, font_info,
9355                         "To correct a mistake,");
9356       ystart2 += ystep_line;
9357       DrawTextSCentered(ystart2, font_info,
9358                         "press the 'backspace' key.");
9359       ystart2 += ystep_line;
9360       DrawTextSCentered(ystart2, font_info,
9361                         "To skip a button or axis,");
9362       ystart2 += ystep_line;
9363       DrawTextSCentered(ystart2, font_info,
9364                         "press the 'return' key.");
9365       ystart2 += ystep_line;
9366       DrawTextSCentered(ystart2, font_info,
9367                         "To exit, press the 'escape' key.");
9368 #endif
9369
9370       BlitBitmapMasked(controller, drawto, 0, 0,
9371                        controller->width, controller->height,
9372                        controller_x, controller_y);
9373
9374       SDL_SetSurfaceBlendMode(marker->surface_masked, SDL_BLENDMODE_BLEND);
9375       SDL_SetSurfaceAlphaMod(marker->surface_masked, alpha);
9376
9377       BlitBitmapMasked(marker, drawto, 0, 0,
9378                        marker->width, marker->height,
9379                        marker_x, marker_y);
9380
9381       if (!screen_initialized)
9382         FadeIn(REDRAW_FIELD);
9383       else
9384         BackToFront();
9385
9386       screen_initialized = TRUE;
9387
9388       DelayCounter event_frame_delay = { GAME_FRAME_DELAY };
9389
9390       // reset frame delay counter directly after updating screen
9391       ResetDelayCounter(&event_frame_delay);
9392
9393       while (NextValidEvent(&event))
9394       {
9395         switch (event.type)
9396         {
9397           case SDL_JOYAXISMOTION:
9398             if (event.jaxis.value > 20000 ||
9399                 event.jaxis.value < -20000)
9400             {
9401               for (j = 0; j < i; j++)
9402                 if (steps[j].axis == event.jaxis.axis)
9403                   break;
9404
9405               if (j == i)
9406               {
9407                 if (step->marker != MARKER_AXIS_X &&
9408                     step->marker != MARKER_AXIS_Y)
9409                   break;
9410
9411                 step->axis = event.jaxis.axis;
9412                 strcat(mapping, step->field);
9413                 snprintf(temp, sizeof(temp), ":a%u,", event.jaxis.axis);
9414                 strcat(mapping, temp);
9415                 i++;
9416                 next = TRUE;
9417               }
9418             }
9419
9420             break;
9421
9422           case SDL_JOYHATMOTION:
9423             // ignore centering; we're probably just coming back
9424             // to the center from the previous item we set
9425             if (event.jhat.value == SDL_HAT_CENTERED)
9426               break;
9427
9428             for (j = 0; j < i; j++)
9429               if (steps[j].hat == event.jhat.hat &&
9430                   steps[j].hat_value == event.jhat.value)
9431                 break;
9432
9433             if (j == i)
9434             {
9435               step->hat = event.jhat.hat;
9436               step->hat_value = event.jhat.value;
9437               strcat(mapping, step->field);
9438               snprintf(temp, sizeof(temp), ":h%u.%u,",
9439                        event.jhat.hat, event.jhat.value );
9440               strcat(mapping, temp);
9441               i++;
9442               next = TRUE;
9443             }
9444
9445             break;
9446
9447           case SDL_JOYBALLMOTION:
9448             break;
9449
9450           case SDL_JOYBUTTONUP:
9451             for (j = 0; j < i; j++)
9452               if (steps[j].button == event.jbutton.button)
9453                 break;
9454
9455             if (j == i)
9456             {
9457               step->button = event.jbutton.button;
9458               strcat(mapping, step->field);
9459               snprintf(temp, sizeof(temp), ":b%u,", event.jbutton.button);
9460               strcat(mapping, temp);
9461               i++;
9462               next = TRUE;
9463             }
9464
9465             break;
9466
9467           case SDL_FINGERDOWN:
9468           case SDL_MOUSEBUTTONDOWN:
9469             // skip this step
9470             i++;
9471             next = TRUE;
9472
9473             break;
9474
9475           case SDL_KEYDOWN:
9476             if (event.key.keysym.sym == KSYM_BackSpace ||
9477                 event.key.keysym.sym == KSYM_Back)
9478             {
9479               if (i == 0)
9480               {
9481                 // leave screen
9482                 success = FALSE;
9483                 done = TRUE;
9484
9485                 break;
9486               }
9487
9488               // undo this step
9489               prev_step = &steps[i - 1];
9490               strcpy(mapping, prev_step->mapping);
9491               i--;
9492               next = TRUE;
9493
9494               break;
9495             }
9496
9497             if (event.key.keysym.sym == KSYM_space ||
9498                 event.key.keysym.sym == KSYM_Return ||
9499                 event.key.keysym.sym == KSYM_Menu)
9500             {
9501               // skip this step
9502               i++;
9503               next = TRUE;
9504
9505               break;
9506             }
9507
9508             if (event.key.keysym.sym == KSYM_Escape)
9509             {
9510               // leave screen
9511               success = FALSE;
9512               done = TRUE;
9513             }
9514
9515             break;
9516
9517           case SDL_QUIT:
9518             program.exit_function(0);
9519             break;
9520
9521           default:
9522             break;
9523         }
9524
9525         // do not handle events for longer than standard frame delay period
9526         if (DelayReached(&event_frame_delay))
9527           break;
9528       }
9529     }
9530   }
9531
9532   if (success)
9533   {
9534 #if DEBUG_JOYSTICKS
9535     Debug("joystick", "New game controller mapping:\n\n%s\n\n", mapping);
9536 #endif
9537
9538     // activate mapping for this game
9539     SDL_GameControllerAddMapping(mapping);
9540
9541     // save mapping to personal mappings
9542     SaveSetup_AddGameControllerMapping(mapping);
9543   }
9544
9545   // wait until the last pending event was removed from event queue
9546   while (NextValidEvent(&event));
9547
9548   return success;
9549 }
9550
9551 static int ConfigureJoystickMain(int player_nr)
9552 {
9553   char *device_name = setup.input[player_nr].joy.device_name;
9554   int joystick_nr = getJoystickNrFromDeviceName(device_name);
9555   boolean joystick_active = CheckJoystickOpened(joystick_nr);
9556   int success = FALSE;
9557   int i;
9558
9559   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
9560     return JOYSTICK_NOT_AVAILABLE;
9561
9562   if (!joystick_active || !setup.input[player_nr].use_joystick)
9563     return JOYSTICK_NOT_AVAILABLE;
9564
9565   FadeSetEnterMenu();
9566   FadeOut(REDRAW_FIELD);
9567
9568   // close all joystick devices (potentially opened as game controllers)
9569   for (i = 0; i < SDL_NumJoysticks(); i++)
9570     SDLCloseJoystick(i);
9571
9572   // open joystick device as plain joystick to configure as game controller
9573   SDL_Joystick *joystick = SDL_JoystickOpen(joystick_nr);
9574
9575   // as the joystick was successfully opened before, this should not happen
9576   if (joystick == NULL)
9577     return FALSE;
9578
9579   // create new game controller mapping (buttons and axes) for joystick device
9580   success = ConfigureJoystickMapButtonsAndAxes(joystick);
9581
9582   // close joystick (and maybe re-open as configured game controller later)
9583   SDL_JoystickClose(joystick);
9584
9585   // re-open all joystick devices (potentially as game controllers)
9586   for (i = 0; i < SDL_NumJoysticks(); i++)
9587     SDLOpenJoystick(i);
9588
9589   // clear all joystick input actions for all joystick devices
9590   SDLClearJoystickState();
9591
9592   return (success ? JOYSTICK_CONFIGURED : JOYSTICK_NOT_CONFIGURED);
9593 }
9594
9595 void ConfigureJoystick(int player_nr)
9596 {
9597   boolean state = ConfigureJoystickMain(player_nr);
9598
9599   if (state != JOYSTICK_NOT_CONFIGURED)
9600   {
9601     boolean success = (state == JOYSTICK_CONFIGURED);
9602     char message1[MAX_OUTPUT_LINESIZE + 1];
9603     char *message2 = (success ? "configured!" : "not available!");
9604     char *device_name = setup.input[player_nr].joy.device_name;
9605     int nr = getJoystickNrFromDeviceName(device_name) + 1;
9606     int font_nr = FONT_TITLE_1;
9607     int font_height = getFontHeight(font_nr);
9608     int ypos1 = SYSIZE / 2 - font_height * 2;
9609     int ypos2 = SYSIZE / 2 - font_height * 1;
9610     DelayCounter wait_frame_delay = { 2000 };
9611
9612     ResetDelayCounter(&wait_frame_delay);
9613
9614     ClearField();
9615
9616     sprintf(message1, "Joystick %d", nr);
9617
9618     DrawTextSCentered(ypos1, font_nr, message1);
9619     DrawTextSCentered(ypos2, font_nr, message2);
9620
9621     while (!DelayReached(&wait_frame_delay))
9622       BackToFront();
9623
9624     ClearEventQueue();
9625   }
9626
9627   DrawSetupScreen_Input();
9628 }
9629
9630 static void MapScreenMenuGadgets_OverlayTouchButtons(int y)
9631 {
9632   if (y < video.screen_height / 3)
9633   {
9634     // remap touch gadgets to access upper part of the screen
9635     UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH);
9636     MapScreenMenuGadgets(SCREEN_MASK_TOUCH2);
9637   }
9638   else if (y > 2 * video.screen_height / 3)
9639   {
9640     // remap touch gadgets to access lower part of the screen
9641     MapScreenMenuGadgets(SCREEN_MASK_TOUCH);
9642     UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH2);
9643   }
9644 }
9645
9646 static boolean ConfigureVirtualButtonsMain(void)
9647 {
9648   static char *customize_step_text[] =
9649   {
9650     "Move Left",
9651     "Move Right",
9652     "Move Up",
9653     "Move Down",
9654     "Snap Field",
9655     "Drop Element"
9656   };
9657   char grid_button[] =
9658   {
9659     CHAR_GRID_BUTTON_LEFT,
9660     CHAR_GRID_BUTTON_RIGHT,
9661     CHAR_GRID_BUTTON_UP,
9662     CHAR_GRID_BUTTON_DOWN,
9663     CHAR_GRID_BUTTON_SNAP,
9664     CHAR_GRID_BUTTON_DROP
9665   };
9666   enum
9667   {
9668     ACTION_NONE,
9669     ACTION_ESCAPE,
9670     ACTION_BACK,
9671     ACTION_NEXT
9672   };
9673   int font_nr = FONT_INPUT_1_ACTIVE;
9674   int font_height = getFontHeight(font_nr);
9675   int ypos1 = SYSIZE / 2 - font_height * 2;
9676   int ypos2 = SYSIZE / 2 - font_height * 1;
9677   boolean success = FALSE;
9678   boolean finished = FALSE;
9679   int step_nr = 0;
9680   char grid_button_draw = CHAR_GRID_BUTTON_NONE;
9681   char grid_button_old[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
9682   char grid_button_tmp[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
9683   boolean set_grid_button = FALSE;
9684   int nr = GRID_ACTIVE_NR();
9685   int x, y;
9686
9687   for (x = 0; x < MAX_GRID_XSIZE; x++)
9688     for (y = 0; y < MAX_GRID_YSIZE; y++)
9689       grid_button_old[x][y] = grid_button_tmp[x][y] = overlay.grid_button[x][y];
9690
9691   overlay.grid_button_highlight = grid_button[step_nr];
9692
9693   UnmapAllGadgets();
9694
9695   FadeSetEnterMenu();
9696   FadeOut(REDRAW_FIELD);
9697
9698   ClearField();
9699
9700   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
9701   DrawTextSCentered(ypos1, font_nr, "Select tiles to");
9702   DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
9703
9704   FadeIn(REDRAW_FIELD);
9705
9706   SetOverlayShowGrid(TRUE);
9707
9708   // map gadgets for setup touch buttons menu screen
9709   MapScreenMenuGadgets(SCREEN_MASK_TOUCH);
9710
9711   while (!finished)
9712   {
9713     Event event;
9714
9715     while (NextValidEvent(&event))
9716     {
9717       int action = ACTION_NONE;
9718
9719       // ---------- handle events and set the resulting action ----------
9720
9721       switch (event.type)
9722       {
9723         case EVENT_USER:
9724           {
9725             UserEvent *user = (UserEvent *)&event;
9726             int id = user->value1;
9727
9728             action = (id == SCREEN_CTRL_ID_TOUCH_PREV_PAGE ||
9729                       id == SCREEN_CTRL_ID_TOUCH_PREV_PAGE2 ? ACTION_BACK :
9730                       id == SCREEN_CTRL_ID_TOUCH_NEXT_PAGE ||
9731                       id == SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2 ? ACTION_NEXT :
9732                       ACTION_NONE);
9733           }
9734           break;
9735
9736         case EVENT_KEYPRESS:
9737           {
9738             Key key = GetEventKey((KeyEvent *)&event);
9739
9740             action = (key == KSYM_Escape ?      ACTION_ESCAPE :
9741                       key == KSYM_BackSpace ||
9742                       key == KSYM_Back ?        ACTION_BACK :
9743                       key == KSYM_Return ||
9744                       key == KSYM_Menu ||
9745                       key == KSYM_space ?       ACTION_NEXT :
9746                       ACTION_NONE);
9747           }
9748           break;
9749
9750         case EVENT_KEYRELEASE:
9751           key_joystick_mapping = 0;
9752           break;
9753
9754         case EVENT_BUTTONPRESS:
9755         case EVENT_BUTTONRELEASE:
9756           {
9757             ButtonEvent *button = (ButtonEvent *)&event;
9758
9759             motion_status = FALSE;
9760
9761             if (button->type == EVENT_BUTTONPRESS)
9762               button_status = button->button;
9763             else
9764               button_status = MB_RELEASED;
9765
9766             if (HandleGadgets(button->x, button->y, button_status))
9767             {
9768               // do not handle this button event anymore
9769               break;
9770             }
9771
9772             button->x += video.screen_xoffset;
9773             button->y += video.screen_yoffset;
9774
9775             x = button->x * overlay.grid_xsize / video.screen_width;
9776             y = button->y * overlay.grid_ysize / video.screen_height;
9777
9778             if (button->type == EVENT_BUTTONPRESS)
9779             {
9780               grid_button_draw =
9781                 (overlay.grid_button[x][y] != grid_button[step_nr] ?
9782                  grid_button[step_nr] : CHAR_GRID_BUTTON_NONE);
9783
9784               set_grid_button = TRUE;
9785             }
9786
9787             MapScreenMenuGadgets_OverlayTouchButtons(button->y);
9788           }
9789           break;
9790
9791         case EVENT_MOTIONNOTIFY:
9792           {
9793             MotionEvent *motion = (MotionEvent *)&event;
9794
9795             motion_status = TRUE;
9796
9797             if (HandleGadgets(motion->x, motion->y, button_status))
9798             {
9799               // do not handle this button event anymore
9800               break;
9801             }
9802
9803             motion->x += video.screen_xoffset;
9804             motion->y += video.screen_yoffset;
9805
9806             x = motion->x * overlay.grid_xsize / video.screen_width;
9807             y = motion->y * overlay.grid_ysize / video.screen_height;
9808
9809             set_grid_button = TRUE;
9810
9811             MapScreenMenuGadgets_OverlayTouchButtons(motion->y);
9812           }
9813           break;
9814
9815         case SDL_WINDOWEVENT:
9816           HandleWindowEvent((WindowEvent *) &event);
9817
9818           // check if device has been rotated
9819           if (nr != GRID_ACTIVE_NR())
9820           {
9821             nr = GRID_ACTIVE_NR();
9822
9823             for (x = 0; x < MAX_GRID_XSIZE; x++)
9824               for (y = 0; y < MAX_GRID_YSIZE; y++)
9825                 grid_button_old[x][y] = grid_button_tmp[x][y] =
9826                   overlay.grid_button[x][y];
9827           }
9828
9829           break;
9830
9831         case SDL_APP_WILLENTERBACKGROUND:
9832         case SDL_APP_DIDENTERBACKGROUND:
9833         case SDL_APP_WILLENTERFOREGROUND:
9834         case SDL_APP_DIDENTERFOREGROUND:
9835           HandlePauseResumeEvent((PauseResumeEvent *) &event);
9836           break;
9837
9838         default:
9839           HandleOtherEvents(&event);
9840           break;
9841       }
9842
9843       // ---------- perform action set by handling events ----------
9844
9845       if (action == ACTION_ESCAPE)
9846       {
9847         // abort and restore the old key bindings
9848
9849         for (x = 0; x < MAX_GRID_XSIZE; x++)
9850           for (y = 0; y < MAX_GRID_YSIZE; y++)
9851             overlay.grid_button[x][y] = grid_button_old[x][y];
9852
9853         FadeSkipNextFadeIn();
9854
9855         finished = TRUE;
9856       }
9857       else if (action == ACTION_BACK)
9858       {
9859         // keep the configured key bindings and go to previous page
9860
9861         step_nr--;
9862
9863         if (step_nr < 0)
9864         {
9865           FadeSkipNextFadeIn();
9866
9867           finished = TRUE;
9868         }
9869       }
9870       else if (action == ACTION_NEXT)
9871       {
9872         // keep the configured key bindings and go to next page
9873
9874         step_nr++;
9875
9876         // all virtual buttons configured
9877         if (step_nr == 6)
9878         {
9879           finished = TRUE;
9880           success = TRUE;
9881         }
9882       }
9883
9884       if (action != ACTION_NONE && !finished)
9885       {
9886         for (x = 0; x < MAX_GRID_XSIZE; x++)
9887           for (y = 0; y < MAX_GRID_YSIZE; y++)
9888             grid_button_tmp[x][y] = overlay.grid_button[x][y];
9889
9890         overlay.grid_button_highlight = grid_button[step_nr];
9891
9892         // configure next virtual button
9893
9894         ClearField();
9895
9896         DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
9897         DrawTextSCentered(ypos1, font_nr, "Select tiles to");
9898         DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
9899       }
9900
9901       if (set_grid_button)
9902       {
9903         overlay.grid_button[x][y] =
9904           (grid_button_draw != CHAR_GRID_BUTTON_NONE ? grid_button_draw :
9905            grid_button_tmp[x][y] == grid_button[step_nr] ? CHAR_GRID_BUTTON_NONE :
9906            grid_button_tmp[x][y]);
9907
9908         set_grid_button = FALSE;
9909       }
9910     }
9911
9912     BackToFront();
9913   }
9914
9915   for (x = 0; x < MAX_GRID_XSIZE; x++)
9916     for (y = 0; y < MAX_GRID_YSIZE; y++)
9917       setup.touch.grid_button[nr][x][y] = overlay.grid_button[x][y];
9918
9919   overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
9920
9921   SetOverlayShowGrid(FALSE);
9922
9923   return success;
9924 }
9925
9926 void ConfigureVirtualButtons(void)
9927 {
9928   boolean success = ConfigureVirtualButtonsMain();
9929
9930   UnmapScreenMenuGadgets(SCREEN_MASK_TOUCH |
9931                          SCREEN_MASK_TOUCH2);
9932
9933   if (success)
9934   {
9935     int font_nr = FONT_TITLE_1;
9936     int font_height = getFontHeight(font_nr);
9937     int ypos1 = SYSIZE / 2 - font_height * 2;
9938     int ypos2 = SYSIZE / 2 - font_height * 1;
9939     DelayCounter wait_frame_delay = { 2000 };
9940
9941     ResetDelayCounter(&wait_frame_delay);
9942
9943     ClearField();
9944
9945     DrawTextSCentered(ypos1, font_nr, "Virtual buttons");
9946     DrawTextSCentered(ypos2, font_nr, "configured!");
9947
9948     while (!DelayReached(&wait_frame_delay))
9949       BackToFront();
9950
9951     ClearEventQueue();
9952   }
9953 }
9954
9955 void DrawSetupScreen(void)
9956 {
9957   align_xoffset = 0;
9958   align_yoffset = 0;
9959
9960   if (setup_mode == SETUP_MODE_INPUT)
9961     DrawSetupScreen_Input();
9962   else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE)
9963     DrawChooseTree(&scores_type_current);
9964   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
9965     DrawChooseTree(&game_speed_current);
9966   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
9967     DrawChooseTree(&scroll_delay_current);
9968   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
9969     DrawChooseTree(&snapshot_mode_current);
9970   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE)
9971     DrawChooseTree(&game_engine_type_current);
9972   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64)
9973     DrawChooseTree(&bd_palette_c64_current);
9974   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV)
9975     DrawChooseTree(&bd_palette_c64dtv_current);
9976   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_ATARI)
9977     DrawChooseTree(&bd_palette_atari_current);
9978   else if (setup_mode == SETUP_MODE_CHOOSE_BD_COLOR_TYPE)
9979     DrawChooseTree(&bd_color_type_current);
9980   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
9981     DrawChooseTree(&window_size_current);
9982   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
9983     DrawChooseTree(&scaling_type_current);
9984   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
9985     DrawChooseTree(&rendering_mode_current);
9986   else if (setup_mode == SETUP_MODE_CHOOSE_VSYNC)
9987     DrawChooseTree(&vsync_mode_current);
9988   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
9989     DrawChooseTree(&artwork.gfx_current);
9990   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
9991     DrawChooseTree(&artwork.snd_current);
9992   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
9993     DrawChooseTree(&artwork.mus_current);
9994   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
9995     DrawChooseTree(&volume_simple_current);
9996   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
9997     DrawChooseTree(&volume_loops_current);
9998   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
9999     DrawChooseTree(&volume_music_current);
10000   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
10001     DrawChooseTree(&touch_control_current);
10002   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
10003     DrawChooseTree(&move_distance_current);
10004   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
10005     DrawChooseTree(&drop_distance_current);
10006   else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
10007     DrawChooseTree(&transparency_current);
10008   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
10009     DrawChooseTree(&grid_size_current[0][0]);
10010   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
10011     DrawChooseTree(&grid_size_current[0][1]);
10012   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
10013     DrawChooseTree(&grid_size_current[1][0]);
10014   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
10015     DrawChooseTree(&grid_size_current[1][1]);
10016   else
10017     DrawSetupScreen_Generic();
10018
10019   PlayMenuSoundsAndMusic();
10020 }
10021
10022 void RedrawSetupScreenAfterFullscreenToggle(void)
10023 {
10024   if (setup_mode == SETUP_MODE_GRAPHICS ||
10025       setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
10026   {
10027     // update list selection from "setup.window_scaling_percent"
10028     execSetupGraphics_setWindowSizes(TRUE);
10029
10030     DrawSetupScreen();
10031   }
10032 }
10033
10034 void RedrawSetupScreenAfterScreenRotation(int nr)
10035 {
10036   int x, y;
10037
10038   if (setup_mode == SETUP_MODE_TOUCH)
10039   {
10040     // update virtual button settings (depending on screen orientation)
10041     DrawSetupScreen();
10042   }
10043   else if (setup_mode == SETUP_MODE_CONFIG_VIRT_BUTTONS)
10044   {
10045     // save already configured virtual buttons
10046     for (x = 0; x < MAX_GRID_XSIZE; x++)
10047       for (y = 0; y < MAX_GRID_YSIZE; y++)
10048         setup.touch.grid_button[nr][x][y] = overlay.grid_button[x][y];
10049   }
10050 }
10051
10052 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
10053 {
10054   if (setup_mode == SETUP_MODE_INPUT)
10055     HandleSetupScreen_Input(mx, my, dx, dy, button);
10056   else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE)
10057     HandleChooseTree(mx, my, dx, dy, button, &scores_type_current);
10058   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
10059     HandleChooseTree(mx, my, dx, dy, button, &game_speed_current);
10060   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
10061     HandleChooseTree(mx, my, dx, dy, button, &scroll_delay_current);
10062   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
10063     HandleChooseTree(mx, my, dx, dy, button, &snapshot_mode_current);
10064   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_ENGINE_TYPE)
10065     HandleChooseTree(mx, my, dx, dy, button, &game_engine_type_current);
10066   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64)
10067     HandleChooseTree(mx, my, dx, dy, button, &bd_palette_c64_current);
10068   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_C64DTV)
10069     HandleChooseTree(mx, my, dx, dy, button, &bd_palette_c64dtv_current);
10070   else if (setup_mode == SETUP_MODE_CHOOSE_BD_PALETTE_ATARI)
10071     HandleChooseTree(mx, my, dx, dy, button, &bd_palette_atari_current);
10072   else if (setup_mode == SETUP_MODE_CHOOSE_BD_COLOR_TYPE)
10073     HandleChooseTree(mx, my, dx, dy, button, &bd_color_type_current);
10074   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
10075     HandleChooseTree(mx, my, dx, dy, button, &window_size_current);
10076   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
10077     HandleChooseTree(mx, my, dx, dy, button, &scaling_type_current);
10078   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
10079     HandleChooseTree(mx, my, dx, dy, button, &rendering_mode_current);
10080   else if (setup_mode == SETUP_MODE_CHOOSE_VSYNC)
10081     HandleChooseTree(mx, my, dx, dy, button, &vsync_mode_current);
10082   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
10083     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
10084   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
10085     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
10086   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
10087     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
10088   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
10089     HandleChooseTree(mx, my, dx, dy, button, &volume_simple_current);
10090   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
10091     HandleChooseTree(mx, my, dx, dy, button, &volume_loops_current);
10092   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
10093     HandleChooseTree(mx, my, dx, dy, button, &volume_music_current);
10094   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
10095     HandleChooseTree(mx, my, dx, dy, button, &touch_control_current);
10096   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
10097     HandleChooseTree(mx, my, dx, dy, button, &move_distance_current);
10098   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
10099     HandleChooseTree(mx, my, dx, dy, button, &drop_distance_current);
10100   else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
10101     HandleChooseTree(mx, my, dx, dy, button, &transparency_current);
10102   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
10103     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][0]);
10104   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
10105     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][1]);
10106   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
10107     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][0]);
10108   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
10109     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][1]);
10110   else
10111     HandleSetupScreen_Generic(mx, my, dx, dy, button);
10112 }
10113
10114 void HandleGameActions(void)
10115 {
10116   if (CheckRestartGame())
10117     return;
10118
10119   if (game_status != GAME_MODE_PLAYING)
10120     return;
10121
10122   GameActions();                // main game loop
10123
10124   if (tape.auto_play && !tape.playing)
10125     AutoPlayTapesContinue();    // continue automatically playing next tape
10126 }
10127
10128
10129 // ---------- new screen button stuff --------------------------------------
10130
10131 static struct
10132 {
10133   int gfx_unpressed, gfx_pressed, gfx_active;
10134   struct MenuPosInfo *pos;
10135   boolean *check_value;
10136   int gadget_id;
10137   int screen_mask;
10138   unsigned int event_mask;
10139   boolean is_touch_button;
10140   char *infotext;
10141 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
10142 {
10143   {
10144     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE, -1,
10145     &menu.main.button.prev_level, NULL,
10146     SCREEN_CTRL_ID_PREV_LEVEL,
10147     SCREEN_MASK_MAIN,
10148     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10149     FALSE, "previous level"
10150   },
10151   {
10152     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE, -1,
10153     &menu.main.button.next_level, NULL,
10154     SCREEN_CTRL_ID_NEXT_LEVEL,
10155     SCREEN_MASK_MAIN,
10156     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10157     FALSE, "next level"
10158   },
10159   {
10160     IMG_MENU_BUTTON_PREV_LEVEL2, IMG_MENU_BUTTON_PREV_LEVEL2_ACTIVE, -1,
10161     &menu.scores.button.prev_level, NULL,
10162     SCREEN_CTRL_ID_PREV_LEVEL2,
10163     SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO,
10164     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10165     FALSE, "previous level"
10166   },
10167   {
10168     IMG_MENU_BUTTON_NEXT_LEVEL2, IMG_MENU_BUTTON_NEXT_LEVEL2_ACTIVE, -1,
10169     &menu.scores.button.next_level, NULL,
10170     SCREEN_CTRL_ID_NEXT_LEVEL2,
10171     SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO,
10172     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10173     FALSE, "next level"
10174   },
10175   {
10176     IMG_MENU_BUTTON_PREV_SCORE, IMG_MENU_BUTTON_PREV_SCORE_ACTIVE, -1,
10177     &menu.scores.button.prev_score, NULL,
10178     SCREEN_CTRL_ID_PREV_SCORE,
10179     SCREEN_MASK_SCORES_INFO,
10180     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10181     FALSE, "previous score"
10182   },
10183   {
10184     IMG_MENU_BUTTON_NEXT_SCORE, IMG_MENU_BUTTON_NEXT_SCORE_ACTIVE, -1,
10185     &menu.scores.button.next_score, NULL,
10186     SCREEN_CTRL_ID_NEXT_SCORE,
10187     SCREEN_MASK_SCORES_INFO,
10188     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10189     FALSE, "next score"
10190   },
10191   {
10192     IMG_MENU_BUTTON_PLAY_TAPE, IMG_MENU_BUTTON_PLAY_TAPE, -1,
10193     &menu.scores.button.play_tape, NULL,
10194     SCREEN_CTRL_ID_PLAY_TAPE,
10195     SCREEN_MASK_SCORES_INFO,
10196     GD_EVENT_RELEASED,
10197     FALSE, "play tape"
10198   },
10199   {
10200     IMG_MENU_BUTTON_FIRST_LEVEL, IMG_MENU_BUTTON_FIRST_LEVEL_ACTIVE, -1,
10201     &menu.main.button.first_level, NULL,
10202     SCREEN_CTRL_ID_FIRST_LEVEL,
10203     SCREEN_MASK_MAIN,
10204     GD_EVENT_RELEASED,
10205     FALSE, "first level"
10206   },
10207   {
10208     IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE, -1,
10209     &menu.main.button.last_level, NULL,
10210     SCREEN_CTRL_ID_LAST_LEVEL,
10211     SCREEN_MASK_MAIN,
10212     GD_EVENT_RELEASED,
10213     FALSE, "last level"
10214   },
10215   {
10216     IMG_MENU_BUTTON_LEVEL_NUMBER, IMG_MENU_BUTTON_LEVEL_NUMBER_ACTIVE, -1,
10217     &menu.main.button.level_number, NULL,
10218     SCREEN_CTRL_ID_LEVEL_NUMBER,
10219     SCREEN_MASK_MAIN,
10220     GD_EVENT_RELEASED,
10221     FALSE, "level number"
10222   },
10223   {
10224     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE, -1,
10225     &menu.setup.button.prev_player, NULL,
10226     SCREEN_CTRL_ID_PREV_PLAYER,
10227     SCREEN_MASK_INPUT,
10228     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10229     FALSE, "previous player"
10230   },
10231   {
10232     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE, -1,
10233     &menu.setup.button.next_player, NULL,
10234     SCREEN_CTRL_ID_NEXT_PLAYER,
10235     SCREEN_MASK_INPUT,
10236     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
10237     FALSE, "next player"
10238   },
10239   {
10240     IMG_MENU_BUTTON_INSERT_SOLUTION, IMG_MENU_BUTTON_INSERT_SOLUTION_ACTIVE, -1,
10241     &menu.main.button.insert_solution, NULL,
10242     SCREEN_CTRL_ID_INSERT_SOLUTION,
10243     SCREEN_MASK_MAIN_HAS_SOLUTION,
10244     GD_EVENT_RELEASED,
10245     FALSE, "insert solution tape"
10246   },
10247   {
10248     IMG_MENU_BUTTON_PLAY_SOLUTION, IMG_MENU_BUTTON_PLAY_SOLUTION_ACTIVE, -1,
10249     &menu.main.button.play_solution, NULL,
10250     SCREEN_CTRL_ID_PLAY_SOLUTION,
10251     SCREEN_MASK_MAIN_HAS_SOLUTION,
10252     GD_EVENT_RELEASED,
10253     FALSE, "play solution tape"
10254   },
10255   {
10256     IMG_MENU_BUTTON_LEVELSET_INFO, IMG_MENU_BUTTON_LEVELSET_INFO_PRESSED,
10257     IMG_MENU_BUTTON_LEVELSET_INFO_ACTIVE,
10258     &menu.main.button.levelset_info, NULL,
10259     SCREEN_CTRL_ID_LEVELSET_INFO,
10260     SCREEN_MASK_MAIN_HAS_SET_INFO,
10261     GD_EVENT_RELEASED,
10262     FALSE, "show level set info"
10263   },
10264   {
10265     IMG_MENU_BUTTON_SWITCH_ECS_AGA, IMG_MENU_BUTTON_SWITCH_ECS_AGA_ACTIVE, -1,
10266     &menu.main.button.switch_ecs_aga, &setup.prefer_aga_graphics,
10267     SCREEN_CTRL_ID_SWITCH_ECS_AGA,
10268     SCREEN_MASK_MAIN,
10269     GD_EVENT_RELEASED | GD_EVENT_OFF_BORDERS,
10270     FALSE, "switch ECS/AGA chipset"
10271   },
10272   {
10273     IMG_MENU_BUTTON_TOUCH_BACK, IMG_MENU_BUTTON_TOUCH_BACK, -1,
10274     &menu.setup.button.touch_back, NULL,
10275     SCREEN_CTRL_ID_TOUCH_PREV_PAGE,
10276     SCREEN_MASK_TOUCH,
10277     GD_EVENT_RELEASED,
10278     TRUE, "previous page"
10279   },
10280   {
10281     IMG_MENU_BUTTON_TOUCH_NEXT, IMG_MENU_BUTTON_TOUCH_NEXT, -1,
10282     &menu.setup.button.touch_next, NULL,
10283     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE,
10284     SCREEN_MASK_TOUCH,
10285     GD_EVENT_RELEASED,
10286     TRUE, "next page"
10287   },
10288   {
10289     IMG_MENU_BUTTON_TOUCH_BACK2, IMG_MENU_BUTTON_TOUCH_BACK2, -1,
10290     &menu.setup.button.touch_back2, NULL,
10291     SCREEN_CTRL_ID_TOUCH_PREV_PAGE2,
10292     SCREEN_MASK_TOUCH2,
10293     GD_EVENT_RELEASED,
10294     TRUE, "previous page"
10295   },
10296   {
10297     IMG_MENU_BUTTON_TOUCH_NEXT2, IMG_MENU_BUTTON_TOUCH_NEXT2, -1,
10298     &menu.setup.button.touch_next2, NULL,
10299     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2,
10300     SCREEN_MASK_TOUCH2,
10301     GD_EVENT_RELEASED,
10302     TRUE, "next page"
10303   },
10304 };
10305
10306 static struct
10307 {
10308   int gfx_unpressed, gfx_pressed;
10309   int x, y;
10310   int gadget_id;
10311   char *infotext;
10312 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
10313 {
10314   {
10315     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
10316     -1, -1,     // these values are not constant, but can change at runtime
10317     SCREEN_CTRL_ID_SCROLL_UP,
10318     "scroll up"
10319   },
10320   {
10321     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
10322     -1, -1,     // these values are not constant, but can change at runtime
10323     SCREEN_CTRL_ID_SCROLL_DOWN,
10324     "scroll down"
10325   }
10326 };
10327
10328 static struct
10329 {
10330   int gfx_unpressed, gfx_pressed;
10331   int x, y;
10332   int width, height;
10333   int type;
10334   int gadget_id;
10335   char *infotext;
10336 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
10337 {
10338   {
10339     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
10340     -1, -1,     // these values are not constant, but can change at runtime
10341     -1, -1,     // these values are not constant, but can change at runtime
10342     GD_TYPE_SCROLLBAR_VERTICAL,
10343     SCREEN_CTRL_ID_SCROLL_VERTICAL,
10344     "scroll level series vertically"
10345   }
10346 };
10347
10348 static struct
10349 {
10350   int graphic;
10351   int gadget_id;
10352   int x, y;
10353   int size;
10354   char *value;
10355   char *infotext;
10356 } textinput_info[NUM_SCREEN_TEXTINPUT] =
10357 {
10358   {
10359     IMG_SETUP_INPUT_TEXT,
10360     SCREEN_CTRL_ID_NETWORK_SERVER,
10361     -1, -1,     // these values are not constant, but can change at runtime
10362     MAX_SETUP_TEXT_INPUT_LEN,
10363     network_server_hostname,
10364     "Network Server Hostname / IP"
10365   },
10366 };
10367
10368 static void CreateScreenMenubuttons(void)
10369 {
10370   struct GadgetInfo *gi;
10371   unsigned int event_mask;
10372   int i;
10373
10374   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
10375   {
10376     struct MenuPosInfo *pos = menubutton_info[i].pos;
10377     int screen_mask = menubutton_info[i].screen_mask;
10378     boolean is_touch_button = menubutton_info[i].is_touch_button;
10379     boolean is_check_button = menubutton_info[i].check_value != NULL;
10380     boolean is_score_button = (screen_mask & SCREEN_MASK_SCORES_INFO);
10381     boolean has_gfx_pressed = (menubutton_info[i].gfx_pressed ==
10382                                menubutton_info[i].gfx_unpressed);
10383     boolean has_gfx_active = (menubutton_info[i].gfx_active != -1);
10384     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
10385     Bitmap *gd_bitmap_unpressed_alt, *gd_bitmap_pressed_alt;
10386     int gfx_unpressed, gfx_pressed;
10387     int gfx_unpressed_alt, gfx_pressed_alt;
10388     int x, y, width, height;
10389     int gd_x1, gd_x2, gd_y1, gd_y2;
10390     int gd_x1a, gd_x2a, gd_y1a, gd_y2a;
10391     int id = menubutton_info[i].gadget_id;
10392     int type = GD_TYPE_NORMAL_BUTTON;
10393     boolean checked = FALSE;
10394
10395     // do not use touch buttons if overlay touch buttons are disabled
10396     if (is_touch_button && !setup.touch.overlay_buttons)
10397       continue;
10398
10399     event_mask = menubutton_info[i].event_mask;
10400
10401     x = (is_touch_button ? pos->x : mSX + GDI_ACTIVE_POS(pos->x));
10402     y = (is_touch_button ? pos->y : mSY + GDI_ACTIVE_POS(pos->y));
10403
10404     width  = graphic_info[menubutton_info[i].gfx_pressed].width;
10405     height = graphic_info[menubutton_info[i].gfx_pressed].height;
10406
10407     gfx_unpressed = menubutton_info[i].gfx_unpressed;
10408     gfx_pressed   = menubutton_info[i].gfx_pressed;
10409     gfx_unpressed_alt = gfx_unpressed;
10410     gfx_pressed_alt   = gfx_pressed;
10411
10412     if (has_gfx_active)
10413     {
10414       gfx_unpressed_alt = menubutton_info[i].gfx_active;
10415
10416       type = GD_TYPE_CHECK_BUTTON_2;
10417
10418       if (menubutton_info[i].check_value != NULL)
10419         checked = *menubutton_info[i].check_value;
10420     }
10421
10422     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
10423     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
10424     gd_bitmap_unpressed_alt = graphic_info[gfx_unpressed_alt].bitmap;
10425     gd_bitmap_pressed_alt   = graphic_info[gfx_pressed_alt].bitmap;
10426
10427     gd_x1 = graphic_info[gfx_unpressed].src_x;
10428     gd_y1 = graphic_info[gfx_unpressed].src_y;
10429     gd_x2 = graphic_info[gfx_pressed].src_x;
10430     gd_y2 = graphic_info[gfx_pressed].src_y;
10431
10432     gd_x1a = graphic_info[gfx_unpressed_alt].src_x;
10433     gd_y1a = graphic_info[gfx_unpressed_alt].src_y;
10434     gd_x2a = graphic_info[gfx_pressed_alt].src_x;
10435     gd_y2a = graphic_info[gfx_pressed_alt].src_y;
10436
10437     if (has_gfx_pressed)
10438     {
10439       gd_x2 += graphic_info[gfx_pressed].pressed_xoffset;
10440       gd_y2 += graphic_info[gfx_pressed].pressed_yoffset;
10441     }
10442
10443     if (is_check_button)
10444     {
10445       gd_x1a += graphic_info[gfx_unpressed].active_xoffset;
10446       gd_y1a += graphic_info[gfx_unpressed].active_yoffset;
10447       gd_x2a += graphic_info[gfx_pressed].active_xoffset;
10448       gd_y2a += graphic_info[gfx_pressed].active_yoffset;
10449
10450       type = GD_TYPE_CHECK_BUTTON;
10451
10452       if (menubutton_info[i].check_value != NULL)
10453         checked = *menubutton_info[i].check_value;
10454     }
10455
10456     if (is_score_button)
10457     {
10458       // if x/y set to -1, dynamically place buttons next to title text
10459       int title_width = getTextWidth(INFOTEXT_SCORE_ENTRY, FONT_TITLE_1);
10460
10461       // special compatibility handling for "Snake Bite" graphics set
10462       if (strPrefix(leveldir_current->identifier, "snake_bite"))
10463         title_width = strlen(INFOTEXT_SCORE_ENTRY) * 32;
10464
10465       // use "SX" here to center buttons (ignore horizontal draw offset)
10466       if (pos->x == -1)
10467         x = (id == SCREEN_CTRL_ID_PREV_LEVEL2 ?
10468              SX + (SXSIZE - title_width) / 2 - width * 3 / 2 :
10469              id == SCREEN_CTRL_ID_NEXT_LEVEL2 ?
10470              SX + (SXSIZE + title_width) / 2 + width / 2 : 0);
10471
10472       // use "mSY" here to place buttons (respect vertical draw offset)
10473       if (pos->y == -1)
10474         y = (id == SCREEN_CTRL_ID_PREV_LEVEL2 ||
10475              id == SCREEN_CTRL_ID_NEXT_LEVEL2 ? mSY + MENU_TITLE1_YPOS : 0);
10476     }
10477
10478     if (id == SCREEN_CTRL_ID_LEVELSET_INFO)
10479     {
10480       if (pos->x == -1 && pos->y == -1)
10481       {
10482         // use "SX" here to place button (ignore draw offsets)
10483         x = SX + SXSIZE - 2 * TILESIZE;
10484         y = SY + SYSIZE - 2 * TILESIZE;
10485
10486         // special compatibility handling for "BD2K3" graphics set
10487         if (strPrefix(leveldir_current->identifier, "BD2K3"))
10488           x = SX + TILESIZE + MINI_TILESIZE;
10489
10490         // special compatibility handling for "jue0" graphics set
10491         if (strPrefix(artwork.gfx_current_identifier, "jue0"))
10492         {
10493           x = SX + SXSIZE - 4 * TILESIZE;
10494           y = SY + SYSIZE - 3 * TILESIZE;
10495         }
10496       }
10497     }
10498
10499     gi = CreateGadget(GDI_CUSTOM_ID, id,
10500                       GDI_CUSTOM_TYPE_ID, i,
10501                       GDI_IMAGE_ID, gfx_unpressed,
10502                       GDI_INFO_TEXT, menubutton_info[i].infotext,
10503                       GDI_X, x,
10504                       GDI_Y, y,
10505                       GDI_WIDTH, width,
10506                       GDI_HEIGHT, height,
10507                       GDI_TYPE, type,
10508                       GDI_STATE, GD_BUTTON_UNPRESSED,
10509                       GDI_CHECKED, checked,
10510                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
10511                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
10512                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap_unpressed_alt, gd_x1a, gd_y1a,
10513                       GDI_ALT_DESIGN_PRESSED, gd_bitmap_pressed_alt, gd_x2a, gd_y2a,
10514                       GDI_DIRECT_DRAW, FALSE,
10515                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
10516                       GDI_EVENT_MASK, event_mask,
10517                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
10518                       GDI_END);
10519
10520     if (gi == NULL)
10521       Fail("cannot create gadget");
10522
10523     screen_gadget[id] = gi;
10524   }
10525 }
10526
10527 static void CreateScreenScrollbuttons(void)
10528 {
10529   struct GadgetInfo *gi;
10530   unsigned int event_mask;
10531   int i;
10532
10533   // these values are not constant, but can change at runtime
10534   scrollbutton_info[0].x = SC_SCROLL_UP_XPOS;
10535   scrollbutton_info[0].y = SC_SCROLL_UP_YPOS;
10536   scrollbutton_info[1].x = SC_SCROLL_DOWN_XPOS;
10537   scrollbutton_info[1].y = SC_SCROLL_DOWN_YPOS;
10538
10539   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
10540   {
10541     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
10542     int gfx_unpressed, gfx_pressed;
10543     int x, y, width, height;
10544     int gd_x1, gd_x2, gd_y1, gd_y2;
10545     int id = scrollbutton_info[i].gadget_id;
10546
10547     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
10548
10549     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
10550     y = mSY + scrollbutton_info[i].y;
10551     width = SC_SCROLLBUTTON_XSIZE;
10552     height = SC_SCROLLBUTTON_YSIZE;
10553
10554     // correct scrollbar position if placed outside menu (playfield) area
10555     if (x > SX + SC_SCROLL_UP_XPOS)
10556       x = SX + SC_SCROLL_UP_XPOS;
10557
10558     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
10559       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
10560                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
10561
10562     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
10563     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
10564     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
10565     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
10566     gd_x1 = graphic_info[gfx_unpressed].src_x;
10567     gd_y1 = graphic_info[gfx_unpressed].src_y;
10568     gd_x2 = graphic_info[gfx_pressed].src_x;
10569     gd_y2 = graphic_info[gfx_pressed].src_y;
10570
10571     gi = CreateGadget(GDI_CUSTOM_ID, id,
10572                       GDI_CUSTOM_TYPE_ID, i,
10573                       GDI_IMAGE_ID, gfx_unpressed,
10574                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
10575                       GDI_X, x,
10576                       GDI_Y, y,
10577                       GDI_WIDTH, width,
10578                       GDI_HEIGHT, height,
10579                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
10580                       GDI_STATE, GD_BUTTON_UNPRESSED,
10581                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
10582                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
10583                       GDI_DIRECT_DRAW, FALSE,
10584                       GDI_EVENT_MASK, event_mask,
10585                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
10586                       GDI_END);
10587
10588     if (gi == NULL)
10589       Fail("cannot create gadget");
10590
10591     screen_gadget[id] = gi;
10592   }
10593 }
10594
10595 static void CreateScreenScrollbars(void)
10596 {
10597   int i;
10598
10599   // these values are not constant, but can change at runtime
10600   scrollbar_info[0].x = SC_SCROLL_VERTICAL_XPOS;
10601   scrollbar_info[0].y = SC_SCROLL_VERTICAL_YPOS;
10602   scrollbar_info[0].width  = SC_SCROLL_VERTICAL_XSIZE;
10603   scrollbar_info[0].height = SC_SCROLL_VERTICAL_YSIZE;
10604
10605   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
10606   {
10607     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
10608     int gfx_unpressed, gfx_pressed;
10609     int x, y, width, height;
10610     int gd_x1, gd_x2, gd_y1, gd_y2;
10611     struct GadgetInfo *gi;
10612     int items_max, items_visible, item_position;
10613     unsigned int event_mask;
10614     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
10615     int id = scrollbar_info[i].gadget_id;
10616
10617     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
10618
10619     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
10620     y = mSY + scrollbar_info[i].y;
10621     width  = scrollbar_info[i].width;
10622     height = scrollbar_info[i].height;
10623
10624     // correct scrollbar position if placed outside menu (playfield) area
10625     if (x > SX + SC_SCROLL_VERTICAL_XPOS)
10626       x = SX + SC_SCROLL_VERTICAL_XPOS;
10627
10628     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
10629       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
10630
10631     items_max = num_page_entries;
10632     items_visible = num_page_entries;
10633     item_position = 0;
10634
10635     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
10636     gfx_pressed   = scrollbar_info[i].gfx_pressed;
10637     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
10638     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
10639     gd_x1 = graphic_info[gfx_unpressed].src_x;
10640     gd_y1 = graphic_info[gfx_unpressed].src_y;
10641     gd_x2 = graphic_info[gfx_pressed].src_x;
10642     gd_y2 = graphic_info[gfx_pressed].src_y;
10643
10644     gi = CreateGadget(GDI_CUSTOM_ID, id,
10645                       GDI_CUSTOM_TYPE_ID, i,
10646                       GDI_IMAGE_ID, gfx_unpressed,
10647                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
10648                       GDI_X, x,
10649                       GDI_Y, y,
10650                       GDI_WIDTH, width,
10651                       GDI_HEIGHT, height,
10652                       GDI_TYPE, scrollbar_info[i].type,
10653                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
10654                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
10655                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
10656                       GDI_WHEEL_AREA_X, SX,
10657                       GDI_WHEEL_AREA_Y, SY,
10658                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
10659                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
10660                       GDI_STATE, GD_BUTTON_UNPRESSED,
10661                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
10662                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
10663                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
10664                       GDI_DIRECT_DRAW, FALSE,
10665                       GDI_EVENT_MASK, event_mask,
10666                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
10667                       GDI_END);
10668
10669     if (gi == NULL)
10670       Fail("cannot create gadget");
10671
10672     screen_gadget[id] = gi;
10673   }
10674 }
10675
10676 static void CreateScreenTextInputGadgets(void)
10677 {
10678   int i;
10679
10680   for (i = 0; i < NUM_SCREEN_TEXTINPUT; i++)
10681   {
10682     int graphic = textinput_info[i].graphic;
10683     struct GraphicInfo *gd = &graphic_info[graphic];
10684     int gd_x1 = gd->src_x;
10685     int gd_y1 = gd->src_y;
10686     int gd_x2 = gd->src_x + gd->active_xoffset;
10687     int gd_y2 = gd->src_y + gd->active_yoffset;
10688     struct GadgetInfo *gi;
10689     unsigned int event_mask;
10690     int id = textinput_info[i].gadget_id;
10691     int x = textinput_info[i].x;
10692     int y = textinput_info[i].y;
10693
10694     event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
10695
10696     gi = CreateGadget(GDI_CUSTOM_ID, id,
10697                       GDI_CUSTOM_TYPE_ID, i,
10698                       GDI_INFO_TEXT, textinput_info[i].infotext,
10699                       GDI_X, SX + x,
10700                       GDI_Y, SY + y,
10701                       GDI_TYPE, GD_TYPE_TEXT_INPUT_ALPHANUMERIC,
10702                       GDI_TEXT_VALUE, textinput_info[i].value,
10703                       GDI_TEXT_SIZE, textinput_info[i].size,
10704                       GDI_TEXT_FONT, getSetupValueFont(TYPE_STRING, NULL),
10705                       GDI_TEXT_FONT_ACTIVE, FONT_TEXT_1,
10706                       GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
10707                       GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
10708                       GDI_BORDER_SIZE, gd->border_size, gd->border_size,
10709                       GDI_DESIGN_WIDTH, gd->width,
10710                       GDI_EVENT_MASK, event_mask,
10711                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
10712                       GDI_CALLBACK_ACTION_ALWAYS, TRUE,
10713                       GDI_END);
10714
10715     if (gi == NULL)
10716       Fail("cannot create gadget");
10717
10718     screen_gadget[id] = gi;
10719   }
10720 }
10721
10722 void CreateScreenGadgets(void)
10723 {
10724   CreateScreenMenubuttons();
10725
10726   CreateScreenScrollbuttons();
10727   CreateScreenScrollbars();
10728
10729   CreateScreenTextInputGadgets();
10730 }
10731
10732 void FreeScreenGadgets(void)
10733 {
10734   int i;
10735
10736   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
10737     FreeGadget(screen_gadget[i]);
10738 }
10739
10740 static void RedrawScreenMenuGadgets(int screen_mask)
10741 {
10742   int i;
10743
10744   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
10745     if (screen_mask & menubutton_info[i].screen_mask)
10746       RedrawGadget(screen_gadget[menubutton_info[i].gadget_id]);
10747 }
10748
10749 static void MapScreenMenuGadgets(int screen_mask)
10750 {
10751   int i;
10752
10753   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
10754     if (screen_mask & menubutton_info[i].screen_mask)
10755       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
10756 }
10757
10758 static void UnmapScreenMenuGadgets(int screen_mask)
10759 {
10760   int i;
10761
10762   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
10763   {
10764     if (screen_mask & menubutton_info[i].screen_mask)
10765     {
10766       UnmapGadget(screen_gadget[menubutton_info[i].gadget_id]);
10767
10768       if (screen_mask & SCREEN_MASK_MAIN_HAS_SOLUTION)
10769         DrawBackground(screen_gadget[menubutton_info[i].gadget_id]->x,
10770                        screen_gadget[menubutton_info[i].gadget_id]->y,
10771                        screen_gadget[menubutton_info[i].gadget_id]->width,
10772                        screen_gadget[menubutton_info[i].gadget_id]->height);
10773     }
10774   }
10775 }
10776
10777 static void UpdateScreenMenuGadgets(int screen_mask, boolean map_gadgets)
10778 {
10779   if (map_gadgets)
10780     MapScreenMenuGadgets(screen_mask);
10781   else
10782     UnmapScreenMenuGadgets(screen_mask);
10783 }
10784
10785 static void MapScreenGadgets(int num_entries)
10786 {
10787   int i;
10788
10789   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
10790     return;
10791
10792   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
10793     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
10794
10795   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
10796     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
10797 }
10798
10799 static void UnmapScreenGadgets(void)
10800 {
10801   int i;
10802
10803   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
10804     UnmapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
10805
10806   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
10807     UnmapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
10808 }
10809
10810 static void MapScreenTreeGadgets(TreeInfo *ti)
10811 {
10812   MapScreenGadgets(numTreeInfoInGroup(ti));
10813 }
10814
10815 static void UnmapScreenTreeGadgets(void)
10816 {
10817   UnmapScreenGadgets();
10818 }
10819
10820 static void AdjustScoreInfoButtons_SelectScore(int x, int y1, int y2)
10821 {
10822   struct GadgetInfo *gi_1 = screen_gadget[SCREEN_CTRL_ID_PREV_SCORE];
10823   struct GadgetInfo *gi_2 = screen_gadget[SCREEN_CTRL_ID_NEXT_SCORE];
10824   struct MenuPosInfo *pos_1 = menubutton_info[SCREEN_CTRL_ID_PREV_SCORE].pos;
10825   struct MenuPosInfo *pos_2 = menubutton_info[SCREEN_CTRL_ID_NEXT_SCORE].pos;
10826
10827   if (pos_1->x == -1 && pos_1->y == -1)
10828     ModifyGadget(gi_1, GDI_X, x, GDI_Y, y1, GDI_END);
10829
10830   if (pos_2->x == -1 && pos_2->y == -1)
10831     ModifyGadget(gi_2, GDI_X, x, GDI_Y, y2, GDI_END);
10832 }
10833
10834 static void AdjustScoreInfoButtons_PlayTape(int x, int y, boolean visible)
10835 {
10836   struct GadgetInfo *gi = screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE];
10837   struct MenuPosInfo *pos = menubutton_info[SCREEN_CTRL_ID_PLAY_TAPE].pos;
10838
10839   // set gadget position dynamically, pre-defined or off-screen
10840   int xx = (visible ? (pos->x == -1 ? x : pos->x) : POS_OFFSCREEN);
10841   int yy = (visible ? (pos->y == -1 ? y : pos->y) : POS_OFFSCREEN);
10842
10843   ModifyGadget(gi, GDI_X, xx, GDI_Y, yy, GDI_END);
10844   MapGadget(gi);        // (needed if deactivated on last score page)
10845 }
10846
10847 static void HandleScreenGadgets(struct GadgetInfo *gi)
10848 {
10849   int id = gi->custom_id;
10850   int button = gi->event.button;
10851   int step = (button == MB_LEFTBUTTON   ? 1 :
10852               button == MB_MIDDLEBUTTON ? 5 :
10853               button == MB_RIGHTBUTTON  ? 10 : 1);
10854
10855   switch (id)
10856   {
10857     case SCREEN_CTRL_ID_PREV_LEVEL:
10858       HandleMainMenu_SelectLevel(step, -1, NO_DIRECT_LEVEL_SELECT);
10859       break;
10860
10861     case SCREEN_CTRL_ID_NEXT_LEVEL:
10862       HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT);
10863       break;
10864
10865     case SCREEN_CTRL_ID_PREV_LEVEL2:
10866       HandleHallOfFame_SelectLevel(step, -1);
10867       break;
10868
10869     case SCREEN_CTRL_ID_NEXT_LEVEL2:
10870       HandleHallOfFame_SelectLevel(step, +1);
10871       break;
10872
10873     case SCREEN_CTRL_ID_PREV_SCORE:
10874       HandleScoreInfo_SelectScore(step, -1);
10875       break;
10876
10877     case SCREEN_CTRL_ID_NEXT_SCORE:
10878       HandleScoreInfo_SelectScore(step, +1);
10879       break;
10880
10881     case SCREEN_CTRL_ID_PLAY_TAPE:
10882       HandleScoreInfo_PlayTape();
10883       break;
10884
10885     case SCREEN_CTRL_ID_FIRST_LEVEL:
10886       HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
10887       break;
10888
10889     case SCREEN_CTRL_ID_LAST_LEVEL:
10890       HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT);
10891       break;
10892
10893     case SCREEN_CTRL_ID_LEVEL_NUMBER:
10894       CloseDoor(DOOR_CLOSE_2);
10895       SetGameStatus(GAME_MODE_LEVELNR);
10896       DrawChooseLevelNr();
10897       break;
10898
10899     case SCREEN_CTRL_ID_PREV_PLAYER:
10900       HandleSetupScreen_Input_Player(step, -1);
10901       break;
10902
10903     case SCREEN_CTRL_ID_NEXT_PLAYER:
10904       HandleSetupScreen_Input_Player(step, +1);
10905       break;
10906
10907     case SCREEN_CTRL_ID_INSERT_SOLUTION:
10908       InsertSolutionTape();
10909       break;
10910
10911     case SCREEN_CTRL_ID_PLAY_SOLUTION:
10912       PlaySolutionTape();
10913       break;
10914
10915     case SCREEN_CTRL_ID_LEVELSET_INFO:
10916       DrawInfoScreen_FromMainMenu(INFO_MODE_LEVELSET);
10917       break;
10918
10919     case SCREEN_CTRL_ID_SWITCH_ECS_AGA:
10920       setup.prefer_aga_graphics = !setup.prefer_aga_graphics;
10921       DrawMainMenu();
10922       break;
10923
10924     case SCREEN_CTRL_ID_TOUCH_PREV_PAGE:
10925     case SCREEN_CTRL_ID_TOUCH_NEXT_PAGE:
10926     case SCREEN_CTRL_ID_TOUCH_PREV_PAGE2:
10927     case SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2:
10928       PushUserEvent(USEREVENT_GADGET_PRESSED, id, 0);
10929       break;
10930
10931     case SCREEN_CTRL_ID_SCROLL_UP:
10932       if (game_status == GAME_MODE_NAMES)
10933         HandleChoosePlayerName(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10934       else if (game_status == GAME_MODE_LEVELS)
10935         HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10936       else if (game_status == GAME_MODE_LEVELNR)
10937         HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10938       else if (game_status == GAME_MODE_SETUP)
10939         HandleSetupScreen(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10940       else if (game_status == GAME_MODE_INFO)
10941         HandleInfoScreen(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10942       else if (game_status == GAME_MODE_SCORES)
10943         HandleHallOfFame(0, 0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
10944       break;
10945
10946     case SCREEN_CTRL_ID_SCROLL_DOWN:
10947       if (game_status == GAME_MODE_NAMES)
10948         HandleChoosePlayerName(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10949       else if (game_status == GAME_MODE_LEVELS)
10950         HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10951       else if (game_status == GAME_MODE_LEVELNR)
10952         HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10953       else if (game_status == GAME_MODE_SETUP)
10954         HandleSetupScreen(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10955       else if (game_status == GAME_MODE_INFO)
10956         HandleInfoScreen(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10957       else if (game_status == GAME_MODE_SCORES)
10958         HandleHallOfFame(0, 0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
10959       break;
10960
10961     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
10962       if (game_status == GAME_MODE_NAMES)
10963         HandleChoosePlayerName(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10964       else if (game_status == GAME_MODE_LEVELS)
10965         HandleChooseLevelSet(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10966       else if (game_status == GAME_MODE_LEVELNR)
10967         HandleChooseLevelNr(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10968       else if (game_status == GAME_MODE_SETUP)
10969         HandleSetupScreen(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10970       else if (game_status == GAME_MODE_INFO)
10971         HandleInfoScreen(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10972       else if (game_status == GAME_MODE_SCORES)
10973         HandleHallOfFame(0, 0, 999, gi->event.item_position, MB_MENU_INITIALIZE);
10974       break;
10975
10976     case SCREEN_CTRL_ID_NETWORK_SERVER:
10977     {
10978       if (!strEqual(gi->textinput.value, ""))
10979       {
10980         setString(&setup.network_server_hostname, gi->textinput.value);
10981
10982         network.server_host = setup.network_server_hostname;
10983       }
10984       else
10985       {
10986         setString(&setup.network_server_hostname, STR_NETWORK_AUTO_DETECT);
10987
10988         network.server_host = NULL;
10989       }
10990
10991       if (strEqual(network.server_host, STR_NETWORK_AUTO_DETECT))
10992         network.server_host = NULL;
10993
10994       execSetupGame_setNetworkServerText();
10995
10996       DrawSetupScreen();
10997
10998       break;
10999     }
11000
11001     default:
11002       break;
11003   }
11004 }
11005
11006 void HandleScreenGadgetKeys(Key key)
11007 {
11008   if (key == setup.shortcut.tape_play || key == KSYM_Return)
11009     HandleScreenGadgets(screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE]);
11010 }
11011
11012 void DumpScreenIdentifiers(void)
11013 {
11014   int i;
11015
11016   Print("Active screen elements on current screen:\n");
11017
11018   for (i = 0; main_controls[i].nr != -1; i++)
11019   {
11020     struct MainControlInfo *mci = &main_controls[i];
11021
11022     if (mci->button_graphic != -1)
11023     {
11024       char *token = getTokenFromImageID(mci->button_graphic);
11025
11026       Print("- '%s'\n", token);
11027     }
11028   }
11029
11030   Print("Done.\n");
11031 }
11032
11033 boolean DoScreenAction(int image_id)
11034 {
11035   int i;
11036
11037   if (game_status != GAME_MODE_MAIN)
11038     return FALSE;
11039
11040   for (i = 0; main_controls[i].nr != -1; i++)
11041   {
11042     struct MainControlInfo *mci = &main_controls[i];
11043     struct MenuPosInfo *pos = mci->pos_button;
11044
11045     if (mci->button_graphic == image_id)
11046     {
11047       int x = mSX + pos->x;
11048       int y = mSY + pos->y;
11049
11050       HandleMainMenu(x, y, 0, 0, MB_MENU_CHOICE);
11051
11052       return TRUE;
11053     }
11054   }
11055
11056   return FALSE;
11057 }
11058
11059 void DrawScreenAfterAddingSet(char *tree_subdir_new, int tree_type)
11060 {
11061   // get tree info node of newly added level or artwork set
11062   TreeInfo *tree_node_first = TREE_FIRST_NODE(tree_type);
11063   TreeInfo *tree_node_new = getTreeInfoFromIdentifier(tree_node_first,
11064                                                       tree_subdir_new);
11065   if (tree_node_new == NULL)    // should not happen
11066     return;
11067
11068   // if request dialog is active, do nothing
11069   if (game.request_active)
11070     return;
11071
11072   if (game_status == GAME_MODE_MAIN &&
11073       tree_type == TREE_TYPE_LEVEL_DIR)
11074   {
11075     // when adding new level set in main menu, select it as current level set
11076
11077     // change current level set to newly added level set from zip file
11078     leveldir_current = tree_node_new;
11079
11080     // change current level number to first level of newly added level set
11081     level_nr = leveldir_current->first_level;
11082
11083     // redraw screen to reflect changed level set
11084     DrawMainMenu();
11085
11086     // save this level set and level number as last selected level set
11087     SaveLevelSetup_LastSeries();
11088     SaveLevelSetup_SeriesInfo();
11089   }
11090   else if (game_status == GAME_MODE_LEVELS &&
11091            tree_type == TREE_TYPE_LEVEL_DIR)
11092   {
11093     // when adding new level set in level set menu, set cursor and update screen
11094
11095     leveldir_current = tree_node_new;
11096
11097     DrawChooseTree(&leveldir_current);
11098   }
11099   else if (game_status == GAME_MODE_SETUP)
11100   {
11101     // when adding new artwork set in setup menu, set cursor and update screen
11102
11103     if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS &&
11104         tree_type == TREE_TYPE_GRAPHICS_DIR)
11105     {
11106       artwork.gfx_current = tree_node_new;
11107
11108       DrawChooseTree(&artwork.gfx_current);
11109     }
11110     else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS &&
11111              tree_type == TREE_TYPE_SOUNDS_DIR)
11112     {
11113       artwork.snd_current = tree_node_new;
11114
11115       DrawChooseTree(&artwork.snd_current);
11116     }
11117     else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC &&
11118              tree_type == TREE_TYPE_MUSIC_DIR)
11119     {
11120       artwork.mus_current = tree_node_new;
11121
11122       DrawChooseTree(&artwork.mus_current);
11123     }
11124   }
11125 }
11126
11127 static int UploadTapes(void)
11128 {
11129   SetGameStatus(GAME_MODE_LOADING);
11130
11131   FadeSetEnterScreen();
11132   FadeOut(REDRAW_ALL);
11133
11134   ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
11135
11136   FadeIn(REDRAW_ALL);
11137
11138   DrawInitTextHead("Uploading tapes");
11139
11140   global.autoplay_mode = AUTOPLAY_MODE_UPLOAD;
11141   global.autoplay_leveldir = "ALL";
11142   global.autoplay_all = TRUE;
11143
11144   int num_tapes_uploaded = AutoPlayTapes();
11145
11146   global.autoplay_mode = AUTOPLAY_MODE_NONE;
11147   global.autoplay_leveldir = NULL;
11148   global.autoplay_all = FALSE;
11149
11150   SetGameStatus(GAME_MODE_MAIN);
11151
11152   DrawMainMenu();
11153
11154   return num_tapes_uploaded;
11155 }
11156
11157 static boolean OfferUploadTapes(void)
11158 {
11159   if (!Request(setup.has_remaining_tapes ?
11160                "Upload missing tapes to the high score server now?" :
11161                "Upload all your tapes to the high score server now?", REQ_ASK))
11162     return FALSE;
11163
11164   // when uploading tapes, make sure that high score server is enabled
11165   runtime.use_api_server = setup.use_api_server = TRUE;
11166
11167   int num_tapes_uploaded = UploadTapes();
11168   char message[100];
11169
11170   if (num_tapes_uploaded < 0)
11171   {
11172     num_tapes_uploaded = -num_tapes_uploaded - 1;
11173
11174     if (num_tapes_uploaded == 0)
11175       sprintf(message, "Upload failed! No tapes uploaded!");
11176     else if (num_tapes_uploaded == 1)
11177       sprintf(message, "Upload failed! Only 1 tape uploaded!");
11178     else
11179       sprintf(message, "Upload failed! Only %d tapes uploaded!",
11180               num_tapes_uploaded);
11181
11182     Request(message, REQ_CONFIRM);
11183
11184     // if uploading tapes failed, add tape upload entry to setup menu
11185     setup.provide_uploading_tapes = TRUE;
11186     setup.has_remaining_tapes = TRUE;
11187
11188     SaveSetup_ServerSetup();
11189
11190     return FALSE;
11191   }
11192
11193   if (num_tapes_uploaded == 0)
11194     sprintf(message, "No tapes uploaded!");
11195   else if (num_tapes_uploaded == 1)
11196     sprintf(message, "1 tape uploaded!");
11197   else
11198     sprintf(message, "%d tapes uploaded!", num_tapes_uploaded);
11199
11200   Request(message, REQ_CONFIRM);
11201
11202   if (num_tapes_uploaded > 0)
11203     Request("New scores will be visible after a few minutes!", REQ_CONFIRM);
11204
11205   // after all tapes have been uploaded, remove entry from setup menu
11206   setup.provide_uploading_tapes = FALSE;
11207   setup.has_remaining_tapes = FALSE;
11208
11209   SaveSetup_ServerSetup();
11210
11211   return TRUE;
11212 }
11213
11214 static void CheckUploadTapes(void)
11215 {
11216   if (!setup.ask_for_uploading_tapes)
11217     return;
11218
11219   // after asking for uploading tapes, do not ask again
11220   setup.ask_for_uploading_tapes = FALSE;
11221   setup.ask_for_remaining_tapes = FALSE;
11222
11223   if (directoryExists(getTapeDir(NULL)))
11224   {
11225     boolean tapes_uploaded = OfferUploadTapes();
11226
11227     if (!tapes_uploaded)
11228     {
11229       Request(setup.has_remaining_tapes ?
11230               "You can upload missing tapes from the setup menu later!" :
11231               "You can upload your tapes from the setup menu later!",
11232               REQ_CONFIRM);
11233     }
11234   }
11235   else
11236   {
11237     // if tapes directory does not exist yet, never offer uploading all tapes
11238     setup.provide_uploading_tapes = FALSE;
11239   }
11240
11241   SaveSetup_ServerSetup();
11242 }
11243
11244 static void UpgradePlayerUUID(void)
11245 {
11246   ApiResetUUIDAsThread(getUUID());
11247 }
11248
11249 static void CheckUpgradePlayerUUID(void)
11250 {
11251   if (setup.player_version > 1)
11252     return;
11253
11254   UpgradePlayerUUID();
11255 }
11256
11257 void CheckApiServerTasks(void)
11258 {
11259   // check if the player's UUID has to be upgraded
11260   CheckUpgradePlayerUUID();
11261
11262   // check if there are any tapes to be uploaded
11263   CheckUploadTapes();
11264 }