fixed variable type
[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 //                  http://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
26
27 #define DEBUG_JOYSTICKS         0
28
29
30 /* screens on the info screen */
31 #define INFO_MODE_MAIN                  0
32 #define INFO_MODE_TITLE                 1
33 #define INFO_MODE_ELEMENTS              2
34 #define INFO_MODE_MUSIC                 3
35 #define INFO_MODE_CREDITS               4
36 #define INFO_MODE_PROGRAM               5
37 #define INFO_MODE_VERSION               6
38 #define INFO_MODE_LEVELSET              7
39
40 #define MAX_INFO_MODES                  8
41
42 /* screens on the setup screen */
43 /* (must match GFX_SPECIAL_ARG_SETUP_* values as defined in src/main.h) */
44 /* (should also match corresponding entries in src/conf_gfx.c) */
45 #define SETUP_MODE_MAIN                 0
46 #define SETUP_MODE_GAME                 1
47 #define SETUP_MODE_EDITOR               2
48 #define SETUP_MODE_GRAPHICS             3
49 #define SETUP_MODE_SOUND                4
50 #define SETUP_MODE_ARTWORK              5
51 #define SETUP_MODE_INPUT                6
52 #define SETUP_MODE_TOUCH                7
53 #define SETUP_MODE_SHORTCUTS            8
54 #define SETUP_MODE_SHORTCUTS_1          9
55 #define SETUP_MODE_SHORTCUTS_2          10
56 #define SETUP_MODE_SHORTCUTS_3          11
57 #define SETUP_MODE_SHORTCUTS_4          12
58 #define SETUP_MODE_SHORTCUTS_5          13
59
60 /* sub-screens on the setup screen (generic) */
61 #define SETUP_MODE_CHOOSE_ARTWORK       14
62 #define SETUP_MODE_CHOOSE_OTHER         15
63
64 /* sub-screens on the setup screen (specific) */
65 #define SETUP_MODE_CHOOSE_GAME_SPEED    16
66 #define SETUP_MODE_CHOOSE_SCROLL_DELAY  17
67 #define SETUP_MODE_CHOOSE_SNAPSHOT_MODE 18
68 #define SETUP_MODE_CHOOSE_WINDOW_SIZE   19
69 #define SETUP_MODE_CHOOSE_SCALING_TYPE  20
70 #define SETUP_MODE_CHOOSE_RENDERING     21
71 #define SETUP_MODE_CHOOSE_GRAPHICS      22
72 #define SETUP_MODE_CHOOSE_SOUNDS        23
73 #define SETUP_MODE_CHOOSE_MUSIC         24
74 #define SETUP_MODE_CHOOSE_VOLUME_SIMPLE 25
75 #define SETUP_MODE_CHOOSE_VOLUME_LOOPS  26
76 #define SETUP_MODE_CHOOSE_VOLUME_MUSIC  27
77 #define SETUP_MODE_CHOOSE_TOUCH_CONTROL 28
78 #define SETUP_MODE_CHOOSE_MOVE_DISTANCE 29
79 #define SETUP_MODE_CHOOSE_DROP_DISTANCE 30
80
81 #define MAX_SETUP_MODES                 31
82
83 #define MAX_MENU_MODES                  MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
84
85 /* setup screen titles */
86 #define STR_SETUP_MAIN                  "Setup"
87 #define STR_SETUP_GAME                  "Game & Menu"
88 #define STR_SETUP_EDITOR                "Editor"
89 #define STR_SETUP_GRAPHICS              "Graphics"
90 #define STR_SETUP_SOUND                 "Sound & Music"
91 #define STR_SETUP_ARTWORK               "Custom Artwork"
92 #define STR_SETUP_INPUT                 "Input Devices"
93 #define STR_SETUP_TOUCH                 "Touch Controls"
94 #define STR_SETUP_SHORTCUTS             "Key Shortcuts"
95 #define STR_SETUP_EXIT                  "Exit"
96 #define STR_SETUP_SAVE_AND_EXIT         "Save and Exit"
97
98 #define STR_SETUP_CHOOSE_GAME_SPEED     "Game Speed"
99 #define STR_SETUP_CHOOSE_SCROLL_DELAY   "Scroll Delay"
100 #define STR_SETUP_CHOOSE_SNAPSHOT_MODE  "Snapshot Mode"
101 #define STR_SETUP_CHOOSE_WINDOW_SIZE    "Window Scaling"
102 #define STR_SETUP_CHOOSE_SCALING_TYPE   "Anti-Aliasing"
103 #define STR_SETUP_CHOOSE_RENDERING      "Rendering Mode"
104 #define STR_SETUP_CHOOSE_VOLUME_SIMPLE  "Sound Volume"
105 #define STR_SETUP_CHOOSE_VOLUME_LOOPS   "Loops Volume"
106 #define STR_SETUP_CHOOSE_VOLUME_MUSIC   "Music Volume"
107 #define STR_SETUP_CHOOSE_TOUCH_CONTROL  "Control Type"
108 #define STR_SETUP_CHOOSE_MOVE_DISTANCE  "Move Distance"
109 #define STR_SETUP_CHOOSE_DROP_DISTANCE  "Drop Distance"
110
111 /* for input setup functions */
112 #define SETUPINPUT_SCREEN_POS_START     0
113 #define SETUPINPUT_SCREEN_POS_EMPTY1    3
114 #define SETUPINPUT_SCREEN_POS_EMPTY2    12
115 #define SETUPINPUT_SCREEN_POS_END       13
116
117 #define MENU_SETUP_FONT_TITLE           FONT_TEXT_1
118 #define MENU_SETUP_FONT_TEXT            FONT_TITLE_2
119
120 /* for various menu stuff  */
121 #define MENU_SCREEN_START_XPOS          1
122 #define MENU_SCREEN_START_YPOS          2
123 #define MENU_SCREEN_VALUE_XPOS          (SCR_FIELDX - 3)
124 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
125 #define MENU_TITLE1_YPOS                8
126 #define MENU_TITLE2_YPOS                46
127 #define MENU_INFO_FONT_TITLE            FONT_TEXT_1
128 #define MENU_INFO_FONT_HEAD             FONT_TEXT_2
129 #define MENU_INFO_FONT_TEXT             FONT_TEXT_3
130 #define MENU_INFO_FONT_FOOT             FONT_TEXT_4
131 #define MENU_INFO_SPACE_HEAD            (menu.headline2_spacing_info[info_mode])
132 #define MENU_SCREEN_INFO_SPACE_LEFT     (menu.left_spacing_info[info_mode])
133 #define MENU_SCREEN_INFO_SPACE_RIGHT    (menu.right_spacing_info[info_mode])
134 #define MENU_SCREEN_INFO_SPACE_TOP      (menu.top_spacing_info[info_mode])
135 #define MENU_SCREEN_INFO_SPACE_BOTTOM   (menu.bottom_spacing_info[info_mode])
136 #define MENU_SCREEN_INFO_YSTART1        MENU_SCREEN_INFO_SPACE_TOP
137 #define MENU_SCREEN_INFO_YSTART2        (MENU_SCREEN_INFO_YSTART1 +            \
138                                          getMenuTextStep(MENU_INFO_SPACE_HEAD, \
139                                                          MENU_INFO_FONT_TITLE))
140 #define MENU_SCREEN_INFO_YSTEP          (TILEY + 4)
141 #define MENU_SCREEN_INFO_YBOTTOM        (SYSIZE - MENU_SCREEN_INFO_SPACE_BOTTOM)
142 #define MENU_SCREEN_INFO_YSIZE          (MENU_SCREEN_INFO_YBOTTOM -     \
143                                          MENU_SCREEN_INFO_YSTART2 -     \
144                                          TILEY / 2)
145 #define MAX_INFO_ELEMENTS_ON_SCREEN     128
146 #define STD_INFO_ELEMENTS_ON_SCREEN     (MENU_SCREEN_INFO_YSIZE /       \
147                                          MENU_SCREEN_INFO_YSTEP)
148 #define NUM_INFO_ELEMENTS_FROM_CONF     \
149   (menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] > 0 ?             \
150    menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] :                 \
151    MAX_MENU_ENTRIES_ON_SCREEN)
152 #define NUM_INFO_ELEMENTS_ON_SCREEN     MIN(MIN(STD_INFO_ELEMENTS_ON_SCREEN, \
153                                                 MAX_INFO_ELEMENTS_ON_SCREEN), \
154                                             NUM_INFO_ELEMENTS_FROM_CONF)
155 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
156 #define MAX_MENU_TEXT_LENGTH_BIG        13
157 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
158
159 /* buttons and scrollbars identifiers */
160 #define SCREEN_CTRL_ID_PREV_LEVEL       0
161 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
162 #define SCREEN_CTRL_ID_PREV_PLAYER      2
163 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
164 #define SCREEN_CTRL_ID_SCROLL_UP        4
165 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
166 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
167
168 #define NUM_SCREEN_GADGETS              7
169
170 #define NUM_SCREEN_MENUBUTTONS          4
171 #define NUM_SCREEN_SCROLLBUTTONS        2
172 #define NUM_SCREEN_SCROLLBARS           1
173
174 #define SCREEN_MASK_MAIN                (1 << 0)
175 #define SCREEN_MASK_INPUT               (1 << 1)
176
177 /* graphic position and size values for buttons and scrollbars */
178 #define SC_MENUBUTTON_XSIZE             TILEX
179 #define SC_MENUBUTTON_YSIZE             TILEY
180
181 #define SC_SCROLLBUTTON_XSIZE           TILEX
182 #define SC_SCROLLBUTTON_YSIZE           TILEY
183
184 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
185
186 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
187 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
188                                          SC_SCROLLBUTTON_YSIZE)
189
190 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
191 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
192
193 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
194 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
195                                          SC_SCROLLBUTTON_YSIZE)
196
197 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
198 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
199                                          SC_SCROLL_VERTICAL_YSIZE)
200
201 #define SC_BORDER_SIZE                  14
202
203
204 /* forward declarations of internal functions */
205 static void HandleScreenGadgets(struct GadgetInfo *);
206 static void HandleSetupScreen_Generic(int, int, int, int, int);
207 static void HandleSetupScreen_Input(int, int, int, int, int);
208 static void CustomizeKeyboard(int);
209 static void ConfigureJoystick(int);
210 static void execSetupGame(void);
211 static void execSetupGraphics(void);
212 static void execSetupSound(void);
213 static void execSetupTouch(void);
214 static void execSetupArtwork(void);
215 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
216
217 static void DrawChooseLevelSet(void);
218 static void DrawChooseLevelNr(void);
219 static void DrawInfoScreen(void);
220 static void DrawSetupScreen(void);
221
222 static void DrawInfoScreen_NotAvailable(char *, char *);
223 static void DrawInfoScreen_HelpAnim(int, int, boolean);
224 static void DrawInfoScreen_HelpText(int, int, int, int);
225 static void HandleInfoScreen_Main(int, int, int, int, int);
226 static void HandleInfoScreen_TitleScreen(int);
227 static void HandleInfoScreen_Elements(int);
228 static void HandleInfoScreen_Music(int);
229 static void HandleInfoScreen_Credits(int);
230 static void HandleInfoScreen_Program(int);
231 static void HandleInfoScreen_Version(int);
232
233 static void MapScreenMenuGadgets(int);
234 static void MapScreenGadgets(int);
235 static void MapScreenTreeGadgets(TreeInfo *);
236
237 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
238
239 static int info_mode = INFO_MODE_MAIN;
240 static int setup_mode = SETUP_MODE_MAIN;
241
242 static TreeInfo *window_sizes = NULL;
243 static TreeInfo *window_size_current = NULL;
244
245 static TreeInfo *scaling_types = NULL;
246 static TreeInfo *scaling_type_current = NULL;
247
248 static TreeInfo *rendering_modes = NULL;
249 static TreeInfo *rendering_mode_current = NULL;
250
251 static TreeInfo *scroll_delays = NULL;
252 static TreeInfo *scroll_delay_current = NULL;
253
254 static TreeInfo *snapshot_modes = NULL;
255 static TreeInfo *snapshot_mode_current = NULL;
256
257 static TreeInfo *game_speeds = NULL;
258 static TreeInfo *game_speed_current = NULL;
259
260 static TreeInfo *volumes_simple = NULL;
261 static TreeInfo *volume_simple_current = NULL;
262
263 static TreeInfo *volumes_loops = NULL;
264 static TreeInfo *volume_loops_current = NULL;
265
266 static TreeInfo *volumes_music = NULL;
267 static TreeInfo *volume_music_current = NULL;
268
269 static TreeInfo *touch_controls = NULL;
270 static TreeInfo *touch_control_current = NULL;
271
272 static TreeInfo *move_distances = NULL;
273 static TreeInfo *move_distance_current = NULL;
274
275 static TreeInfo *drop_distances = NULL;
276 static TreeInfo *drop_distance_current = NULL;
277
278 static TreeInfo *level_number = NULL;
279 static TreeInfo *level_number_current = NULL;
280
281 static struct
282 {
283   int value;
284   char *text;
285 } window_sizes_list[] =
286 {
287   {     50,     "50 %"                          },
288   {     80,     "80 %"                          },
289   {     90,     "90 %"                          },
290   {     100,    "100 % (Default)"               },
291   {     110,    "110 %"                         },
292   {     120,    "120 %"                         },
293   {     130,    "130 %"                         },
294   {     140,    "140 %"                         },
295   {     150,    "150 %"                         },
296   {     200,    "200 %"                         },
297   {     250,    "250 %"                         },
298   {     300,    "300 %"                         },
299
300   {     -1,     NULL                            },
301 };
302
303 static struct
304 {
305   char *value;
306   char *text;
307 } scaling_types_list[] =
308 {
309   {     SCALING_QUALITY_NEAREST, "Off"          },
310   {     SCALING_QUALITY_LINEAR,  "Linear"       },
311   {     SCALING_QUALITY_BEST,    "Anisotropic"  },
312
313   {     NULL,                    NULL           },
314 };
315
316 static struct
317 {
318   char *value;
319   char *text;
320 } rendering_modes_list[] =
321 {
322   {     STR_SPECIAL_RENDERING_OFF,      "Off (May show artifacts, fast)" },
323   {     STR_SPECIAL_RENDERING_BITMAP,   "Bitmap/Texture mode (slower)"   },
324 #if DEBUG
325   // this mode may work under certain conditions, but does not work on Windows
326   {     STR_SPECIAL_RENDERING_TARGET,   "Target Texture mode (slower)"   },
327 #endif
328   {     STR_SPECIAL_RENDERING_DOUBLE,   "Double Texture mode (slower)"   },
329
330   {     NULL,                            NULL                            },
331 };
332
333 static struct
334 {
335   int value;
336   char *text;
337 } game_speeds_list[] =
338 {
339 #if 1
340   {     30,     "Very Slow"                     },
341   {     25,     "Slow"                          },
342   {     20,     "Normal"                        },
343   {     15,     "Fast"                          },
344   {     10,     "Very Fast"                     },
345 #else
346   {     1000,   "1/1s (Extremely Slow)"         },
347   {     500,    "1/2s"                          },
348   {     200,    "1/5s"                          },
349   {     100,    "1/10s"                         },
350   {     50,     "1/20s"                         },
351   {     29,     "1/35s (Original Supaplex)"     },
352   {     25,     "1/40s"                         },
353   {     20,     "1/50s (Normal Speed)"          },
354   {     14,     "1/70s (Maximum Supaplex)"      },
355   {     10,     "1/100s"                        },
356   {     5,      "1/200s"                        },
357   {     2,      "1/500s"                        },
358   {     1,      "1/1000s (Extremely Fast)"      },
359 #endif
360
361   {     -1,     NULL                            },
362 };
363
364 static struct
365 {
366   int value;
367   char *text;
368 } scroll_delays_list[] =
369 {
370   {     0,      "0 Tiles (No Scroll Delay)"     },
371   {     1,      "1 Tile"                        },
372   {     2,      "2 Tiles"                       },
373   {     3,      "3 Tiles (Default)"             },
374   {     4,      "4 Tiles"                       },
375   {     5,      "5 Tiles"                       },
376   {     6,      "6 Tiles"                       },
377   {     7,      "7 Tiles"                       },
378   {     8,      "8 Tiles (Maximum Scroll Delay)"},
379
380   {     -1,     NULL                            },
381 };
382
383 static struct
384 {
385   char *value;
386   char *text;
387 } snapshot_modes_list[] =
388 {
389   {     STR_SNAPSHOT_MODE_OFF,                  "Off"           },
390   {     STR_SNAPSHOT_MODE_EVERY_STEP,           "Every Step"    },
391   {     STR_SNAPSHOT_MODE_EVERY_MOVE,           "Every Move"    },
392   {     STR_SNAPSHOT_MODE_EVERY_COLLECT,        "Every Collect" },
393
394   {     NULL,                                   NULL            },
395 };
396
397 static struct
398 {
399   int value;
400   char *text;
401 } volumes_list[] =
402 {
403   {     0,      "0 %"                           },
404   {     1,      "1 %"                           },
405   {     2,      "2 %"                           },
406   {     5,      "5 %"                           },
407   {     10,     "10 %"                          },
408   {     20,     "20 %"                          },
409   {     30,     "30 %"                          },
410   {     40,     "40 %"                          },
411   {     50,     "50 %"                          },
412   {     60,     "60 %"                          },
413   {     70,     "70 %"                          },
414   {     80,     "80 %"                          },
415   {     90,     "90 %"                          },
416   {     100,    "100 %"                         },
417
418   {     -1,     NULL                            },
419 };
420
421 static struct
422 {
423   char *value;
424   char *text;
425 } touch_controls_list[] =
426 {
427   {     TOUCH_CONTROL_OFF,              "Off"                   },
428   {     TOUCH_CONTROL_VIRTUAL_BUTTONS,  "Virtual Buttons"       },
429   {     TOUCH_CONTROL_WIPE_GESTURES,    "Wipe Gestures"         },
430   {     TOUCH_CONTROL_FOLLOW_FINGER,    "Follow Finger"         },
431
432   {     NULL,                           NULL                    },
433 };
434
435 static struct
436 {
437   int value;
438   char *text;
439 } distances_list[] =
440 {
441   {     1,      "1 %"                           },
442   {     2,      "2 %"                           },
443   {     3,      "3 %"                           },
444   {     4,      "4 %"                           },
445   {     5,      "5 %"                           },
446   {     10,     "10 %"                          },
447   {     15,     "15 %"                          },
448   {     20,     "20 %"                          },
449   {     25,     "25 %"                          },
450
451   {     -1,     NULL                            },
452 };
453
454 #define DRAW_MODE(s)            ((s) >= GAME_MODE_MAIN &&               \
455                                  (s) <= GAME_MODE_SETUP ? (s) :         \
456                                  (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
457                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
458
459 /* (there are no draw offset definitions needed for INFO_MODE_TITLE) */
460 #define DRAW_MODE_INFO(i)       ((i) >= INFO_MODE_TITLE &&              \
461                                  (i) <= INFO_MODE_LEVELSET ? (i) :      \
462                                  INFO_MODE_MAIN)
463
464 #define DRAW_MODE_SETUP(i)      ((i) >= SETUP_MODE_MAIN &&              \
465                                  (i) <= SETUP_MODE_SHORTCUTS_5 ? (i) :  \
466                                  (i) >= SETUP_MODE_CHOOSE_GRAPHICS &&   \
467                                  (i) <= SETUP_MODE_CHOOSE_MUSIC ?       \
468                                  SETUP_MODE_CHOOSE_ARTWORK :            \
469                                  SETUP_MODE_CHOOSE_OTHER)
470
471 #define DRAW_XOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
472                                  menu.draw_xoffset[GAME_MODE_INFO] :    \
473                                  menu.draw_xoffset_info[DRAW_MODE_INFO(i)])
474 #define DRAW_YOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
475                                  menu.draw_yoffset[GAME_MODE_INFO] :    \
476                                  menu.draw_yoffset_info[DRAW_MODE_INFO(i)])
477 #define EXTRA_SPACING_INFO(i)   (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ? \
478                                  menu.extra_spacing[GAME_MODE_INFO] :   \
479                                  menu.extra_spacing_info[DRAW_MODE_INFO(i)])
480
481 #define DRAW_XOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
482                                  menu.draw_xoffset[GAME_MODE_SETUP] :   \
483                                  menu.draw_xoffset_setup[DRAW_MODE_SETUP(i)])
484 #define DRAW_YOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
485                                  menu.draw_yoffset[GAME_MODE_SETUP] :   \
486                                  menu.draw_yoffset_setup[DRAW_MODE_SETUP(i)])
487 #define EXTRA_SPACING_SETUP(i)  (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
488                                  menu.extra_spacing[GAME_MODE_SETUP] :  \
489                                  menu.extra_spacing_setup[DRAW_MODE_SETUP(i)])
490
491 #define DRAW_XOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
492                                  DRAW_XOFFSET_INFO(info_mode) :         \
493                                  (s) == GAME_MODE_SETUP ?               \
494                                  DRAW_XOFFSET_SETUP(setup_mode) :       \
495                                  menu.draw_xoffset[DRAW_MODE(s)])
496 #define DRAW_YOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
497                                  DRAW_YOFFSET_INFO(info_mode) :         \
498                                  (s) == GAME_MODE_SETUP ?               \
499                                  DRAW_YOFFSET_SETUP(setup_mode) :       \
500                                  menu.draw_yoffset[DRAW_MODE(s)])
501 #define EXTRA_SPACING(s)        ((s) == GAME_MODE_INFO ?                \
502                                  EXTRA_SPACING_INFO(info_mode) :        \
503                                  (s) == GAME_MODE_SETUP ?               \
504                                  EXTRA_SPACING_SETUP(setup_mode) :      \
505                                  menu.extra_spacing[DRAW_MODE(s)])
506
507 #define mSX                     (SX + DRAW_XOFFSET(game_status))
508 #define mSY                     (SY + DRAW_YOFFSET(game_status))
509
510 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
511                                     menu.list_size[game_status] :       \
512                                     MAX_MENU_ENTRIES_ON_SCREEN)
513
514 #define IN_VIS_MENU(x, y)       IN_FIELD(x, y, SCR_FIELDX,              \
515                                          NUM_MENU_ENTRIES_ON_SCREEN)
516
517
518 /* title display and control definitions */
519
520 #define MAX_NUM_TITLE_SCREENS   (2 * MAX_NUM_TITLE_IMAGES +             \
521                                  2 * MAX_NUM_TITLE_MESSAGES)
522
523 #define NO_DIRECT_LEVEL_SELECT  (-1)
524
525
526 static int num_title_screens = 0;
527
528 struct TitleControlInfo
529 {
530   boolean is_image;
531   boolean initial;
532   boolean first;
533   int local_nr;
534   int sort_priority;
535 };
536
537 struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS];
538
539 /* main menu display and control definitions */
540
541 #define MAIN_CONTROL_NAME                       0
542 #define MAIN_CONTROL_LEVELS                     1
543 #define MAIN_CONTROL_SCORES                     2
544 #define MAIN_CONTROL_EDITOR                     3
545 #define MAIN_CONTROL_INFO                       4
546 #define MAIN_CONTROL_GAME                       5
547 #define MAIN_CONTROL_SETUP                      6
548 #define MAIN_CONTROL_QUIT                       7
549 #define MAIN_CONTROL_PREV_LEVEL                 8
550 #define MAIN_CONTROL_NEXT_LEVEL                 9
551 #define MAIN_CONTROL_FIRST_LEVEL                10
552 #define MAIN_CONTROL_LAST_LEVEL                 11
553 #define MAIN_CONTROL_LEVEL_NUMBER               12
554 #define MAIN_CONTROL_LEVEL_INFO_1               13
555 #define MAIN_CONTROL_LEVEL_INFO_2               14
556 #define MAIN_CONTROL_LEVEL_NAME                 15
557 #define MAIN_CONTROL_LEVEL_AUTHOR               16
558 #define MAIN_CONTROL_LEVEL_YEAR                 17
559 #define MAIN_CONTROL_LEVEL_IMPORTED_FROM        18
560 #define MAIN_CONTROL_LEVEL_IMPORTED_BY          19
561 #define MAIN_CONTROL_LEVEL_TESTED_BY            20
562 #define MAIN_CONTROL_TITLE_1                    21
563 #define MAIN_CONTROL_TITLE_2                    22
564 #define MAIN_CONTROL_TITLE_3                    23
565
566 static char str_main_text_name[10];
567 static char str_main_text_first_level[10];
568 static char str_main_text_last_level[10];
569 static char str_main_text_level_number[10];
570
571 static char *main_text_name                     = str_main_text_name;
572 static char *main_text_first_level              = str_main_text_first_level;
573 static char *main_text_last_level               = str_main_text_last_level;
574 static char *main_text_level_number             = str_main_text_level_number;
575 static char *main_text_levels                   = "Levelset";
576 static char *main_text_scores                   = "Hall Of Fame";
577 static char *main_text_editor                   = "Level Creator";
578 static char *main_text_info                     = "Info Screen";
579 static char *main_text_game                     = "Start Game";
580 static char *main_text_setup                    = "Setup";
581 static char *main_text_quit                     = "Quit";
582 static char *main_text_level_name               = level.name;
583 static char *main_text_level_author             = level.author;
584 static char *main_text_level_year               = NULL;
585 static char *main_text_level_imported_from      = NULL;
586 static char *main_text_level_imported_by        = NULL;
587 static char *main_text_level_tested_by          = NULL;
588 static char *main_text_title_1                  = NULL;
589 static char *main_text_title_2                  = NULL;
590 static char *main_text_title_3                  = NULL;
591
592 struct MainControlInfo
593 {
594   int nr;
595
596   struct MenuPosInfo *pos_button;
597   int button_graphic;
598
599   struct TextPosInfo *pos_text;
600   char **text;
601
602   struct TextPosInfo *pos_input;
603   char **input;
604 };
605
606 static struct MainControlInfo main_controls[] =
607 {
608   {
609     MAIN_CONTROL_NAME,
610     &menu.main.button.name,             IMG_MENU_BUTTON_NAME,
611     &menu.main.text.name,               &main_text_name,
612     &menu.main.input.name,              &setup.player_name,
613   },
614   {
615     MAIN_CONTROL_LEVELS,
616     &menu.main.button.levels,           IMG_MENU_BUTTON_LEVELS,
617     &menu.main.text.levels,             &main_text_levels,
618     NULL,                               NULL,
619   },
620   {
621     MAIN_CONTROL_SCORES,
622     &menu.main.button.scores,           IMG_MENU_BUTTON_SCORES,
623     &menu.main.text.scores,             &main_text_scores,
624     NULL,                               NULL,
625   },
626   {
627     MAIN_CONTROL_EDITOR,
628     &menu.main.button.editor,           IMG_MENU_BUTTON_EDITOR,
629     &menu.main.text.editor,             &main_text_editor,
630     NULL,                               NULL,
631   },
632   {
633     MAIN_CONTROL_INFO,
634     &menu.main.button.info,             IMG_MENU_BUTTON_INFO,
635     &menu.main.text.info,               &main_text_info,
636     NULL,                               NULL,
637   },
638   {
639     MAIN_CONTROL_GAME,
640     &menu.main.button.game,             IMG_MENU_BUTTON_GAME,
641     &menu.main.text.game,               &main_text_game,
642     NULL,                               NULL,
643   },
644   {
645     MAIN_CONTROL_SETUP,
646     &menu.main.button.setup,            IMG_MENU_BUTTON_SETUP,
647     &menu.main.text.setup,              &main_text_setup,
648     NULL,                               NULL,
649   },
650   {
651     MAIN_CONTROL_QUIT,
652     &menu.main.button.quit,             IMG_MENU_BUTTON_QUIT,
653     &menu.main.text.quit,               &main_text_quit,
654     NULL,                               NULL,
655   },
656 #if 0
657   /* (these two buttons are real gadgets) */
658   {
659     MAIN_CONTROL_PREV_LEVEL,
660     &menu.main.button.prev_level,       IMG_MENU_BUTTON_PREV_LEVEL,
661     NULL,                               NULL,
662     NULL,                               NULL,
663   },
664   {
665     MAIN_CONTROL_NEXT_LEVEL,
666     &menu.main.button.next_level,       IMG_MENU_BUTTON_NEXT_LEVEL,
667     NULL,                               NULL,
668     NULL,                               NULL,
669   },
670 #endif
671   {
672     MAIN_CONTROL_FIRST_LEVEL,
673     NULL,                               -1,
674     &menu.main.text.first_level,        &main_text_first_level,
675     NULL,                               NULL,
676   },
677   {
678     MAIN_CONTROL_LAST_LEVEL,
679     NULL,                               -1,
680     &menu.main.text.last_level,         &main_text_last_level,
681     NULL,                               NULL,
682   },
683   {
684     MAIN_CONTROL_LEVEL_NUMBER,
685     NULL,                               -1,
686     &menu.main.text.level_number,       &main_text_level_number,
687     NULL,                               NULL,
688   },
689   {
690     MAIN_CONTROL_LEVEL_INFO_1,
691     NULL,                               -1,
692     &menu.main.text.level_info_1,       NULL,
693     NULL,                               NULL,
694   },
695   {
696     MAIN_CONTROL_LEVEL_INFO_2,
697     NULL,                               -1,
698     &menu.main.text.level_info_2,       NULL,
699     NULL,                               NULL,
700   },
701   {
702     MAIN_CONTROL_LEVEL_NAME,
703     NULL,                               -1,
704     &menu.main.text.level_name,         &main_text_level_name,
705     NULL,                               NULL,
706   },
707   {
708     MAIN_CONTROL_LEVEL_AUTHOR,
709     NULL,                               -1,
710     &menu.main.text.level_author,       &main_text_level_author,
711     NULL,                               NULL,
712   },
713   {
714     MAIN_CONTROL_LEVEL_YEAR,
715     NULL,                               -1,
716     &menu.main.text.level_year,         &main_text_level_year,
717     NULL,                               NULL,
718   },
719   {
720     MAIN_CONTROL_LEVEL_IMPORTED_FROM,
721     NULL,                               -1,
722     &menu.main.text.level_imported_from, &main_text_level_imported_from,
723     NULL,                               NULL,
724   },
725   {
726     MAIN_CONTROL_LEVEL_IMPORTED_BY,
727     NULL,                               -1,
728     &menu.main.text.level_imported_by,  &main_text_level_imported_by,
729     NULL,                               NULL,
730   },
731   {
732     MAIN_CONTROL_LEVEL_TESTED_BY,
733     NULL,                               -1,
734     &menu.main.text.level_tested_by,    &main_text_level_tested_by,
735     NULL,                               NULL,
736   },
737   {
738     MAIN_CONTROL_TITLE_1,
739     NULL,                               -1,
740     &menu.main.text.title_1,            &main_text_title_1,
741     NULL,                               NULL,
742   },
743   {
744     MAIN_CONTROL_TITLE_2,
745     NULL,                               -1,
746     &menu.main.text.title_2,            &main_text_title_2,
747     NULL,                               NULL,
748   },
749   {
750     MAIN_CONTROL_TITLE_3,
751     NULL,                               -1,
752     &menu.main.text.title_3,            &main_text_title_3,
753     NULL,                               NULL,
754   },
755
756   {
757     -1,
758     NULL,                               -1,
759     NULL,                               NULL,
760     NULL,                               NULL,
761   }
762 };
763
764
765 static int getTitleScreenGraphic(int nr, boolean initial)
766 {
767   return (initial ? IMG_TITLESCREEN_INITIAL_1 : IMG_TITLESCREEN_1) + nr;
768 }
769
770 static struct TitleMessageInfo *getTitleMessageInfo(int nr, boolean initial)
771 {
772   return (initial ? &titlemessage_initial[nr] : &titlemessage[nr]);
773 }
774
775 #if 0
776 static int getTitleScreenGameMode(boolean initial)
777 {
778   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
779 }
780 #endif
781
782 static int getTitleMessageGameMode(boolean initial)
783 {
784   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
785 }
786
787 static int getTitleAnimMode(struct TitleControlInfo *tci)
788 {
789   int base = (tci->initial ? GAME_MODE_TITLE_INITIAL_1 : GAME_MODE_TITLE_1);
790
791   return base + tci->local_nr;
792 }
793
794 #if 0
795 static int getTitleScreenBackground(boolean initial)
796 {
797   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
798 }
799 #endif
800
801 #if 0
802 static int getTitleMessageBackground(int nr, boolean initial)
803 {
804   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
805 }
806 #endif
807
808 static int getTitleBackground(int nr, boolean initial, boolean is_image)
809 {
810   int base = (is_image ?
811               (initial ? IMG_BACKGROUND_TITLESCREEN_INITIAL_1 :
812                          IMG_BACKGROUND_TITLESCREEN_1) :
813               (initial ? IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
814                          IMG_BACKGROUND_TITLEMESSAGE_1));
815   int graphic_global = (initial ? IMG_BACKGROUND_TITLE_INITIAL :
816                                   IMG_BACKGROUND_TITLE);
817   int graphic_local = base + nr;
818
819   if (graphic_info[graphic_local].bitmap != NULL)
820     return graphic_local;
821
822   if (graphic_info[graphic_global].bitmap != NULL)
823     return graphic_global;
824
825   return IMG_UNDEFINED;
826 }
827
828 static int getTitleSound(struct TitleControlInfo *tci)
829 {
830   boolean is_image = tci->is_image;
831   int initial = tci->initial;
832   int nr = tci->local_nr;
833   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
834   int base = (is_image ?
835               (initial ? SND_BACKGROUND_TITLESCREEN_INITIAL_1 :
836                          SND_BACKGROUND_TITLESCREEN_1) :
837               (initial ? SND_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
838                          SND_BACKGROUND_TITLEMESSAGE_1));
839   int sound_global = menu.sound[mode];
840   int sound_local = base + nr;
841
842 #if 0
843   printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
844          nr, initial, is_image,
845          sound_global, getSoundListEntry(sound_global)->filename,
846          sound_local, getSoundListEntry(sound_local)->filename);
847 #endif
848
849   if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME))
850     return sound_local;
851
852   if (!strEqual(getSoundListEntry(sound_global)->filename, UNDEFINED_FILENAME))
853     return sound_global;
854
855   return SND_UNDEFINED;
856 }
857
858 static int getTitleMusic(struct TitleControlInfo *tci)
859 {
860   boolean is_image = tci->is_image;
861   int initial = tci->initial;
862   int nr = tci->local_nr;
863   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
864   int base = (is_image ?
865               (initial ? MUS_BACKGROUND_TITLESCREEN_INITIAL_1 :
866                          MUS_BACKGROUND_TITLESCREEN_1) :
867               (initial ? MUS_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
868                          MUS_BACKGROUND_TITLEMESSAGE_1));
869   int music_global = menu.music[mode];
870   int music_local = base + nr;
871
872 #if 0
873   printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
874          nr, initial, is_image,
875          music_global, getMusicListEntry(music_global)->filename,
876          music_local, getMusicListEntry(music_local)->filename);
877 #endif
878
879   if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME))
880     return music_local;
881
882   if (!strEqual(getMusicListEntry(music_global)->filename, UNDEFINED_FILENAME))
883     return music_global;
884
885   return MUS_UNDEFINED;
886 }
887
888 static struct TitleFadingInfo getTitleFading(struct TitleControlInfo *tci)
889 {
890   boolean is_image = tci->is_image;
891   boolean initial = tci->initial;
892   boolean first = tci->first;
893   int nr = tci->local_nr;
894   struct TitleMessageInfo tmi;
895   struct TitleFadingInfo ti;
896
897   tmi = (is_image ? (initial ? (first ?
898                                 titlescreen_initial_first[nr] :
899                                 titlescreen_initial[nr])
900                              : (first ?
901                                 titlescreen_first[nr] :
902                                 titlescreen[nr]))
903                   : (initial ? (first ?
904                                 titlemessage_initial_first[nr] :
905                                 titlemessage_initial[nr])
906                              : (first ?
907                                 titlemessage_first[nr] :
908                                 titlemessage[nr])));
909
910   ti.fade_mode  = tmi.fade_mode;
911   ti.fade_delay = tmi.fade_delay;
912   ti.post_delay = tmi.post_delay;
913   ti.auto_delay = tmi.auto_delay;
914
915   return ti;
916 }
917
918 static int compareTitleControlInfo(const void *object1, const void *object2)
919 {
920   const struct TitleControlInfo *tci1 = (struct TitleControlInfo *)object1;
921   const struct TitleControlInfo *tci2 = (struct TitleControlInfo *)object2;
922   int compare_result;
923
924   if (tci1->initial != tci2->initial)
925     compare_result = (tci1->initial ? -1 : +1);
926   else if (tci1->sort_priority != tci2->sort_priority)
927     compare_result = tci1->sort_priority - tci2->sort_priority;
928   else if (tci1->is_image != tci2->is_image)
929     compare_result = (tci1->is_image ? -1 : +1);
930   else
931     compare_result = tci1->local_nr - tci2->local_nr;
932
933   return compare_result;
934 }
935
936 static void InitializeTitleControlsExt_AddTitleInfo(boolean is_image,
937                                                     boolean initial,
938                                                     int nr, int sort_priority)
939 {
940   title_controls[num_title_screens].is_image = is_image;
941   title_controls[num_title_screens].initial = initial;
942   title_controls[num_title_screens].local_nr = nr;
943   title_controls[num_title_screens].sort_priority = sort_priority;
944
945   title_controls[num_title_screens].first = FALSE;      /* will be set later */
946
947   num_title_screens++;
948 }
949
950 static void InitializeTitleControls_CheckTitleInfo(boolean initial)
951 {
952   int i;
953
954   for (i = 0; i < MAX_NUM_TITLE_IMAGES; i++)
955   {
956     int graphic = getTitleScreenGraphic(i, initial);
957     Bitmap *bitmap = graphic_info[graphic].bitmap;
958     int sort_priority = graphic_info[graphic].sort_priority;
959
960     if (bitmap != NULL)
961       InitializeTitleControlsExt_AddTitleInfo(TRUE, initial, i, sort_priority);
962   }
963
964   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
965   {
966     struct TitleMessageInfo *tmi = getTitleMessageInfo(i, initial);
967     char *filename = getLevelSetTitleMessageFilename(i, initial);
968     int sort_priority = tmi->sort_priority;
969
970     if (filename != NULL)
971       InitializeTitleControlsExt_AddTitleInfo(FALSE, initial, i, sort_priority);
972   }
973 }
974
975 static void InitializeTitleControls(boolean show_title_initial)
976 {
977   num_title_screens = 0;
978
979   /* 1st step: initialize title screens for game start (only when starting) */
980   if (show_title_initial)
981     InitializeTitleControls_CheckTitleInfo(TRUE);
982
983   /* 2nd step: initialize title screens for current level set */
984   InitializeTitleControls_CheckTitleInfo(FALSE);
985
986   /* sort title screens according to sort_priority and title number */
987   qsort(title_controls, num_title_screens, sizeof(struct TitleControlInfo),
988         compareTitleControlInfo);
989
990   /* mark first title screen */
991   title_controls[0].first = TRUE;
992 }
993
994 static boolean visibleMenuPos(struct MenuPosInfo *pos)
995 {
996   return (pos != NULL && pos->x != -1 && pos->y != -1);
997 }
998
999 static boolean visibleTextPos(struct TextPosInfo *pos)
1000 {
1001   return (pos != NULL && pos->x != -1 && pos->y != -1);
1002 }
1003
1004 static void InitializeMainControls()
1005 {
1006   boolean local_team_mode = (!options.network && setup.team_mode);
1007   int i;
1008
1009   /* set main control text values to dynamically determined values */
1010   sprintf(main_text_name,         "%s",   local_team_mode ? "Team:" : "Name:");
1011
1012   strcpy(main_text_first_level,  int2str(leveldir_current->first_level,
1013                                          menu.main.text.first_level.size));
1014   strcpy(main_text_last_level,   int2str(leveldir_current->last_level,
1015                                          menu.main.text.last_level.size));
1016   strcpy(main_text_level_number, int2str(level_nr,
1017                                          menu.main.text.level_number.size));
1018
1019   main_text_level_year          = leveldir_current->year;
1020   main_text_level_imported_from = leveldir_current->imported_from;
1021   main_text_level_imported_by   = leveldir_current->imported_by;
1022   main_text_level_tested_by     = leveldir_current->tested_by;
1023
1024   main_text_title_1 = getConfigProgramTitleString();
1025   main_text_title_2 = getConfigProgramCopyrightString();
1026   main_text_title_3 = getConfigProgramCompanyString();
1027
1028   /* set main control screen positions to dynamically determined values */
1029   for (i = 0; main_controls[i].nr != -1; i++)
1030   {
1031     struct MainControlInfo *mci = &main_controls[i];
1032     int nr                         = mci->nr;
1033     struct MenuPosInfo *pos_button = mci->pos_button;
1034     struct TextPosInfo *pos_text   = mci->pos_text;
1035     struct TextPosInfo *pos_input  = mci->pos_input;
1036     char *text                     = (mci->text  ? *mci->text  : NULL);
1037     char *input                    = (mci->input ? *mci->input : NULL);
1038     int button_graphic             = mci->button_graphic;
1039     int font_text                  = (pos_text  ? pos_text->font  : -1);
1040     int font_input                 = (pos_input ? pos_input->font : -1);
1041
1042     int font_text_width   = (font_text  != -1 ? getFontWidth(font_text)   : 0);
1043     int font_text_height  = (font_text  != -1 ? getFontHeight(font_text)  : 0);
1044     int font_input_width  = (font_input != -1 ? getFontWidth(font_input)  : 0);
1045     int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0);
1046     int text_chars  = (text  != NULL ? strlen(text)  : 0);
1047     int input_chars = (input != NULL ? strlen(input) : 0);
1048
1049     int button_width =
1050       (button_graphic != -1 ? graphic_info[button_graphic].width  : 0);
1051     int button_height =
1052       (button_graphic != -1 ? graphic_info[button_graphic].height : 0);
1053     int text_width   = font_text_width * text_chars;
1054     int text_height  = font_text_height;
1055     int input_width  = font_input_width * input_chars;
1056     int input_height = font_input_height;
1057
1058     if (nr == MAIN_CONTROL_NAME)
1059     {
1060       menu.main.input.name.width  = input_width;
1061       menu.main.input.name.height = input_height;
1062     }
1063
1064     if (pos_button != NULL)             /* (x/y may be -1/-1 here) */
1065     {
1066       pos_button->width  = button_width;
1067       pos_button->height = button_height;
1068     }
1069
1070     if (pos_text != NULL)               /* (x/y may be -1/-1 here) */
1071     {
1072       /* calculate size for non-clickable text -- needed for text alignment */
1073       boolean calculate_text_size = (pos_button == NULL && text != NULL);
1074
1075       if (pos_text->width == -1 || calculate_text_size)
1076         pos_text->width = text_width;
1077       if (pos_text->height == -1 || calculate_text_size)
1078         pos_text->height = text_height;
1079
1080       if (visibleMenuPos(pos_button))
1081       {
1082         if (pos_text->x == -1)
1083           pos_text->x = pos_button->x + pos_button->width;
1084         if (pos_text->y == -1)
1085           pos_text->y =
1086             pos_button->y + (pos_button->height - pos_text->height) / 2;
1087       }
1088     }
1089
1090     if (pos_input != NULL)              /* (x/y may be -1/-1 here) */
1091     {
1092       if (visibleTextPos(pos_text))
1093       {
1094         if (pos_input->x == -1)
1095           pos_input->x = pos_text->x + pos_text->width;
1096         if (pos_input->y == -1)
1097           pos_input->y = pos_text->y;
1098       }
1099
1100       if (pos_input->width == -1)
1101         pos_input->width = input_width;
1102       if (pos_input->height == -1)
1103         pos_input->height = input_height;
1104     }
1105   }
1106 }
1107
1108 static void DrawPressedGraphicThruMask(int dst_x, int dst_y,
1109                                        int graphic, boolean pressed)
1110 {
1111   struct GraphicInfo *g = &graphic_info[graphic];
1112   Bitmap *src_bitmap;
1113   int src_x, src_y;
1114   int xoffset = (pressed ? g->pressed_xoffset : 0);
1115   int yoffset = (pressed ? g->pressed_yoffset : 0);
1116
1117   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1118
1119   BlitBitmapMasked(src_bitmap, drawto, src_x + xoffset, src_y + yoffset,
1120                    g->width, g->height, dst_x, dst_y);
1121 }
1122
1123 static void DrawCursorAndText_Main_Ext(int nr, boolean active_text,
1124                                        boolean active_input,
1125                                        boolean pressed_button)
1126 {
1127   int i;
1128
1129   for (i = 0; main_controls[i].nr != -1; i++)
1130   {
1131     struct MainControlInfo *mci = &main_controls[i];
1132
1133     if (mci->nr == nr || nr == -1)
1134     {
1135       struct MenuPosInfo *pos_button = mci->pos_button;
1136       struct TextPosInfo *pos_text   = mci->pos_text;
1137       struct TextPosInfo *pos_input  = mci->pos_input;
1138       char *text                     = (mci->text  ? *mci->text  : NULL);
1139       char *input                    = (mci->input ? *mci->input : NULL);
1140       int button_graphic             = mci->button_graphic;
1141       int font_text                  = (pos_text  ? pos_text->font  : -1);
1142       int font_input                 = (pos_input ? pos_input->font : -1);
1143
1144       if (active_text)
1145       {
1146         button_graphic = BUTTON_ACTIVE(button_graphic);
1147         font_text = FONT_ACTIVE(font_text);
1148       }
1149
1150       if (active_input)
1151       {
1152         font_input = FONT_ACTIVE(font_input);
1153       }
1154
1155       if (visibleMenuPos(pos_button))
1156       {
1157         struct MenuPosInfo *pos = pos_button;
1158         int x = mSX + pos->x;
1159         int y = mSY + pos->y;
1160
1161         DrawBackgroundForGraphic(x, y, pos->width, pos->height, button_graphic);
1162         DrawPressedGraphicThruMask(x, y, button_graphic, pressed_button);
1163       }
1164
1165       if (visibleTextPos(pos_text) && text != NULL)
1166       {
1167         struct TextPosInfo *pos = pos_text;
1168         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1169         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1170
1171 #if 1
1172         /* (check why/if this is needed) */
1173         DrawBackgroundForFont(x, y, pos->width, pos->height, font_text);
1174 #endif
1175         DrawText(x, y, text, font_text);
1176       }
1177
1178       if (visibleTextPos(pos_input) && input != NULL)
1179       {
1180         struct TextPosInfo *pos = pos_input;
1181         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1182         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1183
1184 #if 1
1185         /* (check why/if this is needed) */
1186         DrawBackgroundForFont(x, y, pos->width, pos->height, font_input);
1187 #endif
1188         DrawText(x, y, input, font_input);
1189       }
1190     }
1191   }
1192 }
1193
1194 static void DrawCursorAndText_Main(int nr, boolean active_text,
1195                                    boolean pressed_button)
1196 {
1197   DrawCursorAndText_Main_Ext(nr, active_text, FALSE, pressed_button);
1198 }
1199
1200 #if 0
1201 static void DrawCursorAndText_Main_Input(int nr, boolean active_text,
1202                                          boolean pressed_button)
1203 {
1204   DrawCursorAndText_Main_Ext(nr, active_text, TRUE, pressed_button);
1205 }
1206 #endif
1207
1208 static struct MainControlInfo *getMainControlInfo(int nr)
1209 {
1210   int i;
1211
1212   for (i = 0; main_controls[i].nr != -1; i++)
1213     if (main_controls[i].nr == nr)
1214       return &main_controls[i];
1215
1216   return NULL;
1217 }
1218
1219 static boolean insideMenuPosRect(struct MenuPosInfo *rect, int x, int y)
1220 {
1221   if (rect == NULL)
1222     return FALSE;
1223
1224   int rect_x = ALIGNED_TEXT_XPOS(rect);
1225   int rect_y = ALIGNED_TEXT_YPOS(rect);
1226
1227   return (x >= rect_x && x < rect_x + rect->width &&
1228           y >= rect_y && y < rect_y + rect->height);
1229 }
1230
1231 static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
1232 {
1233   if (rect == NULL)
1234     return FALSE;
1235
1236   int rect_x = ALIGNED_TEXT_XPOS(rect);
1237   int rect_y = ALIGNED_TEXT_YPOS(rect);
1238
1239 #if 0
1240   printf("::: insideTextPosRect: (%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d\n",
1241          x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
1242          (x >= rect_x && x < rect_x + rect->width &&
1243           y >= rect_y && y < rect_y + rect->height));
1244 #endif
1245
1246   return (x >= rect_x && x < rect_x + rect->width &&
1247           y >= rect_y && y < rect_y + rect->height);
1248 }
1249
1250 static boolean insidePreviewRect(struct PreviewInfo *preview, int x, int y)
1251 {
1252   int rect_width  = preview->xsize * preview->tile_size;
1253   int rect_height = preview->ysize * preview->tile_size;
1254   int rect_x = ALIGNED_XPOS(preview->x, rect_width,  preview->align);
1255   int rect_y = ALIGNED_YPOS(preview->y, rect_height, preview->valign);
1256
1257   return (x >= rect_x && x < rect_x + rect_width &&
1258           y >= rect_y && y < rect_y + rect_height);
1259 }
1260
1261 static void AdjustScrollbar(int id, int items_max, int items_visible,
1262                             int item_position)
1263 {
1264   struct GadgetInfo *gi = screen_gadget[id];
1265
1266   if (item_position > items_max - items_visible)
1267     item_position = items_max - items_visible;
1268
1269   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
1270                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
1271                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
1272 }
1273
1274 static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
1275 {
1276   AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN,
1277                   first_entry);
1278 }
1279
1280 static void clearMenuListArea()
1281 {
1282   int scrollbar_xpos = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
1283
1284   /* correct scrollbar position if placed outside menu (playfield) area */
1285   if (scrollbar_xpos > SX + SC_SCROLLBAR_XPOS)
1286     scrollbar_xpos = SX + SC_SCROLLBAR_XPOS;
1287
1288   /* clear menu list area, but not title or scrollbar */
1289   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
1290                  scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32);
1291 }
1292
1293 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
1294 {
1295   static int cursor_array[MAX_LEV_FIELDY];
1296   int x = mSX + TILEX * xpos;
1297   int y = mSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
1298
1299   if (xpos == 0)
1300   {
1301     if (graphic != -1)
1302       cursor_array[ypos] = graphic;
1303     else
1304       graphic = cursor_array[ypos];
1305   }
1306
1307   if (active)
1308     graphic = BUTTON_ACTIVE(graphic);
1309
1310   DrawBackgroundForGraphic(x, y, TILEX, TILEY, graphic);
1311   DrawFixedGraphicThruMaskExt(drawto, x, y, graphic, 0);
1312 }
1313
1314 static void initCursor(int ypos, int graphic)
1315 {
1316   drawCursorExt(0, ypos, FALSE, graphic);
1317 }
1318
1319 static void drawCursor(int ypos, boolean active)
1320 {
1321   drawCursorExt(0, ypos, active, -1);
1322 }
1323
1324 static void drawCursorXY(int xpos, int ypos, int graphic)
1325 {
1326   drawCursorExt(xpos, ypos, FALSE, graphic);
1327 }
1328
1329 static void drawChooseTreeCursor(int ypos, boolean active)
1330 {
1331   drawCursorExt(0, ypos, active, -1);
1332 }
1333
1334 void DrawHeadline()
1335 {
1336   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, main_text_title_1);
1337   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, main_text_title_2);
1338 }
1339
1340 void DrawTitleScreenImage(int nr, boolean initial)
1341 {
1342   int graphic = getTitleScreenGraphic(nr, initial);
1343   Bitmap *bitmap = graphic_info[graphic].bitmap;
1344   int width  = graphic_info[graphic].width;
1345   int height = graphic_info[graphic].height;
1346   int src_x = graphic_info[graphic].src_x;
1347   int src_y = graphic_info[graphic].src_y;
1348   int dst_x, dst_y;
1349
1350   if (bitmap == NULL)
1351     return;
1352
1353   if (width > WIN_XSIZE)
1354   {
1355     /* image width too large for window => center image horizontally */
1356     src_x = (width - WIN_XSIZE) / 2;
1357     width = WIN_XSIZE;
1358   }
1359
1360   if (height > WIN_YSIZE)
1361   {
1362     /* image height too large for window => center image vertically */
1363     src_y = (height - WIN_YSIZE) / 2;
1364     height = WIN_YSIZE;
1365   }
1366
1367   /* always display title screens centered */
1368   dst_x = (WIN_XSIZE - width) / 2;
1369   dst_y = (WIN_YSIZE - height) / 2;
1370
1371   SetDrawBackgroundMask(REDRAW_ALL);
1372   SetWindowBackgroundImage(getTitleBackground(nr, initial, TRUE));
1373
1374   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1375
1376   if (DrawingOnBackground(dst_x, dst_y))
1377     BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1378   else
1379     BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1380
1381   redraw_mask = REDRAW_ALL;
1382 }
1383
1384 void DrawTitleScreenMessage(int nr, boolean initial)
1385 {
1386   char *filename = getLevelSetTitleMessageFilename(nr, initial);
1387   struct TitleMessageInfo *tmi = getTitleMessageInfo(nr, initial);
1388
1389   if (filename == NULL)
1390     return;
1391
1392   /* force TITLE font on title message screen */
1393   SetFontStatus(getTitleMessageGameMode(initial));
1394
1395   /* if chars *and* width set to "-1", automatically determine width */
1396   if (tmi->chars == -1 && tmi->width == -1)
1397     tmi->width = viewport.window[game_status].width;
1398
1399   /* if lines *and* height set to "-1", automatically determine height */
1400   if (tmi->lines == -1 && tmi->height == -1)
1401     tmi->height = viewport.window[game_status].height;
1402
1403   /* if chars set to "-1", automatically determine by text and font width */
1404   if (tmi->chars == -1)
1405     tmi->chars = tmi->width / getFontWidth(tmi->font);
1406   else
1407     tmi->width = tmi->chars * getFontWidth(tmi->font);
1408
1409   /* if lines set to "-1", automatically determine by text and font height */
1410   if (tmi->lines == -1)
1411     tmi->lines = tmi->height / getFontHeight(tmi->font);
1412   else
1413     tmi->height = tmi->lines * getFontHeight(tmi->font);
1414
1415   /* if x set to "-1", automatically determine by width and alignment */
1416   if (tmi->x == -1)
1417     tmi->x = -1 * ALIGNED_XPOS(0, tmi->width, tmi->align);
1418
1419   /* if y set to "-1", automatically determine by height and alignment */
1420   if (tmi->y == -1)
1421     tmi->y = -1 * ALIGNED_YPOS(0, tmi->height, tmi->valign);
1422
1423   SetDrawBackgroundMask(REDRAW_ALL);
1424   SetWindowBackgroundImage(getTitleBackground(nr, initial, FALSE));
1425
1426   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1427
1428   DrawTextFile(ALIGNED_TEXT_XPOS(tmi), ALIGNED_TEXT_YPOS(tmi),
1429                filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
1430                tmi->autowrap, tmi->centered, tmi->parse_comments);
1431
1432   ResetFontStatus();
1433 }
1434
1435 void DrawTitleScreen()
1436 {
1437   KeyboardAutoRepeatOff();
1438
1439   HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE);
1440 }
1441
1442 boolean CheckTitleScreen(boolean levelset_has_changed)
1443 {
1444   static boolean show_title_initial = TRUE;
1445   boolean show_titlescreen = FALSE;
1446
1447   /* needed to be able to skip title screen, if no image or message defined */
1448   InitializeTitleControls(show_title_initial);
1449
1450   if (setup.show_titlescreen && (show_title_initial || levelset_has_changed))
1451     show_titlescreen = TRUE;
1452
1453   /* show initial title images and messages only once at program start */
1454   show_title_initial = FALSE;
1455
1456   return (show_titlescreen && num_title_screens > 0);
1457 }
1458
1459 void DrawMainMenu()
1460 {
1461   static LevelDirTree *leveldir_last_valid = NULL;
1462   boolean levelset_has_changed = FALSE;
1463   int fade_mask = REDRAW_FIELD;
1464
1465   LimitScreenUpdates(FALSE);
1466
1467   FadeSetLeaveScreen();
1468
1469   /* do not fade out here -- function may continue and fade on editor screen */
1470
1471   UnmapAllGadgets();
1472   FadeMenuSoundsAndMusic();
1473
1474   ExpireSoundLoops(FALSE);
1475
1476   KeyboardAutoRepeatOn();
1477
1478   audio.sound_deactivated = FALSE;
1479
1480   GetPlayerConfig();
1481
1482   /* needed if last screen was the playing screen, invoked from level editor */
1483   if (level_editor_test_game)
1484   {
1485     CloseDoor(DOOR_CLOSE_ALL);
1486
1487     SetGameStatus(GAME_MODE_EDITOR);
1488
1489     DrawLevelEd();
1490
1491     return;
1492   }
1493
1494   /* needed if last screen was the setup screen and fullscreen state changed */
1495   // (moved to "execSetupGraphics()" to change fullscreen state directly)
1496   // ToggleFullscreenOrChangeWindowScalingIfNeeded();
1497
1498   /* leveldir_current may be invalid (level group, parent link) */
1499   if (!validLevelSeries(leveldir_current))
1500     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
1501
1502   if (leveldir_current != leveldir_last_valid)
1503     levelset_has_changed = TRUE;
1504
1505   /* store valid level series information */
1506   leveldir_last_valid = leveldir_current;
1507
1508   init_last = init;                     /* switch to new busy animation */
1509
1510   /* needed if last screen (level choice) changed graphics, sounds or music */
1511   ReloadCustomArtwork(0);
1512
1513   if (CheckTitleScreen(levelset_has_changed))
1514   {
1515     SetGameStatus(GAME_MODE_TITLE);
1516
1517     DrawTitleScreen();
1518
1519     return;
1520   }
1521
1522   if (redraw_mask & REDRAW_ALL)
1523     fade_mask = REDRAW_ALL;
1524
1525   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
1526     fade_mask = REDRAW_ALL;
1527
1528   FadeOut(fade_mask);
1529
1530   /* needed if different viewport properties defined for menues */
1531   ChangeViewportPropertiesIfNeeded();
1532
1533   SetDrawtoField(DRAW_TO_BACKBUFFER);
1534
1535   /* level_nr may have been set to value over handicap with level editor */
1536   if (setup.handicap && level_nr > leveldir_current->handicap_level)
1537     level_nr = leveldir_current->handicap_level;
1538
1539   LoadLevel(level_nr);
1540   LoadScore(level_nr);
1541
1542   SaveLevelSetup_SeriesInfo();
1543
1544   // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it)
1545   SetDrawDeactivationMask(REDRAW_NONE);
1546   SetDrawBackgroundMask(REDRAW_FIELD);
1547
1548   SetMainBackgroundImage(IMG_BACKGROUND_MAIN);
1549
1550 #if 0
1551   if (fade_mask == REDRAW_ALL)
1552     RedrawGlobalBorder();
1553 #endif
1554
1555   ClearField();
1556
1557   InitializeMainControls();
1558
1559   DrawCursorAndText_Main(-1, FALSE, FALSE);
1560   DrawPreviewLevelInitial();
1561
1562   HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE);
1563
1564   TapeStop();
1565   if (TAPE_IS_EMPTY(tape))
1566     LoadTape(level_nr);
1567   DrawCompleteVideoDisplay();
1568
1569   PlayMenuSoundsAndMusic();
1570
1571   /* create gadgets for main menu screen */
1572   FreeScreenGadgets();
1573   CreateScreenGadgets();
1574
1575   /* map gadgets for main menu screen */
1576   MapTapeButtons();
1577   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
1578
1579   /* copy actual game door content to door double buffer for OpenDoor() */
1580   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
1581   BlitBitmap(drawto, bitmap_db_door_2, VX, VY, VXSIZE, VYSIZE, 0, 0);
1582
1583   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1584
1585   DrawMaskedBorder(fade_mask);
1586
1587   FadeIn(fade_mask);
1588   FadeSetEnterMenu();
1589
1590   /* update screen area with special editor door */
1591   redraw_mask |= REDRAW_ALL;
1592   BackToFront();
1593
1594   SetMouseCursor(CURSOR_DEFAULT);
1595
1596   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
1597 }
1598
1599 static void gotoTopLevelDir()
1600 {
1601   /* move upwards until inside (but not above) top level directory */
1602   while (leveldir_current->node_parent &&
1603          !strEqual(leveldir_current->node_parent->subdir, STRING_TOP_DIRECTORY))
1604   {
1605     /* write a "path" into level tree for easy navigation to last level */
1606     if (leveldir_current->node_parent->node_group->cl_first == -1)
1607     {
1608       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
1609       int leveldir_pos = posTreeInfo(leveldir_current);
1610       int num_page_entries;
1611       int cl_first, cl_cursor;
1612
1613       if (num_leveldirs <= NUM_MENU_ENTRIES_ON_SCREEN)
1614         num_page_entries = num_leveldirs;
1615       else
1616         num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
1617
1618       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
1619       cl_cursor = leveldir_pos - cl_first;
1620
1621       leveldir_current->node_parent->node_group->cl_first = cl_first;
1622       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
1623     }
1624
1625     leveldir_current = leveldir_current->node_parent;
1626   }
1627 }
1628
1629 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
1630 {
1631   static unsigned int title_delay = 0;
1632   static int title_screen_nr = 0;
1633   static int last_sound = -1, last_music = -1;
1634   boolean return_to_main_menu = FALSE;
1635   struct TitleControlInfo *tci;
1636   int sound, music;
1637
1638   if (button == MB_MENU_INITIALIZE)
1639   {
1640     title_delay = 0;
1641     title_screen_nr = 0;
1642     tci = &title_controls[title_screen_nr];
1643
1644     SetAnimStatus(getTitleAnimMode(tci));
1645
1646     last_sound = SND_UNDEFINED;
1647     last_music = MUS_UNDEFINED;
1648
1649     if (num_title_screens != 0)
1650     {
1651       FadeSetEnterScreen();
1652
1653       /* use individual title fading instead of global "enter screen" fading */
1654       fading = getTitleFading(tci);
1655     }
1656
1657     if (game_status_last_screen == GAME_MODE_INFO)
1658     {
1659       if (num_title_screens == 0)
1660       {
1661         /* switch game mode from title screen mode back to info screen mode */
1662         SetGameStatus(GAME_MODE_INFO);
1663
1664         /* store that last screen was info screen, not main menu screen */
1665         game_status_last_screen = GAME_MODE_INFO;
1666
1667         DrawInfoScreen_NotAvailable("Title screen information:",
1668                                     "No title screen for this level set.");
1669         return;
1670       }
1671
1672       FadeMenuSoundsAndMusic();
1673     }
1674
1675     FadeOut(REDRAW_ALL);
1676
1677     /* title screens may have different window size */
1678     ChangeViewportPropertiesIfNeeded();
1679
1680     /* only required to update logic for redrawing global border */
1681     ClearField();
1682
1683     if (tci->is_image)
1684       DrawTitleScreenImage(tci->local_nr, tci->initial);
1685     else
1686       DrawTitleScreenMessage(tci->local_nr, tci->initial);
1687
1688     sound = getTitleSound(tci);
1689     music = getTitleMusic(tci);
1690
1691     if (sound != last_sound)
1692       PlayMenuSoundExt(sound);
1693     if (music != last_music)
1694       PlayMenuMusicExt(music);
1695
1696     last_sound = sound;
1697     last_music = music;
1698
1699     SetMouseCursor(CURSOR_NONE);
1700
1701     FadeIn(REDRAW_ALL);
1702
1703     DelayReached(&title_delay, 0);      /* reset delay counter */
1704
1705     return;
1706   }
1707
1708   if (fading.auto_delay > 0 && DelayReached(&title_delay, fading.auto_delay))
1709     button = MB_MENU_CHOICE;
1710
1711   if (button == MB_MENU_LEAVE)
1712   {
1713     return_to_main_menu = TRUE;
1714   }
1715   else if (button == MB_MENU_CHOICE)
1716   {
1717     if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0)
1718     {
1719       SetGameStatus(GAME_MODE_INFO);
1720
1721       info_mode = INFO_MODE_MAIN;
1722
1723       DrawInfoScreen();
1724
1725       return;
1726     }
1727
1728     title_screen_nr++;
1729
1730     if (title_screen_nr < num_title_screens)
1731     {
1732       tci = &title_controls[title_screen_nr];
1733
1734       SetAnimStatus(getTitleAnimMode(tci));
1735
1736       sound = getTitleSound(tci);
1737       music = getTitleMusic(tci);
1738
1739       if (last_sound != SND_UNDEFINED && sound != last_sound)
1740         FadeSound(last_sound);
1741       if (last_music != MUS_UNDEFINED && music != last_music)
1742         FadeMusic();
1743
1744       fading = getTitleFading(tci);
1745
1746       FadeOut(REDRAW_ALL);
1747
1748       if (tci->is_image)
1749         DrawTitleScreenImage(tci->local_nr, tci->initial);
1750       else
1751         DrawTitleScreenMessage(tci->local_nr, tci->initial);
1752
1753       sound = getTitleSound(tci);
1754       music = getTitleMusic(tci);
1755
1756       if (sound != last_sound)
1757         PlayMenuSoundExt(sound);
1758       if (music != last_music)
1759         PlayMenuMusicExt(music);
1760
1761       last_sound = sound;
1762       last_music = music;
1763
1764       FadeIn(REDRAW_ALL);
1765
1766       DelayReached(&title_delay, 0);    /* reset delay counter */
1767     }
1768     else
1769     {
1770       FadeMenuSoundsAndMusic();
1771
1772       return_to_main_menu = TRUE;
1773     }
1774   }
1775
1776   if (return_to_main_menu)
1777   {
1778     SetMouseCursor(CURSOR_DEFAULT);
1779
1780     /* force full menu screen redraw after displaying title screens */
1781     redraw_mask = REDRAW_ALL;
1782
1783     if (game_status_last_screen == GAME_MODE_INFO)
1784     {
1785       SetGameStatus(GAME_MODE_INFO);
1786
1787       info_mode = INFO_MODE_MAIN;
1788
1789       DrawInfoScreen();
1790     }
1791     else        /* default: return to main menu */
1792     {
1793       SetGameStatus(GAME_MODE_MAIN);
1794
1795       DrawMainMenu();
1796     }
1797   }
1798 }
1799
1800 void HandleMainMenu_SelectLevel(int step, int direction, int selected_level_nr)
1801 {
1802   int old_level_nr = level_nr;
1803   int new_level_nr;
1804
1805   if (selected_level_nr != NO_DIRECT_LEVEL_SELECT)
1806     new_level_nr = selected_level_nr;
1807   else
1808     new_level_nr = old_level_nr + step * direction;
1809
1810   if (new_level_nr < leveldir_current->first_level)
1811     new_level_nr = leveldir_current->first_level;
1812   if (new_level_nr > leveldir_current->last_level)
1813     new_level_nr = leveldir_current->last_level;
1814
1815   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
1816   {
1817     /* skipping levels is only allowed when trying to skip single level */
1818     if (setup.skip_levels && new_level_nr == old_level_nr + 1 &&
1819         Request("Level still unsolved! Skip despite handicap?", REQ_ASK))
1820     {
1821       leveldir_current->handicap_level++;
1822       SaveLevelSetup_SeriesInfo();
1823     }
1824
1825     new_level_nr = leveldir_current->handicap_level;
1826   }
1827
1828   if (new_level_nr != old_level_nr)
1829   {
1830     struct MainControlInfo *mci= getMainControlInfo(MAIN_CONTROL_LEVEL_NUMBER);
1831
1832     PlaySound(SND_MENU_ITEM_SELECTING);
1833
1834     level_nr = new_level_nr;
1835
1836     DrawText(mSX + mci->pos_text->x, mSY + mci->pos_text->y,
1837              int2str(level_nr, menu.main.text.level_number.size),
1838              mci->pos_text->font);
1839
1840     LoadLevel(level_nr);
1841     DrawPreviewLevelInitial();
1842
1843     TapeErase();
1844     LoadTape(level_nr);
1845     DrawCompleteVideoDisplay();
1846
1847     SaveLevelSetup_SeriesInfo();
1848
1849     /* needed because DrawPreviewLevelInitial() takes some time */
1850     BackToFront();
1851     /* SyncDisplay(); */
1852   }
1853 }
1854
1855 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
1856 {
1857   static int choice = MAIN_CONTROL_GAME;
1858   static boolean button_pressed_last = FALSE;
1859   boolean button_pressed = FALSE;
1860   int pos = choice;
1861   int i;
1862
1863   if (button == MB_MENU_INITIALIZE)
1864   {
1865     DrawCursorAndText_Main(choice, TRUE, FALSE);
1866
1867     return;
1868   }
1869
1870   if (mx || my)         /* mouse input */
1871   {
1872     pos = -1;
1873
1874     for (i = 0; main_controls[i].nr != -1; i++)
1875     {
1876       if (insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY) ||
1877           insideTextPosRect(main_controls[i].pos_text,   mx - mSX, my - mSY) ||
1878           insideTextPosRect(main_controls[i].pos_input,  mx - mSX, my - mSY))
1879       {
1880         pos = main_controls[i].nr;
1881
1882         break;
1883       }
1884     }
1885
1886     /* check if level preview was clicked */
1887     if (insidePreviewRect(&preview, mx - SX, my - SY))
1888       pos = MAIN_CONTROL_GAME;
1889
1890     // handle pressed/unpressed state for active/inactive menu buttons
1891     // (if pos != -1, "i" contains index position corresponding to "pos")
1892     if (button &&
1893         pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT &&
1894         insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY))
1895       button_pressed = TRUE;
1896
1897     if (button_pressed != button_pressed_last)
1898     {
1899       DrawCursorAndText_Main(choice, TRUE, button_pressed);
1900
1901       if (button_pressed)
1902         PlaySound(SND_MENU_BUTTON_PRESSING);
1903       else
1904         PlaySound(SND_MENU_BUTTON_RELEASING);
1905     }
1906   }
1907   else if (dx || dy)    /* keyboard input */
1908   {
1909     if (dx > 0 && (choice == MAIN_CONTROL_INFO ||
1910                    choice == MAIN_CONTROL_SETUP))
1911       button = MB_MENU_CHOICE;
1912     else if (dy)
1913       pos = choice + dy;
1914   }
1915
1916   if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
1917   {
1918     HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
1919   }
1920   else if (pos == MAIN_CONTROL_LAST_LEVEL && !button)
1921   {
1922     HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT);
1923   }
1924   else if (pos == MAIN_CONTROL_LEVEL_NUMBER && !button)
1925   {
1926     CloseDoor(DOOR_CLOSE_2);
1927
1928     SetGameStatus(GAME_MODE_LEVELNR);
1929
1930     DrawChooseLevelNr();
1931   }
1932   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
1933   {
1934     if (button)
1935     {
1936       if (pos != choice)
1937       {
1938         PlaySound(SND_MENU_ITEM_ACTIVATING);
1939
1940         DrawCursorAndText_Main(choice, FALSE, FALSE);
1941         DrawCursorAndText_Main(pos, TRUE, button_pressed);
1942
1943         choice = pos;
1944       }
1945       else if (dx != 0)
1946       {
1947         if (choice != MAIN_CONTROL_INFO &&
1948             choice != MAIN_CONTROL_SETUP)
1949           HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
1950       }
1951     }
1952     else
1953     {
1954       PlaySound(SND_MENU_ITEM_SELECTING);
1955
1956       if (pos == MAIN_CONTROL_NAME)
1957       {
1958         SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
1959
1960         HandleTypeName(strlen(setup.player_name), 0);
1961       }
1962       else if (pos == MAIN_CONTROL_LEVELS)
1963       {
1964         if (leveldir_first)
1965         {
1966           CloseDoor(DOOR_CLOSE_2);
1967
1968           SetGameStatus(GAME_MODE_LEVELS);
1969
1970           SaveLevelSetup_LastSeries();
1971           SaveLevelSetup_SeriesInfo();
1972
1973           if (setup.internal.choose_from_top_leveldir)
1974             gotoTopLevelDir();
1975
1976           DrawChooseLevelSet();
1977         }
1978       }
1979       else if (pos == MAIN_CONTROL_SCORES)
1980       {
1981         CloseDoor(DOOR_CLOSE_2);
1982
1983         SetGameStatus(GAME_MODE_SCORES);
1984
1985         DrawHallOfFame(-1);
1986       }
1987       else if (pos == MAIN_CONTROL_EDITOR)
1988       {
1989         if (leveldir_current->readonly &&
1990             !strEqual(setup.player_name, "Artsoft"))
1991           Request("This level is read only!", REQ_CONFIRM);
1992
1993         CloseDoor(DOOR_CLOSE_2);
1994
1995         SetGameStatus(GAME_MODE_EDITOR);
1996
1997         FadeSetEnterScreen();
1998
1999         DrawLevelEd();
2000       }
2001       else if (pos == MAIN_CONTROL_INFO)
2002       {
2003         CloseDoor(DOOR_CLOSE_2);
2004
2005         SetGameStatus(GAME_MODE_INFO);
2006
2007         info_mode = INFO_MODE_MAIN;
2008
2009         DrawInfoScreen();
2010       }
2011       else if (pos == MAIN_CONTROL_GAME)
2012       {
2013         StartGameActions(options.network, setup.autorecord, level.random_seed);
2014       }
2015       else if (pos == MAIN_CONTROL_SETUP)
2016       {
2017         CloseDoor(DOOR_CLOSE_2);
2018
2019         SetGameStatus(GAME_MODE_SETUP);
2020
2021         setup_mode = SETUP_MODE_MAIN;
2022
2023         DrawSetupScreen();
2024       }
2025       else if (pos == MAIN_CONTROL_QUIT)
2026       {
2027         SaveLevelSetup_LastSeries();
2028         SaveLevelSetup_SeriesInfo();
2029
2030         if (Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED))
2031           SetGameStatus(GAME_MODE_QUIT);
2032       }
2033     }
2034   }
2035
2036   button_pressed_last = button_pressed;
2037 }
2038
2039
2040 /* ========================================================================= */
2041 /* info screen functions                                                     */
2042 /* ========================================================================= */
2043
2044 static struct TokenInfo *info_info;
2045 static int num_info_info;       /* number of info entries shown on screen */
2046 static int max_info_info;       /* total number of info entries in list */
2047
2048 static void execInfoTitleScreen()
2049 {
2050   info_mode = INFO_MODE_TITLE;
2051
2052   DrawInfoScreen();
2053 }
2054
2055 static void execInfoElements()
2056 {
2057   info_mode = INFO_MODE_ELEMENTS;
2058
2059   DrawInfoScreen();
2060 }
2061
2062 static void execInfoMusic()
2063 {
2064   info_mode = INFO_MODE_MUSIC;
2065
2066   DrawInfoScreen();
2067 }
2068
2069 static void execInfoCredits()
2070 {
2071   info_mode = INFO_MODE_CREDITS;
2072
2073   DrawInfoScreen();
2074 }
2075
2076 static void execInfoProgram()
2077 {
2078   info_mode = INFO_MODE_PROGRAM;
2079
2080   DrawInfoScreen();
2081 }
2082
2083 static void execInfoVersion()
2084 {
2085   info_mode = INFO_MODE_VERSION;
2086
2087   DrawInfoScreen();
2088 }
2089
2090 static void execInfoLevelSet()
2091 {
2092   info_mode = INFO_MODE_LEVELSET;
2093
2094   DrawInfoScreen();
2095 }
2096
2097 static void execExitInfo()
2098 {
2099   SetGameStatus(GAME_MODE_MAIN);
2100
2101   DrawMainMenu();
2102 }
2103
2104 static struct TokenInfo info_info_main[] =
2105 {
2106   { TYPE_ENTER_SCREEN,  execInfoTitleScreen,    "Title Screen"          },
2107   { TYPE_ENTER_SCREEN,  execInfoElements,       "Elements Info"         },
2108   { TYPE_ENTER_SCREEN,  execInfoMusic,          "Music Info"            },
2109   { TYPE_ENTER_SCREEN,  execInfoCredits,        "Credits"               },
2110   { TYPE_ENTER_SCREEN,  execInfoProgram,        "Program Info"          },
2111   { TYPE_ENTER_SCREEN,  execInfoVersion,        "Version Info"          },
2112   { TYPE_ENTER_SCREEN,  execInfoLevelSet,       "Level Set Info"        },
2113   { TYPE_EMPTY,         NULL,                   ""                      },
2114   { TYPE_LEAVE_MENU,    execExitInfo,           "Exit"                  },
2115
2116   { 0,                  NULL,                   NULL                    }
2117 };
2118
2119 static int getMenuTextFont(int type)
2120 {
2121   if (type & (TYPE_SWITCH       |
2122               TYPE_YES_NO       |
2123               TYPE_YES_NO_AUTO  |
2124               TYPE_STRING       |
2125               TYPE_ECS_AGA      |
2126               TYPE_KEYTEXT      |
2127               TYPE_ENTER_LIST))
2128     return FONT_MENU_2;
2129   else
2130     return FONT_MENU_1;
2131 }
2132
2133 static struct TokenInfo *setup_info;
2134 static struct TokenInfo setup_info_input[];
2135
2136 static struct TokenInfo *menu_info;
2137
2138 static void DrawCursorAndText_Menu_Ext(struct TokenInfo *token_info,
2139                                        int screen_pos, int menu_info_pos_raw,
2140                                        boolean active)
2141 {
2142   int pos = (menu_info_pos_raw < 0 ? screen_pos : menu_info_pos_raw);
2143   struct TokenInfo *ti = &token_info[pos];
2144   int xpos = MENU_SCREEN_START_XPOS;
2145   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
2146   int font_nr = getMenuTextFont(ti->type);
2147
2148   if (setup_mode == SETUP_MODE_INPUT)
2149     font_nr = FONT_MENU_1;
2150
2151   if (active)
2152     font_nr = FONT_ACTIVE(font_nr);
2153
2154   DrawText(mSX + xpos * 32, mSY + ypos * 32, ti->text, font_nr);
2155
2156   if (ti->type & ~TYPE_SKIP_ENTRY)
2157     drawCursor(screen_pos, active);
2158 }
2159
2160 static void DrawCursorAndText_Menu(int screen_pos, int menu_info_pos_raw,
2161                                    boolean active)
2162 {
2163   DrawCursorAndText_Menu_Ext(menu_info, screen_pos, menu_info_pos_raw, active);
2164 }
2165
2166 static void DrawCursorAndText_Setup(int screen_pos, int menu_info_pos_raw,
2167                                     boolean active)
2168 {
2169   DrawCursorAndText_Menu_Ext(setup_info, screen_pos, menu_info_pos_raw, active);
2170 }
2171
2172 static char *window_size_text;
2173 static char *scaling_type_text;
2174
2175 static void drawSetupValue(int, int);
2176
2177 static void drawMenuInfoList(int first_entry, int num_page_entries,
2178                              int max_page_entries)
2179 {
2180   int i;
2181
2182   if (first_entry + num_page_entries > max_page_entries)
2183     first_entry = 0;
2184
2185   clearMenuListArea();
2186
2187   for (i = 0; i < num_page_entries; i++)
2188   {
2189     int menu_info_pos = first_entry + i;
2190     struct TokenInfo *si = &menu_info[menu_info_pos];
2191     void *value_ptr = si->value;
2192
2193     /* set some entries to "unchangeable" according to other variables */
2194     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
2195         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
2196         (value_ptr == &setup.sound_music  && !audio.music_available) ||
2197         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
2198         (value_ptr == &window_size_text   && !video.window_scaling_available) ||
2199         (value_ptr == &scaling_type_text  && !video.window_scaling_available))
2200       si->type |= TYPE_GHOSTED;
2201
2202     if (si->type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
2203       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2204     else if (si->type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
2205       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2206     else if (si->type & ~TYPE_SKIP_ENTRY)
2207       initCursor(i, IMG_MENU_BUTTON);
2208
2209     DrawCursorAndText_Menu(i, menu_info_pos, FALSE);
2210
2211     if (si->type & TYPE_VALUE &&
2212         menu_info == setup_info)
2213       drawSetupValue(i, menu_info_pos);
2214   }
2215 }
2216
2217 static void DrawInfoScreen_Main()
2218 {
2219   int fade_mask = REDRAW_FIELD;
2220   int i;
2221
2222   if (redraw_mask & REDRAW_ALL)
2223     fade_mask = REDRAW_ALL;
2224
2225   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
2226     fade_mask = REDRAW_ALL;
2227
2228   UnmapAllGadgets();
2229   FadeMenuSoundsAndMusic();
2230
2231   FreeScreenGadgets();
2232   CreateScreenGadgets();
2233
2234   /* (needed after displaying title screens which disable auto repeat) */
2235   KeyboardAutoRepeatOn();
2236
2237   FadeSetLeaveScreen();
2238
2239   FadeOut(fade_mask);
2240
2241   /* needed if different viewport properties defined for info screen */
2242   ChangeViewportPropertiesIfNeeded();
2243
2244   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
2245
2246   ClearField();
2247
2248   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
2249
2250   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Info Screen");
2251
2252   info_info = info_info_main;
2253
2254   // determine maximal number of info entries that can be displayed on screen
2255   num_info_info = 0;
2256   for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2257     num_info_info++;
2258
2259   // determine maximal number of info entries available for menu of info screen
2260   max_info_info = 0;
2261   for (i = 0; info_info[i].type != 0; i++)
2262     max_info_info++;
2263
2264   HandleInfoScreen_Main(0, 0, 0, 0, MB_MENU_INITIALIZE);
2265
2266   MapScreenGadgets(max_info_info);
2267
2268   PlayMenuSoundsAndMusic();
2269
2270   DrawMaskedBorder(fade_mask);
2271
2272   FadeIn(fade_mask);
2273 }
2274
2275 static void changeSetupValue(int, int, int);
2276
2277 void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
2278                       int mode, int num_page_entries, int max_page_entries)
2279 {
2280   static int num_page_entries_all_last[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2281   static int choice_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2282   static int first_entry_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2283   int *num_page_entries_last = num_page_entries_all_last[game_status];
2284   int *choice_store = choice_stores[game_status];
2285   int *first_entry_store = first_entry_stores[game_status];
2286   int choice = choice_store[mode];              /* starts with 0 */
2287   int first_entry = first_entry_store[mode];    /* starts with 0 */
2288   int x = 0;
2289   int y = choice - first_entry;
2290   int y_old = y;
2291   boolean position_set_by_scrollbar = (dx == 999);
2292   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2293   int i;
2294
2295   if (button == MB_MENU_INITIALIZE)
2296   {
2297     // check if number of menu page entries has changed (may happen by change
2298     // of custom artwork definition value for 'list_size' for this menu screen)
2299     // (in this case, the last menu position most probably has to be corrected)
2300     if (num_page_entries != num_page_entries_last[mode])
2301     {
2302       choice_store[mode] = first_entry_store[mode] = 0;
2303
2304       choice = first_entry = 0;
2305       y = y_old = 0;
2306
2307       num_page_entries_last[mode] = num_page_entries;
2308     }
2309
2310     /* advance to first valid menu entry */
2311     while (choice < num_page_entries &&
2312            menu_info[choice].type & TYPE_SKIP_ENTRY)
2313       choice++;
2314
2315     if (position_set_by_scrollbar)
2316       first_entry = first_entry_store[mode] = dy;
2317     else
2318       AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2319                       NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2320
2321     drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2322
2323     if (choice < first_entry)
2324     {
2325       choice = first_entry;
2326
2327       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2328         choice++;
2329     }
2330     else if (choice > first_entry + num_page_entries - 1)
2331     {
2332       choice = first_entry + num_page_entries - 1;
2333
2334       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2335         choice--;
2336     }
2337
2338     choice_store[mode] = choice;
2339
2340     DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2341
2342     return;
2343   }
2344   else if (button == MB_MENU_LEAVE)
2345   {
2346     PlaySound(SND_MENU_ITEM_SELECTING);
2347
2348     for (i = 0; i < max_page_entries; i++)
2349     {
2350       if (menu_info[i].type & TYPE_LEAVE_MENU)
2351       {
2352         void (*menu_callback_function)(void) = menu_info[i].value;
2353
2354         FadeSetLeaveMenu();
2355
2356         menu_callback_function();
2357
2358         break;  /* absolutely needed because function changes 'menu_info'! */
2359       }
2360     }
2361
2362     return;
2363   }
2364
2365   if (mx || my)         /* mouse input */
2366   {
2367     x = (mx - mSX) / 32;
2368     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2369   }
2370   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
2371   {
2372     /* move cursor instead of scrolling when already at start/end of list */
2373     if (dy == -1 * SCROLL_LINE && first_entry == 0)
2374       dy = -1;
2375     else if (dy == +1 * SCROLL_LINE &&
2376              first_entry + num_page_entries == max_page_entries)
2377       dy = 1;
2378
2379     /* handle scrolling screen one line or page */
2380     if (y + dy < 0 ||
2381         y + dy > num_page_entries - 1)
2382     {
2383       boolean redraw = FALSE;
2384
2385       if (ABS(dy) == SCROLL_PAGE)
2386         step = num_page_entries - 1;
2387
2388       if (dy < 0 && first_entry > 0)
2389       {
2390         /* scroll page/line up */
2391
2392         first_entry -= step;
2393         if (first_entry < 0)
2394           first_entry = 0;
2395
2396         redraw = TRUE;
2397       }
2398       else if (dy > 0 && first_entry + num_page_entries < max_page_entries)
2399       {
2400         /* scroll page/line down */
2401
2402         first_entry += step;
2403         if (first_entry + num_page_entries > max_page_entries)
2404           first_entry = MAX(0, max_page_entries - num_page_entries);
2405
2406         redraw = TRUE;
2407       }
2408
2409       if (redraw)
2410       {
2411         choice += first_entry - first_entry_store[mode];
2412
2413         if (choice < first_entry)
2414         {
2415           choice = first_entry;
2416
2417           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2418             choice++;
2419         }
2420         else if (choice > first_entry + num_page_entries - 1)
2421         {
2422           choice = first_entry + num_page_entries - 1;
2423
2424           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2425             choice--;
2426         }
2427         else if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2428         {
2429           choice += SIGN(dy);
2430
2431           if (choice < first_entry ||
2432               choice > first_entry + num_page_entries - 1)
2433           first_entry += SIGN(dy);
2434         }
2435
2436         first_entry_store[mode] = first_entry;
2437         choice_store[mode] = choice;
2438
2439         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2440
2441         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2442
2443         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2444                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2445       }
2446
2447       return;
2448     }
2449
2450     if (dx)
2451     {
2452       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
2453
2454       if (menu_info[choice].type & menu_navigation_type ||
2455           menu_info[choice].type & TYPE_BOOLEAN_STYLE ||
2456           menu_info[choice].type & TYPE_YES_NO_AUTO)
2457         button = MB_MENU_CHOICE;
2458     }
2459     else if (dy)
2460       y += dy;
2461
2462     /* jump to next non-empty menu entry (up or down) */
2463     while (first_entry + y > 0 &&
2464            first_entry + y < max_page_entries - 1 &&
2465            menu_info[first_entry + y].type & TYPE_SKIP_ENTRY)
2466       y += dy;
2467
2468     if (!IN_VIS_MENU(x, y))
2469     {
2470       choice += y - y_old;
2471
2472       if (choice < first_entry)
2473         first_entry = choice;
2474       else if (choice > first_entry + num_page_entries - 1)
2475         first_entry = choice - num_page_entries + 1;
2476
2477       if (first_entry >= 0 &&
2478           first_entry + num_page_entries <= max_page_entries)
2479       {
2480         first_entry_store[mode] = first_entry;
2481
2482         if (choice < first_entry)
2483           choice = first_entry;
2484         else if (choice > first_entry + num_page_entries - 1)
2485           choice = first_entry + num_page_entries - 1;
2486
2487         choice_store[mode] = choice;
2488
2489         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2490
2491         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2492
2493         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2494                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2495       }
2496
2497       return;
2498     }
2499   }
2500
2501   if (!anyScrollbarGadgetActive() &&
2502       IN_VIS_MENU(x, y) &&
2503       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
2504       y >= 0 && y < num_page_entries)
2505   {
2506     if (button)
2507     {
2508       if (first_entry + y != choice &&
2509           menu_info[first_entry + y].type & ~TYPE_SKIP_ENTRY)
2510       {
2511         PlaySound(SND_MENU_ITEM_ACTIVATING);
2512
2513         DrawCursorAndText_Menu(choice - first_entry, choice, FALSE);
2514         DrawCursorAndText_Menu(y, first_entry + y, TRUE);
2515
2516         choice = choice_store[mode] = first_entry + y;
2517       }
2518       else if (dx < 0)
2519       {
2520         PlaySound(SND_MENU_ITEM_SELECTING);
2521
2522         for (i = 0; menu_info[i].type != 0; i++)
2523         {
2524           if (menu_info[i].type & TYPE_LEAVE_MENU)
2525           {
2526             void (*menu_callback_function)(void) = menu_info[i].value;
2527
2528             FadeSetLeaveMenu();
2529
2530             menu_callback_function();
2531
2532             /* absolutely needed because function changes 'menu_info'! */
2533             break;
2534           }
2535         }
2536
2537         return;
2538       }
2539     }
2540     else if (!(menu_info[first_entry + y].type & TYPE_GHOSTED))
2541     {
2542       PlaySound(SND_MENU_ITEM_SELECTING);
2543
2544       /* when selecting key headline, execute function for key value change */
2545       if (menu_info[first_entry + y].type & TYPE_KEYTEXT &&
2546           menu_info[first_entry + y + 1].type & TYPE_KEY)
2547         y++;
2548
2549       /* when selecting string value, execute function for list selection */
2550       if (menu_info[first_entry + y].type & TYPE_STRING && y > 0 &&
2551           menu_info[first_entry + y - 1].type & TYPE_ENTER_LIST)
2552         y--;
2553
2554       if (menu_info[first_entry + y].type & TYPE_ENTER_OR_LEAVE)
2555       {
2556         void (*menu_callback_function)(void) =
2557           menu_info[first_entry + y].value;
2558
2559         FadeSetFromType(menu_info[first_entry + y].type);
2560
2561         menu_callback_function();
2562       }
2563       else if (menu_info[first_entry + y].type & TYPE_VALUE &&
2564                menu_info == setup_info)
2565       {
2566         changeSetupValue(y, first_entry + y, dx);
2567       }
2568     }
2569   }
2570 }
2571
2572 void HandleInfoScreen_Main(int mx, int my, int dx, int dy, int button)
2573 {
2574   menu_info = info_info;
2575
2576   HandleMenuScreen(mx, my, dx, dy, button,
2577                    info_mode, num_info_info, max_info_info);
2578 }
2579
2580 static int getMenuFontSpacing(int spacing_height, int font_nr)
2581 {
2582   int font_spacing = getFontHeight(font_nr) + EXTRA_SPACING(game_status);
2583
2584   return (spacing_height < 0 ? ABS(spacing_height) * font_spacing :
2585           spacing_height);
2586 }
2587
2588 static int getMenuTextSpacing(int spacing_height, int font_nr)
2589 {
2590   return (getMenuFontSpacing(spacing_height, font_nr) +
2591           EXTRA_SPACING(game_status));
2592 }
2593
2594 static int getMenuTextStep(int spacing_height, int font_nr)
2595 {
2596   return getFontHeight(font_nr) + getMenuTextSpacing(spacing_height, font_nr);
2597 }
2598
2599 void DrawInfoScreen_NotAvailable(char *text_title, char *text_error)
2600 {
2601   int font_title = MENU_INFO_FONT_TITLE;
2602   int font_error = FONT_TEXT_2;
2603   int font_foot  = MENU_INFO_FONT_FOOT;
2604   int spacing_title = menu.headline1_spacing_info[info_mode];
2605   int ystep_title = getMenuTextStep(spacing_title, font_title);
2606   int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1;
2607   int ystart2 = ystart1 + ystep_title;
2608   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
2609
2610   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO);
2611
2612   FadeOut(REDRAW_FIELD);
2613
2614   ClearField();
2615   DrawHeadline();
2616
2617   DrawTextSCentered(ystart1, font_title, text_title);
2618   DrawTextSCentered(ystart2, font_error, text_error);
2619
2620   DrawTextSCentered(ybottom, font_foot,
2621                     "Press any key or button for info menu");
2622
2623   FadeIn(REDRAW_FIELD);
2624 }
2625
2626 void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init)
2627 {
2628   static int infoscreen_step[MAX_INFO_ELEMENTS_ON_SCREEN];
2629   static int infoscreen_frame[MAX_INFO_ELEMENTS_ON_SCREEN];
2630   int font_title = MENU_INFO_FONT_TITLE;
2631   int font_foot  = MENU_INFO_FONT_FOOT;
2632   int xstart  = mSX + MENU_SCREEN_INFO_SPACE_LEFT;
2633   int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1;
2634   int ystart2 = mSY + MENU_SCREEN_INFO_YSTART2;
2635   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
2636   int ystep = MENU_SCREEN_INFO_YSTEP;
2637   int element, action, direction;
2638   int graphic;
2639   int delay;
2640   int sync_frame;
2641   int i, j;
2642
2643   if (init)
2644   {
2645     for (i = 0; i < NUM_INFO_ELEMENTS_ON_SCREEN; i++)
2646       infoscreen_step[i] = infoscreen_frame[i] = 0;
2647
2648     ClearField();
2649     DrawHeadline();
2650
2651     DrawTextSCentered(ystart1, font_title, "The Game Elements:");
2652
2653     DrawTextSCentered(ybottom, font_foot,
2654                       "Press any key or button for next page");
2655
2656     FrameCounter = 0;
2657   }
2658
2659   i = j = 0;
2660   while (helpanim_info[j].element != HELPANIM_LIST_END)
2661   {
2662     if (i >= start + NUM_INFO_ELEMENTS_ON_SCREEN ||
2663         i >= max_anims)
2664       break;
2665     else if (i < start)
2666     {
2667       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2668         j++;
2669
2670       j++;
2671       i++;
2672
2673       continue;
2674     }
2675
2676     j += infoscreen_step[i - start];
2677
2678     element = helpanim_info[j].element;
2679     action = helpanim_info[j].action;
2680     direction = helpanim_info[j].direction;
2681
2682     if (element < 0)
2683       element = EL_UNKNOWN;
2684
2685     if (action != -1 && direction != -1)
2686       graphic = el_act_dir2img(element, action, direction);
2687     else if (action != -1)
2688       graphic = el_act2img(element, action);
2689     else if (direction != -1)
2690       graphic = el_dir2img(element, direction);
2691     else
2692       graphic = el2img(element);
2693
2694     delay = helpanim_info[j++].delay;
2695
2696     if (delay == -1)
2697       delay = 1000000;
2698
2699     if (infoscreen_frame[i - start] == 0)
2700     {
2701       sync_frame = 0;
2702       infoscreen_frame[i - start] = delay - 1;
2703     }
2704     else
2705     {
2706       sync_frame = delay - infoscreen_frame[i - start];
2707       infoscreen_frame[i - start]--;
2708     }
2709
2710     if (helpanim_info[j].element == HELPANIM_LIST_NEXT)
2711     {
2712       if (!infoscreen_frame[i - start])
2713         infoscreen_step[i - start] = 0;
2714     }
2715     else
2716     {
2717       if (!infoscreen_frame[i - start])
2718         infoscreen_step[i - start]++;
2719       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2720         j++;
2721     }
2722
2723     j++;
2724
2725     ClearRectangleOnBackground(drawto, xstart, ystart2 + (i - start) * ystep,
2726                                TILEX, TILEY);
2727     DrawFixedGraphicAnimationExt(drawto, xstart, ystart2 + (i - start) * ystep,
2728                                  graphic, sync_frame, USE_MASKING);
2729
2730     if (init)
2731       DrawInfoScreen_HelpText(element, action, direction, i - start);
2732
2733     i++;
2734   }
2735
2736   redraw_mask |= REDRAW_FIELD;
2737
2738   FrameCounter++;
2739 }
2740
2741 static char *getHelpText(int element, int action, int direction)
2742 {
2743   char token[MAX_LINE_LEN];
2744
2745   strcpy(token, element_info[element].token_name);
2746
2747   if (action != -1)
2748     strcat(token, element_action_info[action].suffix);
2749
2750   if (direction != -1)
2751     strcat(token, element_direction_info[MV_DIR_TO_BIT(direction)].suffix);
2752
2753   return getHashEntry(helptext_info, token);
2754 }
2755
2756 void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
2757 {
2758   int font_nr = FONT_INFO_ELEMENTS;
2759   int font_width = getFontWidth(font_nr);
2760   int font_height = getFontHeight(font_nr);
2761   int yoffset = (TILEX - 2 * font_height) / 2;
2762   int xstart = mSX + MENU_SCREEN_INFO_SPACE_LEFT + TILEX + MINI_TILEX;
2763   int ystart = mSY + MENU_SCREEN_INFO_YSTART2 + yoffset;
2764   int ystep = TILEY + 4;
2765   int pad_left = xstart - SX;
2766   int pad_right = MENU_SCREEN_INFO_SPACE_RIGHT;
2767   int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width;
2768   int max_lines_per_text = 2;    
2769   char *text = NULL;
2770
2771   if (action != -1 && direction != -1)          /* element.action.direction */
2772     text = getHelpText(element, action, direction);
2773
2774   if (text == NULL && action != -1)             /* element.action */
2775     text = getHelpText(element, action, -1);
2776
2777   if (text == NULL && direction != -1)          /* element.direction */
2778     text = getHelpText(element, -1, direction);
2779
2780   if (text == NULL)                             /* base element */
2781     text = getHelpText(element, -1, -1);
2782
2783   if (text == NULL)                             /* not found */
2784     text = "No description available";
2785
2786   if (strlen(text) <= max_chars_per_line)       /* only one line of text */
2787     ystart += getFontHeight(font_nr) / 2;
2788
2789   DrawTextBuffer(xstart, ystart + ypos * ystep, text, font_nr,
2790                  max_chars_per_line, -1, max_lines_per_text, 0, -1,
2791                  TRUE, FALSE, FALSE);
2792 }
2793
2794 void DrawInfoScreen_TitleScreen()
2795 {
2796   SetGameStatus(GAME_MODE_TITLE);
2797
2798   DrawTitleScreen();
2799 }
2800
2801 void HandleInfoScreen_TitleScreen(int button)
2802 {
2803   HandleTitleScreen(0, 0, 0, 0, button);
2804 }
2805
2806 void DrawInfoScreen_Elements()
2807 {
2808   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_ELEMENTS);
2809
2810   FadeOut(REDRAW_FIELD);
2811
2812   LoadHelpAnimInfo();
2813   LoadHelpTextInfo();
2814
2815   HandleInfoScreen_Elements(MB_MENU_INITIALIZE);
2816
2817   FadeIn(REDRAW_FIELD);
2818 }
2819
2820 void HandleInfoScreen_Elements(int button)
2821 {
2822   static unsigned int info_delay = 0;
2823   static int num_anims;
2824   static int num_pages;
2825   static int page;
2826   int anims_per_page = NUM_INFO_ELEMENTS_ON_SCREEN;
2827   int i;
2828
2829   if (button == MB_MENU_INITIALIZE)
2830   {
2831     boolean new_element = TRUE;
2832
2833     num_anims = 0;
2834
2835     for (i = 0; helpanim_info[i].element != HELPANIM_LIST_END; i++)
2836     {
2837       if (helpanim_info[i].element == HELPANIM_LIST_NEXT)
2838         new_element = TRUE;
2839       else if (new_element)
2840       {
2841         num_anims++;
2842         new_element = FALSE;
2843       }
2844     }
2845
2846     num_pages = (num_anims + anims_per_page - 1) / anims_per_page;
2847     page = 0;
2848   }
2849
2850   if (button == MB_MENU_LEAVE)
2851   {
2852     PlaySound(SND_MENU_ITEM_SELECTING);
2853
2854     info_mode = INFO_MODE_MAIN;
2855     DrawInfoScreen();
2856
2857     return;
2858   }
2859   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
2860   {
2861     if (button != MB_MENU_INITIALIZE)
2862     {
2863       PlaySound(SND_MENU_ITEM_SELECTING);
2864
2865       page++;
2866     }
2867
2868     if (page >= num_pages)
2869     {
2870       FadeMenuSoundsAndMusic();
2871
2872       info_mode = INFO_MODE_MAIN;
2873       DrawInfoScreen();
2874
2875       return;
2876     }
2877
2878     if (page > 0)
2879       FadeSetNextScreen();
2880
2881     if (button != MB_MENU_INITIALIZE)
2882       FadeOut(REDRAW_FIELD);
2883
2884     DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, TRUE);
2885
2886     if (button != MB_MENU_INITIALIZE)
2887       FadeIn(REDRAW_FIELD);
2888   }
2889   else
2890   {
2891     if (DelayReached(&info_delay, GameFrameDelay))
2892       if (page < num_pages)
2893         DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, FALSE);
2894
2895     PlayMenuSoundIfLoop();
2896   }
2897 }
2898
2899 void DrawInfoScreen_Music()
2900 {
2901   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_MUSIC);
2902
2903   FadeOut(REDRAW_FIELD);
2904
2905   ClearField();
2906   DrawHeadline();
2907
2908   LoadMusicInfo();
2909
2910   HandleInfoScreen_Music(MB_MENU_INITIALIZE);
2911
2912   FadeIn(REDRAW_FIELD);
2913 }
2914
2915 void HandleInfoScreen_Music(int button)
2916 {
2917   static struct MusicFileInfo *list = NULL;
2918   int font_title = MENU_INFO_FONT_TITLE;
2919   int font_head  = MENU_INFO_FONT_HEAD;
2920   int font_text  = MENU_INFO_FONT_TEXT;
2921   int font_foot  = MENU_INFO_FONT_FOOT;
2922   int spacing_title = menu.headline1_spacing_info[info_mode];
2923   int spacing_head  = menu.headline2_spacing_info[info_mode];
2924   int ystep_title = getMenuTextStep(spacing_title, font_title);
2925   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
2926   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
2927   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
2928
2929   if (button == MB_MENU_INITIALIZE)
2930   {
2931     list = music_file_info;
2932
2933     if (list == NULL)
2934     {
2935       FadeMenuSoundsAndMusic();
2936
2937       ClearField();
2938       DrawHeadline();
2939
2940       DrawTextSCentered(ystart, font_title,
2941                         "No music info for this level set.");
2942
2943       DrawTextSCentered(ybottom, font_foot,
2944                         "Press any key or button for info menu");
2945
2946       return;
2947     }
2948   }
2949
2950   if (button == MB_MENU_LEAVE)
2951   {
2952     PlaySound(SND_MENU_ITEM_SELECTING);
2953
2954     FadeMenuSoundsAndMusic();
2955
2956     info_mode = INFO_MODE_MAIN;
2957     DrawInfoScreen();
2958
2959     return;
2960   }
2961   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
2962   {
2963     if (button != MB_MENU_INITIALIZE)
2964     {
2965       PlaySound(SND_MENU_ITEM_SELECTING);
2966
2967       if (list != NULL)
2968         list = list->next;
2969     }
2970
2971     if (list == NULL)
2972     {
2973       FadeMenuSoundsAndMusic();
2974
2975       info_mode = INFO_MODE_MAIN;
2976       DrawInfoScreen();
2977
2978       return;
2979     }
2980
2981     FadeMenuSoundsAndMusic();
2982
2983     if (list != music_file_info)
2984       FadeSetNextScreen();
2985
2986     if (button != MB_MENU_INITIALIZE)
2987       FadeOut(REDRAW_FIELD);
2988
2989     ClearField();
2990     DrawHeadline();
2991
2992     if (list->is_sound)
2993     {
2994       int sound = list->music;
2995
2996       if (sound_info[sound].loop)
2997         PlaySoundLoop(sound);
2998       else
2999         PlaySound(sound);
3000
3001       DrawTextSCentered(ystart, font_title, "The Game Background Sounds:");
3002     }
3003     else
3004     {
3005       PlayMusic(list->music);
3006
3007       DrawTextSCentered(ystart, font_title, "The Game Background Music:");
3008     }
3009
3010     ystart += ystep_title;
3011
3012     if (!strEqual(list->title, UNKNOWN_NAME))
3013     {
3014       if (!strEqual(list->title_header, UNKNOWN_NAME))
3015       {
3016         DrawTextSCentered(ystart, font_head, list->title_header);
3017         ystart += ystep_head;
3018       }
3019
3020       DrawTextFCentered(ystart, font_text, "\"%s\"", list->title);
3021       ystart += ystep_head;
3022     }
3023
3024     if (!strEqual(list->artist, UNKNOWN_NAME))
3025     {
3026       if (!strEqual(list->artist_header, UNKNOWN_NAME))
3027         DrawTextSCentered(ystart, font_head, list->artist_header);
3028       else
3029         DrawTextSCentered(ystart, font_head, "by");
3030
3031       ystart += ystep_head;
3032
3033       DrawTextFCentered(ystart, font_text, "%s", list->artist);
3034       ystart += ystep_head;
3035     }
3036
3037     if (!strEqual(list->album, UNKNOWN_NAME))
3038     {
3039       if (!strEqual(list->album_header, UNKNOWN_NAME))
3040         DrawTextSCentered(ystart, font_head, list->album_header);
3041       else
3042         DrawTextSCentered(ystart, font_head, "from the album");
3043
3044       ystart += ystep_head;
3045
3046       DrawTextFCentered(ystart, font_text, "\"%s\"", list->album);
3047       ystart += ystep_head;
3048     }
3049
3050     if (!strEqual(list->year, UNKNOWN_NAME))
3051     {
3052       if (!strEqual(list->year_header, UNKNOWN_NAME))
3053         DrawTextSCentered(ystart, font_head, list->year_header);
3054       else
3055         DrawTextSCentered(ystart, font_head, "from the year");
3056
3057       ystart += ystep_head;
3058
3059       DrawTextFCentered(ystart, font_text, "%s", list->year);
3060       ystart += ystep_head;
3061     }
3062
3063     DrawTextSCentered(ybottom, FONT_TEXT_4,
3064                       "Press any key or button for next page");
3065
3066     if (button != MB_MENU_INITIALIZE)
3067       FadeIn(REDRAW_FIELD);
3068   }
3069
3070   if (list != NULL && list->is_sound && sound_info[list->music].loop)
3071     PlaySoundLoop(list->music);
3072 }
3073
3074 static void DrawInfoScreen_CreditsScreen(int screen_nr)
3075 {
3076   int font_title = MENU_INFO_FONT_TITLE;
3077   int font_head  = MENU_INFO_FONT_HEAD;
3078   int font_text  = MENU_INFO_FONT_TEXT;
3079   int font_foot  = MENU_INFO_FONT_FOOT;
3080   int spacing_title = menu.headline1_spacing_info[info_mode];
3081   int spacing_head  = menu.headline2_spacing_info[info_mode];
3082   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3083   int spacing_line  = menu.line_spacing_info[info_mode];
3084   int ystep_title = getMenuTextStep(spacing_title, font_title);
3085   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3086   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3087   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3088   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3089   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3090
3091   ClearField();
3092   DrawHeadline();
3093
3094   DrawTextSCentered(ystart, font_title, "Credits:");
3095   ystart += ystep_title;
3096
3097   if (screen_nr == 0)
3098   {
3099     DrawTextSCentered(ystart, font_head,
3100                       "Special thanks to");
3101     ystart += ystep_head;
3102     DrawTextSCentered(ystart, font_text,
3103                       "Peter Liepa");
3104     ystart += ystep_head;
3105     DrawTextSCentered(ystart, font_head,
3106                       "for creating");
3107     ystart += ystep_head;
3108     DrawTextSCentered(ystart, font_text,
3109                       "\"Boulder Dash\"");
3110     ystart += ystep_head;
3111     DrawTextSCentered(ystart, font_head,
3112                       "in the year");
3113     ystart += ystep_head;
3114     DrawTextSCentered(ystart, font_text,
3115                       "1984");
3116     ystart += ystep_head;
3117     DrawTextSCentered(ystart, font_head,
3118                       "published by");
3119     ystart += ystep_head;
3120     DrawTextSCentered(ystart, font_text,
3121                       "First Star Software");
3122   }
3123   else if (screen_nr == 1)
3124   {
3125     DrawTextSCentered(ystart, font_head,
3126                       "Special thanks to");
3127     ystart += ystep_head;
3128     DrawTextSCentered(ystart, font_text,
3129                       "Klaus Heinz & Volker Wertich");
3130     ystart += ystep_head;
3131     DrawTextSCentered(ystart, font_head,
3132                       "for creating");
3133     ystart += ystep_head;
3134     DrawTextSCentered(ystart, font_text,
3135                       "\"Emerald Mine\"");
3136     ystart += ystep_head;
3137     DrawTextSCentered(ystart, font_head,
3138                       "in the year");
3139     ystart += ystep_head;
3140     DrawTextSCentered(ystart, font_text,
3141                       "1987");
3142     ystart += ystep_head;
3143     DrawTextSCentered(ystart, font_head,
3144                       "published by");
3145     ystart += ystep_head;
3146     DrawTextSCentered(ystart, font_text,
3147                       "Kingsoft");
3148   }
3149   else if (screen_nr == 2)
3150   {
3151     DrawTextSCentered(ystart, font_head,
3152                       "Special thanks to");
3153     ystart += ystep_head;
3154     DrawTextSCentered(ystart, font_text,
3155                       "Michael Stopp & Philip Jespersen");
3156     ystart += ystep_head;
3157     DrawTextSCentered(ystart, font_head,
3158                       "for creating");
3159     ystart += ystep_head;
3160     DrawTextSCentered(ystart, font_text,
3161                       "\"Supaplex\"");
3162     ystart += ystep_head;
3163     DrawTextSCentered(ystart, font_head,
3164                       "in the year");
3165     ystart += ystep_head;
3166     DrawTextSCentered(ystart, font_text,
3167                       "1991");
3168     ystart += ystep_head;
3169     DrawTextSCentered(ystart, font_head,
3170                       "published by");
3171     ystart += ystep_head;
3172     DrawTextSCentered(ystart, font_text,
3173                       "Digital Integration");
3174   }
3175   else if (screen_nr == 3)
3176   {
3177     DrawTextSCentered(ystart, font_head,
3178                       "Special thanks to");
3179     ystart += ystep_head;
3180     DrawTextSCentered(ystart, font_text,
3181                       "Hiroyuki Imabayashi");
3182     ystart += ystep_head;
3183     DrawTextSCentered(ystart, font_head,
3184                       "for creating");
3185     ystart += ystep_head;
3186     DrawTextSCentered(ystart, font_text,
3187                       "\"Sokoban\"");
3188     ystart += ystep_head;
3189     DrawTextSCentered(ystart, font_head,
3190                       "in the year");
3191     ystart += ystep_head;
3192     DrawTextSCentered(ystart, font_text,
3193                       "1982");
3194     ystart += ystep_head;
3195     DrawTextSCentered(ystart, font_head,
3196                       "published by");
3197     ystart += ystep_head;
3198     DrawTextSCentered(ystart, font_text,
3199                       "Thinking Rabbit");
3200   }
3201   else if (screen_nr == 4)
3202   {
3203     DrawTextSCentered(ystart, font_head,
3204                       "Special thanks to");
3205     ystart += ystep_head;
3206     DrawTextSCentered(ystart, font_text,
3207                       "Alan Bond");
3208     ystart += ystep_head;
3209     DrawTextSCentered(ystart, font_head,
3210                       "and");
3211     ystart += ystep_head;
3212     DrawTextSCentered(ystart, font_text,
3213                       "J\xfcrgen Bonhagen");
3214     ystart += ystep_head;
3215     DrawTextSCentered(ystart, font_head,
3216                       "for the continuous creation");
3217     ystart += ystep_line;
3218     DrawTextSCentered(ystart, font_head,
3219                       "of outstanding level sets");
3220   }
3221   else if (screen_nr == 5)
3222   {
3223     DrawTextSCentered(ystart, font_head,
3224                       "Thanks to");
3225     ystart += ystep_head;
3226     DrawTextSCentered(ystart, font_text,
3227                       "Peter Elzner");
3228     ystart += ystep_head;
3229     DrawTextSCentered(ystart, font_head,
3230                       "for ideas and inspiration by");
3231     ystart += ystep_head;
3232     DrawTextSCentered(ystart, font_text,
3233                       "Diamond Caves");
3234     ystart += ystep_para;
3235
3236     DrawTextSCentered(ystart, font_head,
3237                       "Thanks to");
3238     ystart += ystep_head;
3239     DrawTextSCentered(ystart, font_text,
3240                       "Steffest");
3241     ystart += ystep_head;
3242     DrawTextSCentered(ystart, font_head,
3243                       "for ideas and inspiration by");
3244     ystart += ystep_head;
3245     DrawTextSCentered(ystart, font_text,
3246                       "DX-Boulderdash");
3247   }
3248   else if (screen_nr == 6)
3249   {
3250     DrawTextSCentered(ystart, font_head,
3251                       "Thanks to");
3252     ystart += ystep_head;
3253     DrawTextSCentered(ystart, font_text,
3254                       "David Tritscher");
3255     ystart += ystep_head;
3256     DrawTextSCentered(ystart, font_head,
3257                       "for the code base used for the");
3258     ystart += ystep_line;
3259     DrawTextSCentered(ystart, font_head,
3260                       "native Emerald Mine engine");
3261   }
3262   else if (screen_nr == 7)
3263   {
3264     DrawTextSCentered(ystart, font_head,
3265                       "Thanks to");
3266     ystart += ystep_head;
3267     DrawTextSCentered(ystart, font_text,
3268                       "Guido Schulz");
3269     ystart += ystep_head;
3270     DrawTextSCentered(ystart, font_head,
3271                       "for the initial DOS port");
3272     ystart += ystep_para;
3273
3274     DrawTextSCentered(ystart, font_head,
3275                       "Thanks to");
3276     ystart += ystep_head;
3277     DrawTextSCentered(ystart, font_text,
3278                       "Karl H\xf6rnell");
3279     ystart += ystep_head;
3280     DrawTextSCentered(ystart, font_head,
3281                       "for some additional toons");
3282   }
3283   else if (screen_nr == 8)
3284   {
3285     DrawTextSCentered(ystart, font_head,
3286                       "And not to forget:");
3287     ystart += ystep_head;
3288     DrawTextSCentered(ystart, font_head,
3289                       "Many thanks to");
3290     ystart += ystep_head;
3291     DrawTextSCentered(ystart, font_text,
3292                       "All those who contributed");
3293     ystart += ystep_line;
3294     DrawTextSCentered(ystart, font_text,
3295                       "levels to this game");
3296     ystart += ystep_line;
3297     DrawTextSCentered(ystart, font_text,
3298                       "since 1995");
3299   }
3300
3301   DrawTextSCentered(ybottom, font_foot,
3302                     "Press any key or button for next page");
3303 }
3304
3305 void DrawInfoScreen_Credits()
3306 {
3307   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_CREDITS);
3308
3309   FadeMenuSoundsAndMusic();
3310
3311   FadeOut(REDRAW_FIELD);
3312
3313   HandleInfoScreen_Credits(MB_MENU_INITIALIZE);
3314
3315   FadeIn(REDRAW_FIELD);
3316 }
3317
3318 void HandleInfoScreen_Credits(int button)
3319 {
3320   static int screen_nr = 0;
3321   int num_screens = 9;
3322
3323   if (button == MB_MENU_INITIALIZE)
3324   {
3325     screen_nr = 0;
3326
3327     // DrawInfoScreen_CreditsScreen(screen_nr);
3328   }
3329
3330   if (button == MB_MENU_LEAVE)
3331   {
3332     PlaySound(SND_MENU_ITEM_SELECTING);
3333
3334     info_mode = INFO_MODE_MAIN;
3335     DrawInfoScreen();
3336
3337     return;
3338   }
3339   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
3340   {
3341     if (button != MB_MENU_INITIALIZE)
3342     {
3343       PlaySound(SND_MENU_ITEM_SELECTING);
3344
3345       screen_nr++;
3346     }
3347
3348     if (screen_nr >= num_screens)
3349     {
3350       FadeMenuSoundsAndMusic();
3351
3352       info_mode = INFO_MODE_MAIN;
3353       DrawInfoScreen();
3354
3355       return;
3356     }
3357
3358     if (screen_nr > 0)
3359       FadeSetNextScreen();
3360
3361     if (button != MB_MENU_INITIALIZE)
3362       FadeOut(REDRAW_FIELD);
3363
3364     DrawInfoScreen_CreditsScreen(screen_nr);
3365
3366     if (button != MB_MENU_INITIALIZE)
3367       FadeIn(REDRAW_FIELD);
3368   }
3369   else
3370   {
3371     PlayMenuSoundIfLoop();
3372   }
3373 }
3374
3375 void DrawInfoScreen_Program()
3376 {
3377   int font_title = MENU_INFO_FONT_TITLE;
3378   int font_head  = MENU_INFO_FONT_HEAD;
3379   int font_text  = MENU_INFO_FONT_TEXT;
3380   int font_foot  = MENU_INFO_FONT_FOOT;
3381   int spacing_title = menu.headline1_spacing_info[info_mode];
3382   int spacing_head  = menu.headline2_spacing_info[info_mode];
3383   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3384   int spacing_line  = menu.line_spacing_info[info_mode];
3385   int ystep_title = getMenuTextStep(spacing_title, font_title);
3386   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3387   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3388   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3389   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3390   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3391
3392   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM);
3393
3394   FadeOut(REDRAW_FIELD);
3395
3396   ClearField();
3397   DrawHeadline();
3398
3399   DrawTextSCentered(ystart, font_title, "Program Information:");
3400   ystart += ystep_title;
3401
3402   DrawTextSCentered(ystart, font_head,
3403                     "This game is Freeware!");
3404   ystart += ystep_head;
3405   DrawTextSCentered(ystart, font_head,
3406                     "If you like it, send e-mail to:");
3407   ystart += ystep_head;
3408   DrawTextSCentered(ystart, font_text,
3409                     setup.internal.program_email);
3410   ystart += ystep_para;
3411
3412   DrawTextSCentered(ystart, font_head,
3413                     "More information and levels:");
3414   ystart += ystep_head;
3415   DrawTextSCentered(ystart, font_text,
3416                     setup.internal.program_website);
3417   ystart += ystep_para;
3418
3419   DrawTextSCentered(ystart, font_head,
3420                     "If you have created new levels,");
3421   ystart += ystep_line;
3422   DrawTextSCentered(ystart, font_head,
3423                     "send them to me to include them!");
3424   ystart += ystep_head;
3425   DrawTextSCentered(ystart, font_head,
3426                     ":-)");
3427
3428   DrawTextSCentered(ybottom, font_foot,
3429                     "Press any key or button for info menu");
3430
3431   FadeIn(REDRAW_FIELD);
3432 }
3433
3434 void HandleInfoScreen_Program(int button)
3435 {
3436   if (button == MB_MENU_LEAVE)
3437   {
3438     PlaySound(SND_MENU_ITEM_SELECTING);
3439
3440     info_mode = INFO_MODE_MAIN;
3441     DrawInfoScreen();
3442
3443     return;
3444   }
3445   else if (button == MB_MENU_CHOICE)
3446   {
3447     PlaySound(SND_MENU_ITEM_SELECTING);
3448
3449     FadeMenuSoundsAndMusic();
3450
3451     info_mode = INFO_MODE_MAIN;
3452     DrawInfoScreen();
3453   }
3454   else
3455   {
3456     PlayMenuSoundIfLoop();
3457   }
3458 }
3459
3460 void DrawInfoScreen_Version()
3461 {
3462   int font_title = MENU_INFO_FONT_TITLE;
3463   int font_head  = MENU_INFO_FONT_HEAD;
3464   int font_text  = MENU_INFO_FONT_TEXT;
3465   int font_foot  = MENU_INFO_FONT_FOOT;
3466   int spacing_title = menu.headline1_spacing_info[info_mode];
3467   int spacing_head  = menu.headline2_spacing_info[info_mode];
3468   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3469   int spacing_line  = menu.line_spacing_info[info_mode];
3470   int xstep = getFontWidth(font_text);
3471   int ystep_title = getMenuTextStep(spacing_title, font_title);
3472   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3473   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3474   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3475   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3476   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3477   int xstart1 = mSX - SX + 2 * xstep;
3478   int xstart2 = mSX - SX + 18 * xstep;
3479   int xstart3 = mSX - SX + 28 * xstep;
3480   SDL_version sdl_version_compiled;
3481   const SDL_version *sdl_version_linked;
3482   int driver_name_len = 10;
3483 #if defined(TARGET_SDL2)
3484   SDL_version sdl_version_linked_ext;
3485   const char *driver_name = NULL;
3486 #else
3487   char driver_name[driver_name_len];
3488 #endif
3489
3490   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_VERSION);
3491
3492   FadeOut(REDRAW_FIELD);
3493
3494   ClearField();
3495   DrawHeadline();
3496
3497   DrawTextSCentered(ystart, font_title, "Version Information:");
3498   ystart += ystep_title;
3499
3500   DrawTextF(xstart1, ystart, font_head, "Name");
3501   DrawTextF(xstart2, ystart, font_text, getProgramTitleString());
3502   ystart += ystep_line;
3503
3504   if (!strEqual(getProgramVersionString(), getProgramRealVersionString()))
3505   {
3506     DrawTextF(xstart1, ystart, font_head, "Version (fake)");
3507     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3508     ystart += ystep_line;
3509
3510     DrawTextF(xstart1, ystart, font_head, "Version (real)");
3511     DrawTextF(xstart2, ystart, font_text, getProgramRealVersionString());
3512     ystart += ystep_line;
3513   }
3514   else
3515   {
3516     DrawTextF(xstart1, ystart, font_head, "Version");
3517     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3518     ystart += ystep_line;
3519   }
3520
3521   DrawTextF(xstart1, ystart, font_head, "Platform");
3522   DrawTextF(xstart2, ystart, font_text, PLATFORM_STRING);
3523   ystart += ystep_line;
3524
3525   DrawTextF(xstart1, ystart, font_head, "Target");
3526   DrawTextF(xstart2, ystart, font_text, TARGET_STRING);
3527   ystart += ystep_line;
3528
3529   DrawTextF(xstart1, ystart, font_head, "Source date");
3530   DrawTextF(xstart2, ystart, font_text, getSourceDateString());
3531   ystart += ystep_para;
3532
3533   DrawTextF(xstart1, ystart, font_head, "Library");
3534   DrawTextF(xstart2, ystart, font_head, "compiled");
3535   DrawTextF(xstart3, ystart, font_head, "linked");
3536   ystart += ystep_head;
3537
3538   SDL_VERSION(&sdl_version_compiled);
3539 #if defined(TARGET_SDL2)
3540   SDL_GetVersion(&sdl_version_linked_ext);
3541   sdl_version_linked = &sdl_version_linked_ext;
3542 #else
3543   sdl_version_linked = SDL_Linked_Version();
3544 #endif
3545
3546   DrawTextF(xstart1, ystart, font_text, "SDL");
3547   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3548             sdl_version_compiled.major,
3549             sdl_version_compiled.minor,
3550             sdl_version_compiled.patch);
3551   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3552             sdl_version_linked->major,
3553             sdl_version_linked->minor,
3554             sdl_version_linked->patch);
3555   ystart += ystep_line;
3556
3557   SDL_IMAGE_VERSION(&sdl_version_compiled);
3558   sdl_version_linked = IMG_Linked_Version();
3559
3560   DrawTextF(xstart1, ystart, font_text, "SDL_image");
3561   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3562             sdl_version_compiled.major,
3563             sdl_version_compiled.minor,
3564             sdl_version_compiled.patch);
3565   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3566             sdl_version_linked->major,
3567             sdl_version_linked->minor,
3568             sdl_version_linked->patch);
3569   ystart += ystep_line;
3570
3571   SDL_MIXER_VERSION(&sdl_version_compiled);
3572   sdl_version_linked = Mix_Linked_Version();
3573
3574   DrawTextF(xstart1, ystart, font_text, "SDL_mixer");
3575   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3576             sdl_version_compiled.major,
3577             sdl_version_compiled.minor,
3578             sdl_version_compiled.patch);
3579   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3580             sdl_version_linked->major,
3581             sdl_version_linked->minor,
3582             sdl_version_linked->patch);
3583   ystart += ystep_line;
3584
3585   SDL_NET_VERSION(&sdl_version_compiled);
3586   sdl_version_linked = SDLNet_Linked_Version();
3587
3588   DrawTextF(xstart1, ystart, font_text, "SDL_net");
3589   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3590             sdl_version_compiled.major,
3591             sdl_version_compiled.minor,
3592             sdl_version_compiled.patch);
3593   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3594             sdl_version_linked->major,
3595             sdl_version_linked->minor,
3596             sdl_version_linked->patch);
3597   ystart += ystep_para;
3598
3599   DrawTextF(xstart1, ystart, font_head, "Driver");
3600   DrawTextF(xstart2, ystart, font_head, "Requested");
3601   DrawTextF(xstart3, ystart, font_head, "Used");
3602   ystart += ystep_head;
3603
3604 #if defined(TARGET_SDL2)
3605   driver_name = getStringCopyNStatic(SDL_GetVideoDriver(0), driver_name_len);
3606 #else
3607   SDL_VideoDriverName(driver_name, driver_name_len);
3608 #endif
3609
3610   DrawTextF(xstart1, ystart, font_text, "SDL_VideoDriver");
3611   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
3612   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3613   ystart += ystep_line;
3614
3615 #if defined(TARGET_SDL2)
3616   driver_name = getStringCopyNStatic(SDL_GetAudioDriver(0), driver_name_len);
3617 #else
3618   SDL_AudioDriverName(driver_name, driver_name_len);
3619 #endif
3620
3621   DrawTextF(xstart1, ystart, font_text, "SDL_AudioDriver");
3622   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
3623   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3624
3625   DrawTextSCentered(ybottom, font_foot,
3626                     "Press any key or button for info menu");
3627
3628   FadeIn(REDRAW_FIELD);
3629 }
3630
3631 void HandleInfoScreen_Version(int button)
3632 {
3633   if (button == MB_MENU_LEAVE)
3634   {
3635     PlaySound(SND_MENU_ITEM_SELECTING);
3636
3637     info_mode = INFO_MODE_MAIN;
3638     DrawInfoScreen();
3639
3640     return;
3641   }
3642   else if (button == MB_MENU_CHOICE)
3643   {
3644     PlaySound(SND_MENU_ITEM_SELECTING);
3645
3646     FadeMenuSoundsAndMusic();
3647
3648     info_mode = INFO_MODE_MAIN;
3649     DrawInfoScreen();
3650   }
3651   else
3652   {
3653     PlayMenuSoundIfLoop();
3654   }
3655 }
3656
3657 void DrawInfoScreen_LevelSet()
3658 {
3659   struct TitleMessageInfo *tmi = &readme;
3660   char *filename = getLevelSetInfoFilename();
3661   char *title = "Level Set Information:";
3662   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3663   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3664
3665   if (filename == NULL)
3666   {
3667     DrawInfoScreen_NotAvailable(title, "No information for this level set.");
3668
3669     return;
3670   }
3671
3672   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
3673
3674   FadeOut(REDRAW_FIELD);
3675
3676   ClearField();
3677   DrawHeadline();
3678
3679   DrawTextSCentered(ystart, FONT_TEXT_1, title);
3680
3681   /* if x position set to "-1", automatically determine by playfield width */
3682   if (tmi->x == -1)
3683     tmi->x = SXSIZE / 2;
3684
3685   /* if y position set to "-1", use static default value */
3686   if (tmi->y == -1)
3687     tmi->y = 150;
3688
3689   /* if width set to "-1", automatically determine by playfield width */
3690   if (tmi->width == -1)
3691     tmi->width = SXSIZE - 2 * TILEX;
3692
3693   /* if height set to "-1", automatically determine by playfield height */
3694   if (tmi->height == -1)
3695     tmi->height = MENU_SCREEN_INFO_YBOTTOM - tmi->y - 10;
3696
3697   /* if chars set to "-1", automatically determine by text and font width */
3698   if (tmi->chars == -1)
3699     tmi->chars = tmi->width / getFontWidth(tmi->font);
3700   else
3701     tmi->width = tmi->chars * getFontWidth(tmi->font);
3702
3703   /* if lines set to "-1", automatically determine by text and font height */
3704   if (tmi->lines == -1)
3705     tmi->lines = tmi->height / getFontHeight(tmi->font);
3706   else
3707     tmi->height = tmi->lines * getFontHeight(tmi->font);
3708
3709   DrawTextFile(mSX + ALIGNED_TEXT_XPOS(tmi), mSY + ALIGNED_TEXT_YPOS(tmi),
3710                filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
3711                tmi->autowrap, tmi->centered, tmi->parse_comments);
3712
3713   DrawTextSCentered(ybottom, FONT_TEXT_4,
3714                     "Press any key or button for info menu");
3715
3716   FadeIn(REDRAW_FIELD);
3717 }
3718
3719 void HandleInfoScreen_LevelSet(int button)
3720 {
3721   if (button == MB_MENU_LEAVE)
3722   {
3723     PlaySound(SND_MENU_ITEM_SELECTING);
3724
3725     info_mode = INFO_MODE_MAIN;
3726     DrawInfoScreen();
3727
3728     return;
3729   }
3730   else if (button == MB_MENU_CHOICE)
3731   {
3732     PlaySound(SND_MENU_ITEM_SELECTING);
3733
3734     FadeMenuSoundsAndMusic();
3735
3736     info_mode = INFO_MODE_MAIN;
3737     DrawInfoScreen();
3738   }
3739   else
3740   {
3741     PlayMenuSoundIfLoop();
3742   }
3743 }
3744
3745 static void DrawInfoScreen()
3746 {
3747   if (info_mode == INFO_MODE_TITLE)
3748     DrawInfoScreen_TitleScreen();
3749   else if (info_mode == INFO_MODE_ELEMENTS)
3750     DrawInfoScreen_Elements();
3751   else if (info_mode == INFO_MODE_MUSIC)
3752     DrawInfoScreen_Music();
3753   else if (info_mode == INFO_MODE_CREDITS)
3754     DrawInfoScreen_Credits();
3755   else if (info_mode == INFO_MODE_PROGRAM)
3756     DrawInfoScreen_Program();
3757   else if (info_mode == INFO_MODE_VERSION)
3758     DrawInfoScreen_Version();
3759   else if (info_mode == INFO_MODE_LEVELSET)
3760     DrawInfoScreen_LevelSet();
3761   else
3762     DrawInfoScreen_Main();
3763
3764   if (info_mode != INFO_MODE_MAIN &&
3765       info_mode != INFO_MODE_TITLE &&
3766       info_mode != INFO_MODE_MUSIC)
3767     PlayMenuSoundsAndMusic();
3768 }
3769
3770 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
3771 {
3772   if (info_mode == INFO_MODE_TITLE)
3773     HandleInfoScreen_TitleScreen(button);
3774   else if (info_mode == INFO_MODE_ELEMENTS)
3775     HandleInfoScreen_Elements(button);
3776   else if (info_mode == INFO_MODE_MUSIC)
3777     HandleInfoScreen_Music(button);
3778   else if (info_mode == INFO_MODE_CREDITS)
3779     HandleInfoScreen_Credits(button);
3780   else if (info_mode == INFO_MODE_PROGRAM)
3781     HandleInfoScreen_Program(button);
3782   else if (info_mode == INFO_MODE_VERSION)
3783     HandleInfoScreen_Version(button);
3784   else if (info_mode == INFO_MODE_LEVELSET)
3785     HandleInfoScreen_LevelSet(button);
3786   else
3787     HandleInfoScreen_Main(mx, my, dx, dy, button);
3788 }
3789
3790
3791 /* ========================================================================= */
3792 /* type name functions                                                       */
3793 /* ========================================================================= */
3794
3795 void HandleTypeName(int newxpos, Key key)
3796 {
3797   static char last_player_name[MAX_PLAYER_NAME_LEN + 1];
3798   struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
3799   struct TextPosInfo *pos = mci->pos_input;
3800   int startx = mSX + ALIGNED_TEXT_XPOS(pos);
3801   int starty = mSY + ALIGNED_TEXT_YPOS(pos);
3802   static int xpos = 0;
3803   int font_nr = pos->font;
3804   int font_active_nr = FONT_ACTIVE(font_nr);
3805   int font_width = getFontWidth(font_active_nr);
3806   char key_char = getValidConfigValueChar(getCharFromKey(key));
3807   boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
3808   boolean is_active = TRUE;
3809
3810   DrawBackgroundForFont(startx,starty, pos->width, pos->height, font_active_nr);
3811
3812   if (newxpos)
3813   {
3814     strcpy(last_player_name, setup.player_name);
3815
3816     xpos = newxpos;
3817
3818     StartTextInput(startx, starty, pos->width, pos->height);
3819   }
3820   else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
3821   {
3822     setup.player_name[xpos] = key_char;
3823     setup.player_name[xpos + 1] = 0;
3824
3825     xpos++;
3826   }
3827   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
3828   {
3829     xpos--;
3830
3831     setup.player_name[xpos] = 0;
3832   }
3833   else if (key == KSYM_Return && xpos > 0)
3834   {
3835     SaveSetup();
3836
3837     is_active = FALSE;
3838
3839     SetGameStatus(GAME_MODE_MAIN);
3840   }
3841   else if (key == KSYM_Escape)
3842   {
3843     strcpy(setup.player_name, last_player_name);
3844
3845     is_active = FALSE;
3846
3847     SetGameStatus(GAME_MODE_MAIN);
3848   }
3849
3850   if (is_active)
3851   {
3852     pos->width = (strlen(setup.player_name) + 1) * font_width;
3853     startx = mSX + ALIGNED_TEXT_XPOS(pos);
3854
3855     DrawText(startx, starty, setup.player_name, font_active_nr);
3856     DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
3857   }
3858   else
3859   {
3860     pos->width = strlen(setup.player_name) * font_width;
3861     startx = mSX + ALIGNED_TEXT_XPOS(pos);
3862
3863     DrawText(startx, starty, setup.player_name, font_nr);
3864
3865     StopTextInput();
3866   }
3867 }
3868
3869
3870 /* ========================================================================= */
3871 /* tree menu functions                                                       */
3872 /* ========================================================================= */
3873
3874 static void DrawChooseTree(TreeInfo **ti_ptr)
3875 {
3876   int fade_mask = REDRAW_FIELD;
3877
3878   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3879     fade_mask = REDRAW_ALL;
3880
3881   if (strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
3882   {
3883     SetGameStatus(GAME_MODE_MAIN);
3884
3885     DrawMainMenu();
3886
3887     return;
3888   }
3889
3890   UnmapAllGadgets();
3891
3892   FreeScreenGadgets();
3893   CreateScreenGadgets();
3894
3895   FadeOut(fade_mask);
3896
3897   /* needed if different viewport properties defined for choosing level (set) */
3898   ChangeViewportPropertiesIfNeeded();
3899
3900   if (game_status == GAME_MODE_LEVELNR)
3901     SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
3902   else if (game_status == GAME_MODE_LEVELS)
3903     SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
3904
3905   ClearField();
3906
3907   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3908
3909   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
3910   MapScreenTreeGadgets(*ti_ptr);
3911
3912   DrawMaskedBorder(fade_mask);
3913
3914   FadeIn(fade_mask);
3915 }
3916
3917 static void drawChooseTreeList(int first_entry, int num_page_entries,
3918                                TreeInfo *ti)
3919 {
3920   int i;
3921   char *title_string = NULL;
3922   int yoffset_sets = MENU_TITLE1_YPOS;
3923   int yoffset_setup = 16;
3924   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ||
3925                  ti->type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup);
3926
3927   title_string = ti->infotext;
3928
3929   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
3930
3931   clearMenuListArea();
3932
3933   for (i = 0; i < num_page_entries; i++)
3934   {
3935     TreeInfo *node, *node_first;
3936     int entry_pos = first_entry + i;
3937     int xpos = MENU_SCREEN_START_XPOS;
3938     int ypos = MENU_SCREEN_START_YPOS + i;
3939     int startx = mSX + xpos * 32;
3940     int starty = mSY + ypos * 32;
3941     int font_nr = FONT_TEXT_1;
3942     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
3943     int startx_text = startx + font_xoffset;
3944     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
3945     int text_size = startx_scrollbar - startx_text;
3946     int max_buffer_len = text_size / getFontWidth(font_nr);
3947     char buffer[max_buffer_len + 1];
3948
3949     node_first = getTreeInfoFirstGroupEntry(ti);
3950     node = getTreeInfoFromPos(node_first, entry_pos);
3951
3952     strncpy(buffer, node->name, max_buffer_len);
3953     buffer[max_buffer_len] = '\0';
3954
3955     DrawText(startx, starty, buffer, font_nr + node->color);
3956
3957     if (node->parent_link)
3958       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3959     else if (node->level_group)
3960       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3961     else
3962       initCursor(i, IMG_MENU_BUTTON);
3963   }
3964
3965   redraw_mask |= REDRAW_FIELD;
3966 }
3967
3968 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
3969 {
3970   TreeInfo *node, *node_first;
3971   int x, last_redraw_mask = redraw_mask;
3972   int ypos = MENU_TITLE2_YPOS;
3973   int font_nr = FONT_TITLE_2;
3974
3975   if (ti->type == TREE_TYPE_LEVEL_NR)
3976     DrawTextFCentered(ypos, font_nr, leveldir_current->name);
3977
3978   if (ti->type != TREE_TYPE_LEVEL_DIR)
3979     return;
3980
3981   node_first = getTreeInfoFirstGroupEntry(ti);
3982   node = getTreeInfoFromPos(node_first, entry_pos);
3983
3984   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
3985
3986   if (node->parent_link)
3987     DrawTextFCentered(ypos, font_nr, "leave \"%s\"",
3988                       node->node_parent->name);
3989   else if (node->level_group)
3990     DrawTextFCentered(ypos, font_nr, "enter \"%s\"",
3991                       node->name);
3992   else if (ti->type == TREE_TYPE_LEVEL_DIR)
3993     DrawTextFCentered(ypos, font_nr, "%3d %s (%s)",
3994                       node->levels, (node->levels > 1 ? "levels" : "level"),
3995                       node->class_desc);
3996
3997   /* let BackToFront() redraw only what is needed */
3998   redraw_mask = last_redraw_mask;
3999   for (x = 0; x < SCR_FIELDX; x++)
4000     MarkTileDirty(x, 1);
4001 }
4002
4003 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
4004                              TreeInfo **ti_ptr)
4005 {
4006   TreeInfo *ti = *ti_ptr;
4007   int x = 0;
4008   int y = ti->cl_cursor;
4009   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4010   int num_entries = numTreeInfoInGroup(ti);
4011   int num_page_entries;
4012   boolean position_set_by_scrollbar = (dx == 999);
4013
4014   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4015     num_page_entries = num_entries;
4016   else
4017     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4018
4019   if (button == MB_MENU_INITIALIZE)
4020   {
4021     int num_entries = numTreeInfoInGroup(ti);
4022     int entry_pos = posTreeInfo(ti);
4023
4024     if (ti->cl_first == -1)
4025     {
4026       /* only on initialization */
4027       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
4028       ti->cl_cursor = entry_pos - ti->cl_first;
4029     }
4030     else if (ti->cl_cursor >= num_page_entries ||
4031              (num_entries > num_page_entries &&
4032               num_entries - ti->cl_first < num_page_entries))
4033     {
4034       /* only after change of list size (by custom graphic configuration) */
4035       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
4036       ti->cl_cursor = entry_pos - ti->cl_first;
4037     }
4038
4039     if (position_set_by_scrollbar)
4040       ti->cl_first = dy;
4041     else
4042       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
4043                                 ti->cl_first, ti);
4044
4045     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
4046     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
4047     drawChooseTreeCursor(ti->cl_cursor, TRUE);
4048
4049     return;
4050   }
4051   else if (button == MB_MENU_LEAVE)
4052   {
4053     FadeSetLeaveMenu();
4054
4055     PlaySound(SND_MENU_ITEM_SELECTING);
4056
4057     if (ti->node_parent)
4058     {
4059       *ti_ptr = ti->node_parent;
4060       DrawChooseTree(ti_ptr);
4061     }
4062     else if (game_status == GAME_MODE_SETUP)
4063     {
4064       if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4065           setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4066           setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4067         execSetupGame();
4068       else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4069                setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4070                setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4071         execSetupGraphics();
4072       else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4073                setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4074                setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4075         execSetupSound();
4076       else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4077                setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4078                setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
4079         execSetupTouch();
4080       else
4081         execSetupArtwork();
4082     }
4083     else
4084     {
4085       if (game_status == GAME_MODE_LEVELNR)
4086       {
4087         int new_level_nr = atoi(level_number_current->identifier);
4088
4089         HandleMainMenu_SelectLevel(0, 0, new_level_nr);
4090       }
4091
4092       SetGameStatus(GAME_MODE_MAIN);
4093
4094       DrawMainMenu();
4095     }
4096
4097     return;
4098   }
4099
4100   if (mx || my)         /* mouse input */
4101   {
4102     x = (mx - mSX) / 32;
4103     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
4104   }
4105   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
4106   {
4107     /* move cursor instead of scrolling when already at start/end of list */
4108     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
4109       dy = -1;
4110     else if (dy == +1 * SCROLL_LINE &&
4111              ti->cl_first + num_page_entries == num_entries)
4112       dy = 1;
4113
4114     /* handle scrolling screen one line or page */
4115     if (ti->cl_cursor + dy < 0 ||
4116         ti->cl_cursor + dy > num_page_entries - 1)
4117     {
4118       boolean redraw = FALSE;
4119
4120       if (ABS(dy) == SCROLL_PAGE)
4121         step = num_page_entries - 1;
4122
4123       if (dy < 0 && ti->cl_first > 0)
4124       {
4125         /* scroll page/line up */
4126
4127         ti->cl_first -= step;
4128         if (ti->cl_first < 0)
4129           ti->cl_first = 0;
4130
4131         redraw = TRUE;
4132       }
4133       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
4134       {
4135         /* scroll page/line down */
4136
4137         ti->cl_first += step;
4138         if (ti->cl_first + num_page_entries > num_entries)
4139           ti->cl_first = MAX(0, num_entries - num_page_entries);
4140
4141         redraw = TRUE;
4142       }
4143
4144       if (redraw)
4145       {
4146         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
4147         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
4148         drawChooseTreeCursor(ti->cl_cursor, TRUE);
4149
4150         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
4151                                   ti->cl_first, ti);
4152       }
4153
4154       return;
4155     }
4156
4157     /* handle moving cursor one line */
4158     y = ti->cl_cursor + dy;
4159   }
4160
4161   if (dx == 1)
4162   {
4163     TreeInfo *node_first, *node_cursor;
4164     int entry_pos = ti->cl_first + y;
4165
4166     node_first = getTreeInfoFirstGroupEntry(ti);
4167     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
4168
4169     if (node_cursor->node_group)
4170     {
4171       FadeSetEnterMenu();
4172
4173       PlaySound(SND_MENU_ITEM_SELECTING);
4174
4175       node_cursor->cl_first = ti->cl_first;
4176       node_cursor->cl_cursor = ti->cl_cursor;
4177       *ti_ptr = node_cursor->node_group;
4178       DrawChooseTree(ti_ptr);
4179
4180       return;
4181     }
4182   }
4183   else if (dx == -1 && ti->node_parent)
4184   {
4185     FadeSetLeaveMenu();
4186
4187     PlaySound(SND_MENU_ITEM_SELECTING);
4188
4189     *ti_ptr = ti->node_parent;
4190     DrawChooseTree(ti_ptr);
4191
4192     return;
4193   }
4194
4195   if (!anyScrollbarGadgetActive() &&
4196       IN_VIS_MENU(x, y) &&
4197       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
4198       y >= 0 && y < num_page_entries)
4199   {
4200     if (button)
4201     {
4202       if (y != ti->cl_cursor)
4203       {
4204         PlaySound(SND_MENU_ITEM_ACTIVATING);
4205
4206         drawChooseTreeCursor(ti->cl_cursor, FALSE);
4207         drawChooseTreeCursor(y, TRUE);
4208         drawChooseTreeInfo(ti->cl_first + y, ti);
4209
4210         ti->cl_cursor = y;
4211       }
4212       else if (dx < 0)
4213       {
4214         if (game_status == GAME_MODE_SETUP)
4215         {
4216           if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4217               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4218               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4219             execSetupGame();
4220           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4221                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4222                    setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4223             execSetupGraphics();
4224           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4225                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4226                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4227             execSetupSound();
4228           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4229                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4230                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
4231             execSetupTouch();
4232           else
4233             execSetupArtwork();
4234         }
4235       }
4236     }
4237     else
4238     {
4239       TreeInfo *node_first, *node_cursor;
4240       int entry_pos = ti->cl_first + y;
4241
4242       PlaySound(SND_MENU_ITEM_SELECTING);
4243
4244       node_first = getTreeInfoFirstGroupEntry(ti);
4245       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
4246
4247       if (node_cursor->node_group)
4248       {
4249         FadeSetEnterMenu();
4250
4251         node_cursor->cl_first = ti->cl_first;
4252         node_cursor->cl_cursor = ti->cl_cursor;
4253         *ti_ptr = node_cursor->node_group;
4254         DrawChooseTree(ti_ptr);
4255       }
4256       else if (node_cursor->parent_link)
4257       {
4258         FadeSetLeaveMenu();
4259
4260         *ti_ptr = node_cursor->node_parent;
4261         DrawChooseTree(ti_ptr);
4262       }
4263       else
4264       {
4265         FadeSetEnterMenu();
4266
4267         node_cursor->cl_first = ti->cl_first;
4268         node_cursor->cl_cursor = ti->cl_cursor;
4269         *ti_ptr = node_cursor;
4270
4271         if (ti->type == TREE_TYPE_LEVEL_DIR)
4272         {
4273           LoadLevelSetup_SeriesInfo();
4274
4275           SaveLevelSetup_LastSeries();
4276           SaveLevelSetup_SeriesInfo();
4277           TapeErase();
4278         }
4279
4280         if (game_status == GAME_MODE_SETUP)
4281         {
4282           if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4283               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4284               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4285             execSetupGame();
4286           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4287                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4288                    setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4289             execSetupGraphics();
4290           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4291                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4292                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4293             execSetupSound();
4294           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4295                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4296                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
4297             execSetupTouch();
4298           else
4299             execSetupArtwork();
4300         }
4301         else
4302         {
4303           if (game_status == GAME_MODE_LEVELNR)
4304           {
4305             int new_level_nr = atoi(level_number_current->identifier);
4306
4307             HandleMainMenu_SelectLevel(0, 0, new_level_nr);
4308           }
4309
4310           SetGameStatus(GAME_MODE_MAIN);
4311
4312           DrawMainMenu();
4313         }
4314       }
4315     }
4316   }
4317 }
4318
4319 void DrawChooseLevelSet()
4320 {
4321   FadeMenuSoundsAndMusic();
4322
4323   DrawChooseTree(&leveldir_current);
4324
4325   PlayMenuSoundsAndMusic();
4326 }
4327
4328 void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
4329 {
4330   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
4331 }
4332
4333 void DrawChooseLevelNr()
4334 {
4335   int i;
4336
4337   FadeMenuSoundsAndMusic();
4338
4339   if (level_number != NULL)
4340   {
4341     freeTreeInfo(level_number);
4342
4343     level_number = NULL;
4344   }
4345
4346   for (i = leveldir_current->first_level; i <= leveldir_current->last_level;i++)
4347   {
4348     TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_LEVEL_NR);
4349     char identifier[32], name[32];
4350     int value = i;
4351
4352     /* temporarily load level info to get level name */
4353     LoadLevelInfoOnly(i);
4354
4355     ti->node_top = &level_number;
4356     ti->sort_priority = 10000 + value;
4357     ti->color = (level.no_level_file ? FC_BLUE :
4358                  LevelStats_getSolved(i) ? FC_GREEN :
4359                  LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
4360
4361     snprintf(identifier, sizeof(identifier), "%d", value);
4362     snprintf(name, sizeof(name), "%03d: %s", value,
4363              (level.no_level_file ? "(no file)" : level.name));
4364
4365     setString(&ti->identifier, identifier);
4366     setString(&ti->name, name);
4367     setString(&ti->name_sorting, name);
4368
4369     pushTreeInfo(&level_number, ti);
4370   }
4371
4372   /* sort level number values to start with lowest level number */
4373   sortTreeInfo(&level_number);
4374
4375   /* set current level number to current level number */
4376   level_number_current =
4377     getTreeInfoFromIdentifier(level_number, i_to_a(level_nr));
4378
4379   /* if that also fails, set current level number to first available level */
4380   if (level_number_current == NULL)
4381     level_number_current = level_number;
4382
4383   DrawChooseTree(&level_number_current);
4384
4385   PlayMenuSoundsAndMusic();
4386 }
4387
4388 void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
4389 {
4390   HandleChooseTree(mx, my, dx, dy, button, &level_number_current);
4391 }
4392
4393 void DrawHallOfFame(int highlight_position)
4394 {
4395   int fade_mask = REDRAW_FIELD;
4396
4397   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
4398     fade_mask = REDRAW_ALL;
4399
4400   UnmapAllGadgets();
4401   FadeMenuSoundsAndMusic();
4402
4403   /* (this is needed when called from GameEnd() after winning a game) */
4404   KeyboardAutoRepeatOn();
4405
4406   /* (this is needed when called from GameEnd() after winning a game) */
4407   SetDrawDeactivationMask(REDRAW_NONE);
4408   SetDrawBackgroundMask(REDRAW_FIELD);
4409
4410   if (highlight_position < 0) 
4411     LoadScore(level_nr);
4412   else
4413     SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW);
4414
4415   FadeSetEnterScreen();
4416
4417   FadeOut(fade_mask);
4418
4419   /* needed if different viewport properties defined for scores */
4420   ChangeViewportPropertiesIfNeeded();
4421
4422   PlayMenuSoundsAndMusic();
4423
4424   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
4425
4426   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
4427
4428   DrawMaskedBorder(fade_mask);
4429
4430   FadeIn(fade_mask);
4431 }
4432
4433 static void drawHallOfFameList(int first_entry, int highlight_position)
4434 {
4435   int i, j;
4436
4437   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
4438   ClearField();
4439
4440   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
4441   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
4442                     "HighScores of Level %d", level_nr);
4443
4444   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
4445   {
4446     int entry = first_entry + i;
4447     boolean active = (entry == highlight_position);
4448     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
4449     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
4450     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
4451     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
4452     int dxoff = getFontDrawOffsetX(font_nr1);
4453     int dx1 = 3 * getFontWidth(font_nr1);
4454     int dx2 = dx1 + getFontWidth(font_nr1);
4455     int dx3 = SXSIZE - 2 * (mSX - SX + dxoff) - 5 * getFontWidth(font_nr4);
4456     int num_dots = (dx3 - dx2) / getFontWidth(font_nr3);
4457     int sy = mSY + 64 + i * 32;
4458
4459     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
4460     DrawText(mSX + dx1, sy, ".", font_nr1);
4461
4462     for (j = 0; j < num_dots; j++)
4463       DrawText(mSX + dx2 + j * getFontWidth(font_nr3), sy, ".", font_nr3);
4464
4465     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
4466       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
4467
4468     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
4469   }
4470
4471   redraw_mask |= REDRAW_FIELD;
4472 }
4473
4474 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
4475 {
4476   static int first_entry = 0;
4477   static int highlight_position = 0;
4478   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4479
4480   if (button == MB_MENU_INITIALIZE)
4481   {
4482     first_entry = 0;
4483     highlight_position = mx;
4484     drawHallOfFameList(first_entry, highlight_position);
4485
4486     return;
4487   }
4488
4489   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
4490     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
4491
4492   if (dy < 0)
4493   {
4494     if (first_entry > 0)
4495     {
4496       first_entry -= step;
4497       if (first_entry < 0)
4498         first_entry = 0;
4499
4500       drawHallOfFameList(first_entry, highlight_position);
4501     }
4502   }
4503   else if (dy > 0)
4504   {
4505     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
4506     {
4507       first_entry += step;
4508       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
4509         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
4510
4511       drawHallOfFameList(first_entry, highlight_position);
4512     }
4513   }
4514   else if (button == MB_MENU_LEAVE)
4515   {
4516     PlaySound(SND_MENU_ITEM_SELECTING);
4517
4518     FadeSound(SND_BACKGROUND_SCORES);
4519
4520     SetGameStatus(GAME_MODE_MAIN);
4521
4522     DrawMainMenu();
4523   }
4524   else if (button == MB_MENU_CHOICE)
4525   {
4526     PlaySound(SND_MENU_ITEM_SELECTING);
4527
4528     FadeSound(SND_BACKGROUND_SCORES);
4529
4530     SetGameStatus(GAME_MODE_MAIN);
4531
4532     DrawMainMenu();
4533   }
4534
4535   if (game_status == GAME_MODE_SCORES)
4536     PlayMenuSoundIfLoop();
4537 }
4538
4539
4540 /* ========================================================================= */
4541 /* setup screen functions                                                    */
4542 /* ========================================================================= */
4543
4544 static struct TokenInfo *setup_info;
4545 static int num_setup_info;      /* number of setup entries shown on screen */
4546 static int max_setup_info;      /* total number of setup entries in list */
4547
4548 static char *window_size_text;
4549 static char *scaling_type_text;
4550 static char *rendering_mode_text;
4551 static char *scroll_delay_text;
4552 static char *snapshot_mode_text;
4553 static char *game_speed_text;
4554 static char *graphics_set_name;
4555 static char *sounds_set_name;
4556 static char *music_set_name;
4557 static char *volume_simple_text;
4558 static char *volume_loops_text;
4559 static char *volume_music_text;
4560 static char *touch_controls_text;
4561 static char *move_distance_text;
4562 static char *drop_distance_text;
4563
4564 static void execSetupMain()
4565 {
4566   setup_mode = SETUP_MODE_MAIN;
4567
4568   DrawSetupScreen();
4569 }
4570
4571 static void execSetupGame_setGameSpeeds()
4572 {
4573   if (game_speeds == NULL)
4574   {
4575     int i;
4576
4577     for (i = 0; game_speeds_list[i].value != -1; i++)
4578     {
4579       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4580       char identifier[32], name[32];
4581       int value = game_speeds_list[i].value;
4582       char *text = game_speeds_list[i].text;
4583
4584       ti->node_top = &game_speeds;
4585       ti->sort_priority = 10000 - value;
4586
4587       sprintf(identifier, "%d", value);
4588       sprintf(name, "%s", text);
4589
4590       setString(&ti->identifier, identifier);
4591       setString(&ti->name, name);
4592       setString(&ti->name_sorting, name);
4593       setString(&ti->infotext, STR_SETUP_CHOOSE_GAME_SPEED);
4594
4595       pushTreeInfo(&game_speeds, ti);
4596     }
4597
4598     /* sort game speed values to start with slowest game speed */
4599     sortTreeInfo(&game_speeds);
4600
4601     /* set current game speed to configured game speed value */
4602     game_speed_current =
4603       getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
4604
4605     /* if that fails, set current game speed to reliable default value */
4606     if (game_speed_current == NULL)
4607       game_speed_current =
4608         getTreeInfoFromIdentifier(game_speeds, i_to_a(GAME_FRAME_DELAY));
4609
4610     /* if that also fails, set current game speed to first available speed */
4611     if (game_speed_current == NULL)
4612       game_speed_current = game_speeds;
4613   }
4614
4615   setup.game_frame_delay = atoi(game_speed_current->identifier);
4616
4617   /* needed for displaying game speed text instead of identifier */
4618   game_speed_text = game_speed_current->name;
4619 }
4620
4621 static void execSetupGame_setScrollDelays()
4622 {
4623   if (scroll_delays == NULL)
4624   {
4625     int i;
4626
4627     for (i = 0; scroll_delays_list[i].value != -1; i++)
4628     {
4629       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4630       char identifier[32], name[32];
4631       int value = scroll_delays_list[i].value;
4632       char *text = scroll_delays_list[i].text;
4633
4634       ti->node_top = &scroll_delays;
4635       ti->sort_priority = value;
4636
4637       sprintf(identifier, "%d", value);
4638       sprintf(name, "%s", text);
4639
4640       setString(&ti->identifier, identifier);
4641       setString(&ti->name, name);
4642       setString(&ti->name_sorting, name);
4643       setString(&ti->infotext, STR_SETUP_CHOOSE_SCROLL_DELAY);
4644
4645       pushTreeInfo(&scroll_delays, ti);
4646     }
4647
4648     /* sort scroll delay values to start with lowest scroll delay value */
4649     sortTreeInfo(&scroll_delays);
4650
4651     /* set current scroll delay value to configured scroll delay value */
4652     scroll_delay_current =
4653       getTreeInfoFromIdentifier(scroll_delays,i_to_a(setup.scroll_delay_value));
4654
4655     /* if that fails, set current scroll delay to reliable default value */
4656     if (scroll_delay_current == NULL)
4657       scroll_delay_current =
4658         getTreeInfoFromIdentifier(scroll_delays, i_to_a(STD_SCROLL_DELAY));
4659
4660     /* if that also fails, set current scroll delay to first available value */
4661     if (scroll_delay_current == NULL)
4662       scroll_delay_current = scroll_delays;
4663   }
4664
4665   setup.scroll_delay_value = atoi(scroll_delay_current->identifier);
4666
4667   /* needed for displaying scroll delay text instead of identifier */
4668   scroll_delay_text = scroll_delay_current->name;
4669 }
4670
4671 static void execSetupGame_setSnapshotModes()
4672 {
4673   if (snapshot_modes == NULL)
4674   {
4675     int i;
4676
4677     for (i = 0; snapshot_modes_list[i].value != NULL; i++)
4678     {
4679       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4680       char identifier[32], name[32];
4681       char *value = snapshot_modes_list[i].value;
4682       char *text = snapshot_modes_list[i].text;
4683
4684       ti->node_top = &snapshot_modes;
4685       ti->sort_priority = i;
4686
4687       sprintf(identifier, "%s", value);
4688       sprintf(name, "%s", text);
4689
4690       setString(&ti->identifier, identifier);
4691       setString(&ti->name, name);
4692       setString(&ti->name_sorting, name);
4693       setString(&ti->infotext, STR_SETUP_CHOOSE_SNAPSHOT_MODE);
4694
4695       pushTreeInfo(&snapshot_modes, ti);
4696     }
4697
4698     /* sort snapshot mode values to start with lowest snapshot mode value */
4699     sortTreeInfo(&snapshot_modes);
4700
4701     /* set current snapshot mode value to configured snapshot mode value */
4702     snapshot_mode_current =
4703       getTreeInfoFromIdentifier(snapshot_modes, setup.engine_snapshot_mode);
4704
4705     /* if that fails, set current snapshot mode to reliable default value */
4706     if (snapshot_mode_current == NULL)
4707       snapshot_mode_current =
4708         getTreeInfoFromIdentifier(snapshot_modes, STR_SNAPSHOT_MODE_DEFAULT);
4709
4710     /* if that also fails, set current snapshot mode to first available value */
4711     if (snapshot_mode_current == NULL)
4712       snapshot_mode_current = snapshot_modes;
4713   }
4714
4715   setup.engine_snapshot_mode = snapshot_mode_current->identifier;
4716
4717   /* needed for displaying snapshot mode text instead of identifier */
4718   snapshot_mode_text = snapshot_mode_current->name;
4719 }
4720
4721 static void execSetupGame()
4722 {
4723   execSetupGame_setGameSpeeds();
4724   execSetupGame_setScrollDelays();
4725   execSetupGame_setSnapshotModes();
4726
4727   setup_mode = SETUP_MODE_GAME;
4728
4729   DrawSetupScreen();
4730 }
4731
4732 static void execSetupChooseGameSpeed()
4733 {
4734   setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED;
4735
4736   DrawSetupScreen();
4737 }
4738
4739 static void execSetupChooseScrollDelay()
4740 {
4741   setup_mode = SETUP_MODE_CHOOSE_SCROLL_DELAY;
4742
4743   DrawSetupScreen();
4744 }
4745
4746 static void execSetupChooseSnapshotMode()
4747 {
4748   setup_mode = SETUP_MODE_CHOOSE_SNAPSHOT_MODE;
4749
4750   DrawSetupScreen();
4751 }
4752
4753 static void execSetupEditor()
4754 {
4755   setup_mode = SETUP_MODE_EDITOR;
4756
4757   DrawSetupScreen();
4758 }
4759
4760 static void execSetupGraphics_setWindowSizes(boolean update_list)
4761 {
4762   if (window_sizes != NULL && update_list)
4763   {
4764     freeTreeInfo(window_sizes);
4765
4766     window_sizes = NULL;
4767   }
4768
4769   if (window_sizes == NULL)
4770   {
4771     boolean current_window_size_found = FALSE;
4772     int i;
4773
4774     for (i = 0; window_sizes_list[i].value != -1; i++)
4775     {
4776       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4777       char identifier[32], name[32];
4778       int value = window_sizes_list[i].value;
4779       char *text = window_sizes_list[i].text;
4780
4781       ti->node_top = &window_sizes;
4782       ti->sort_priority = value;
4783
4784       sprintf(identifier, "%d", value);
4785       sprintf(name, "%s", text);
4786
4787       setString(&ti->identifier, identifier);
4788       setString(&ti->name, name);
4789       setString(&ti->name_sorting, name);
4790       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
4791
4792       pushTreeInfo(&window_sizes, ti);
4793
4794       if (value == setup.window_scaling_percent)
4795         current_window_size_found = TRUE;
4796     }
4797
4798     if (!current_window_size_found)
4799     {
4800       // add entry for non-preset window scaling value
4801
4802       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4803       char identifier[32], name[32];
4804       int value = setup.window_scaling_percent;
4805
4806       ti->node_top = &window_sizes;
4807       ti->sort_priority = value;
4808
4809       sprintf(identifier, "%d", value);
4810       sprintf(name, "%d %% (Current)", value);
4811
4812       setString(&ti->identifier, identifier);
4813       setString(&ti->name, name);
4814       setString(&ti->name_sorting, name);
4815       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
4816
4817       pushTreeInfo(&window_sizes, ti);
4818     }
4819
4820     /* sort window size values to start with lowest window size value */
4821     sortTreeInfo(&window_sizes);
4822
4823     /* set current window size value to configured window size value */
4824     window_size_current =
4825       getTreeInfoFromIdentifier(window_sizes,
4826                                 i_to_a(setup.window_scaling_percent));
4827
4828     /* if that fails, set current window size to reliable default value */
4829     if (window_size_current == NULL)
4830       window_size_current =
4831         getTreeInfoFromIdentifier(window_sizes,
4832                                   i_to_a(STD_WINDOW_SCALING_PERCENT));
4833
4834     /* if that also fails, set current window size to first available value */
4835     if (window_size_current == NULL)
4836       window_size_current = window_sizes;
4837   }
4838
4839   setup.window_scaling_percent = atoi(window_size_current->identifier);
4840
4841   /* needed for displaying window size text instead of identifier */
4842   window_size_text = window_size_current->name;
4843 }
4844
4845 static void execSetupGraphics_setScalingTypes()
4846 {
4847   if (scaling_types == NULL)
4848   {
4849     int i;
4850
4851     for (i = 0; scaling_types_list[i].value != NULL; i++)
4852     {
4853       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4854       char identifier[32], name[32];
4855       char *value = scaling_types_list[i].value;
4856       char *text = scaling_types_list[i].text;
4857
4858       ti->node_top = &scaling_types;
4859       ti->sort_priority = i;
4860
4861       sprintf(identifier, "%s", value);
4862       sprintf(name, "%s", text);
4863
4864       setString(&ti->identifier, identifier);
4865       setString(&ti->name, name);
4866       setString(&ti->name_sorting, name);
4867       setString(&ti->infotext, STR_SETUP_CHOOSE_SCALING_TYPE);
4868
4869       pushTreeInfo(&scaling_types, ti);
4870     }
4871
4872     /* sort scaling type values to start with lowest scaling type value */
4873     sortTreeInfo(&scaling_types);
4874
4875     /* set current scaling type value to configured scaling type value */
4876     scaling_type_current =
4877       getTreeInfoFromIdentifier(scaling_types, setup.window_scaling_quality);
4878
4879     /* if that fails, set current scaling type to reliable default value */
4880     if (scaling_type_current == NULL)
4881       scaling_type_current =
4882         getTreeInfoFromIdentifier(scaling_types, SCALING_QUALITY_DEFAULT);
4883
4884     /* if that also fails, set current scaling type to first available value */
4885     if (scaling_type_current == NULL)
4886       scaling_type_current = scaling_types;
4887   }
4888
4889   setup.window_scaling_quality = scaling_type_current->identifier;
4890
4891   /* needed for displaying scaling type text instead of identifier */
4892   scaling_type_text = scaling_type_current->name;
4893 }
4894
4895 static void execSetupGraphics_setRenderingModes()
4896 {
4897   if (rendering_modes == NULL)
4898   {
4899     int i;
4900
4901     for (i = 0; rendering_modes_list[i].value != NULL; i++)
4902     {
4903       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4904       char identifier[32], name[32];
4905       char *value = rendering_modes_list[i].value;
4906       char *text = rendering_modes_list[i].text;
4907
4908       ti->node_top = &rendering_modes;
4909       ti->sort_priority = i;
4910
4911       sprintf(identifier, "%s", value);
4912       sprintf(name, "%s", text);
4913
4914       setString(&ti->identifier, identifier);
4915       setString(&ti->name, name);
4916       setString(&ti->name_sorting, name);
4917       setString(&ti->infotext, STR_SETUP_CHOOSE_RENDERING);
4918
4919       pushTreeInfo(&rendering_modes, ti);
4920     }
4921
4922     /* sort rendering mode values to start with lowest rendering mode value */
4923     sortTreeInfo(&rendering_modes);
4924
4925     /* set current rendering mode value to configured rendering mode value */
4926     rendering_mode_current =
4927       getTreeInfoFromIdentifier(rendering_modes, setup.screen_rendering_mode);
4928
4929     /* if that fails, set current rendering mode to reliable default value */
4930     if (rendering_mode_current == NULL)
4931       rendering_mode_current =
4932         getTreeInfoFromIdentifier(rendering_modes,
4933                                   STR_SPECIAL_RENDERING_DEFAULT);
4934
4935     /* if that also fails, set current rendering mode to first available one */
4936     if (rendering_mode_current == NULL)
4937       rendering_mode_current = rendering_modes;
4938   }
4939
4940   setup.screen_rendering_mode = rendering_mode_current->identifier;
4941
4942   /* needed for displaying rendering mode text instead of identifier */
4943   rendering_mode_text = rendering_mode_current->name;
4944 }
4945
4946 static void execSetupGraphics()
4947 {
4948   // update "setup.window_scaling_percent" from list selection
4949   // (in this case, window scaling was changed on setup screen)
4950   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
4951     execSetupGraphics_setWindowSizes(FALSE);
4952
4953   // update list selection from "setup.window_scaling_percent"
4954   // (window scaling may have changed by resizing the window)
4955   execSetupGraphics_setWindowSizes(TRUE);
4956
4957   execSetupGraphics_setScalingTypes();
4958   execSetupGraphics_setRenderingModes();
4959
4960   setup_mode = SETUP_MODE_GRAPHICS;
4961
4962   DrawSetupScreen();
4963
4964 #if defined(TARGET_SDL2)
4965   // window scaling may have changed at this point
4966   ToggleFullscreenOrChangeWindowScalingIfNeeded();
4967
4968   // window scaling quality may have changed at this point
4969   if (!strEqual(setup.window_scaling_quality, video.window_scaling_quality))
4970     SDLSetWindowScalingQuality(setup.window_scaling_quality);
4971
4972   // screen rendering mode may have changed at this point
4973   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
4974 #endif
4975 }
4976
4977 static void execSetupChooseWindowSize()
4978 {
4979   setup_mode = SETUP_MODE_CHOOSE_WINDOW_SIZE;
4980
4981   DrawSetupScreen();
4982 }
4983
4984 static void execSetupChooseScalingType()
4985 {
4986   setup_mode = SETUP_MODE_CHOOSE_SCALING_TYPE;
4987
4988   DrawSetupScreen();
4989 }
4990
4991 static void execSetupChooseRenderingMode()
4992 {
4993   setup_mode = SETUP_MODE_CHOOSE_RENDERING;
4994
4995   DrawSetupScreen();
4996 }
4997
4998 static void execSetupChooseVolumeSimple()
4999 {
5000   setup_mode = SETUP_MODE_CHOOSE_VOLUME_SIMPLE;
5001
5002   DrawSetupScreen();
5003 }
5004
5005 static void execSetupChooseVolumeLoops()
5006 {
5007   setup_mode = SETUP_MODE_CHOOSE_VOLUME_LOOPS;
5008
5009   DrawSetupScreen();
5010 }
5011
5012 static void execSetupChooseVolumeMusic()
5013 {
5014   setup_mode = SETUP_MODE_CHOOSE_VOLUME_MUSIC;
5015
5016   DrawSetupScreen();
5017 }
5018
5019 static void execSetupSound()
5020 {
5021   if (volumes_simple == NULL)
5022   {
5023     boolean current_volume_simple_found = FALSE;
5024     int i;
5025
5026     for (i = 0; volumes_list[i].value != -1; i++)
5027     {
5028       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5029       char identifier[32], name[32];
5030       int value = volumes_list[i].value;
5031       char *text = volumes_list[i].text;
5032
5033       ti->node_top = &volumes_simple;
5034       ti->sort_priority = value;
5035
5036       sprintf(identifier, "%d", value);
5037       sprintf(name, "%s", text);
5038
5039       setString(&ti->identifier, identifier);
5040       setString(&ti->name, name);
5041       setString(&ti->name_sorting, name);
5042       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
5043
5044       pushTreeInfo(&volumes_simple, ti);
5045
5046       if (value == setup.volume_simple)
5047         current_volume_simple_found = TRUE;
5048     }
5049
5050     if (!current_volume_simple_found)
5051     {
5052       // add entry for non-preset volume value
5053
5054       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5055       char identifier[32], name[32];
5056       int value = setup.volume_simple;
5057
5058       ti->node_top = &volumes_simple;
5059       ti->sort_priority = value;
5060
5061       sprintf(identifier, "%d", value);
5062       sprintf(name, "%d %% (Current)", value);
5063
5064       setString(&ti->identifier, identifier);
5065       setString(&ti->name, name);
5066       setString(&ti->name_sorting, name);
5067       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
5068
5069       pushTreeInfo(&volumes_simple, ti);
5070     }
5071
5072     /* sort volume values to start with lowest volume value */
5073     sortTreeInfo(&volumes_simple);
5074
5075     /* set current volume value to configured volume value */
5076     volume_simple_current =
5077       getTreeInfoFromIdentifier(volumes_simple,i_to_a(setup.volume_simple));
5078
5079     /* if that fails, set current volume to reliable default value */
5080     if (volume_simple_current == NULL)
5081       volume_simple_current =
5082         getTreeInfoFromIdentifier(volumes_simple, i_to_a(100));
5083
5084     /* if that also fails, set current volume to first available value */
5085     if (volume_simple_current == NULL)
5086       volume_simple_current = volumes_simple;
5087   }
5088
5089   if (volumes_loops == NULL)
5090   {
5091     boolean current_volume_loops_found = FALSE;
5092     int i;
5093
5094     for (i = 0; volumes_list[i].value != -1; i++)
5095     {
5096       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5097       char identifier[32], name[32];
5098       int value = volumes_list[i].value;
5099       char *text = volumes_list[i].text;
5100
5101       ti->node_top = &volumes_loops;
5102       ti->sort_priority = value;
5103
5104       sprintf(identifier, "%d", value);
5105       sprintf(name, "%s", text);
5106
5107       setString(&ti->identifier, identifier);
5108       setString(&ti->name, name);
5109       setString(&ti->name_sorting, name);
5110       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
5111
5112       pushTreeInfo(&volumes_loops, ti);
5113
5114       if (value == setup.volume_loops)
5115         current_volume_loops_found = TRUE;
5116     }
5117
5118     if (!current_volume_loops_found)
5119     {
5120       // add entry for non-preset volume value
5121
5122       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5123       char identifier[32], name[32];
5124       int value = setup.volume_loops;
5125
5126       ti->node_top = &volumes_loops;
5127       ti->sort_priority = value;
5128
5129       sprintf(identifier, "%d", value);
5130       sprintf(name, "%d %% (Current)", value);
5131
5132       setString(&ti->identifier, identifier);
5133       setString(&ti->name, name);
5134       setString(&ti->name_sorting, name);
5135       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
5136
5137       pushTreeInfo(&volumes_loops, ti);
5138     }
5139
5140     /* sort volume values to start with lowest volume value */
5141     sortTreeInfo(&volumes_loops);
5142
5143     /* set current volume value to configured volume value */
5144     volume_loops_current =
5145       getTreeInfoFromIdentifier(volumes_loops,i_to_a(setup.volume_loops));
5146
5147     /* if that fails, set current volume to reliable default value */
5148     if (volume_loops_current == NULL)
5149       volume_loops_current =
5150         getTreeInfoFromIdentifier(volumes_loops, i_to_a(100));
5151
5152     /* if that also fails, set current volume to first available value */
5153     if (volume_loops_current == NULL)
5154       volume_loops_current = volumes_loops;
5155   }
5156
5157   if (volumes_music == NULL)
5158   {
5159     boolean current_volume_music_found = FALSE;
5160     int i;
5161
5162     for (i = 0; volumes_list[i].value != -1; i++)
5163     {
5164       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5165       char identifier[32], name[32];
5166       int value = volumes_list[i].value;
5167       char *text = volumes_list[i].text;
5168
5169       ti->node_top = &volumes_music;
5170       ti->sort_priority = value;
5171
5172       sprintf(identifier, "%d", value);
5173       sprintf(name, "%s", text);
5174
5175       setString(&ti->identifier, identifier);
5176       setString(&ti->name, name);
5177       setString(&ti->name_sorting, name);
5178       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
5179
5180       pushTreeInfo(&volumes_music, ti);
5181
5182       if (value == setup.volume_music)
5183         current_volume_music_found = TRUE;
5184     }
5185
5186     if (!current_volume_music_found)
5187     {
5188       // add entry for non-preset volume value
5189
5190       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5191       char identifier[32], name[32];
5192       int value = setup.volume_music;
5193
5194       ti->node_top = &volumes_music;
5195       ti->sort_priority = value;
5196
5197       sprintf(identifier, "%d", value);
5198       sprintf(name, "%d %% (Current)", value);
5199
5200       setString(&ti->identifier, identifier);
5201       setString(&ti->name, name);
5202       setString(&ti->name_sorting, name);
5203       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
5204
5205       pushTreeInfo(&volumes_music, ti);
5206     }
5207
5208     /* sort volume values to start with lowest volume value */
5209     sortTreeInfo(&volumes_music);
5210
5211     /* set current volume value to configured volume value */
5212     volume_music_current =
5213       getTreeInfoFromIdentifier(volumes_music,i_to_a(setup.volume_music));
5214
5215     /* if that fails, set current volume to reliable default value */
5216     if (volume_music_current == NULL)
5217       volume_music_current =
5218         getTreeInfoFromIdentifier(volumes_music, i_to_a(100));
5219
5220     /* if that also fails, set current volume to first available value */
5221     if (volume_music_current == NULL)
5222       volume_music_current = volumes_music;
5223   }
5224
5225   setup.volume_simple = atoi(volume_simple_current->identifier);
5226   setup.volume_loops  = atoi(volume_loops_current->identifier);
5227   setup.volume_music  = atoi(volume_music_current->identifier);
5228
5229   /* needed for displaying volume text instead of identifier */
5230   volume_simple_text = volume_simple_current->name;
5231   volume_loops_text = volume_loops_current->name;
5232   volume_music_text = volume_music_current->name;
5233
5234   setup_mode = SETUP_MODE_SOUND;
5235
5236   DrawSetupScreen();
5237 }
5238
5239 static void execSetupChooseTouchControls()
5240 {
5241   setup_mode = SETUP_MODE_CHOOSE_TOUCH_CONTROL;
5242
5243   DrawSetupScreen();
5244 }
5245
5246 static void execSetupChooseMoveDistance()
5247 {
5248   setup_mode = SETUP_MODE_CHOOSE_MOVE_DISTANCE;
5249
5250   DrawSetupScreen();
5251 }
5252
5253 static void execSetupChooseDropDistance()
5254 {
5255   setup_mode = SETUP_MODE_CHOOSE_DROP_DISTANCE;
5256
5257   DrawSetupScreen();
5258 }
5259
5260 static void execSetupTouch()
5261 {
5262   if (touch_controls == NULL)
5263   {
5264     int i;
5265
5266     for (i = 0; touch_controls_list[i].value != NULL; i++)
5267     {
5268       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5269       char identifier[32], name[32];
5270       char *value = touch_controls_list[i].value;
5271       char *text = touch_controls_list[i].text;
5272
5273       ti->node_top = &touch_controls;
5274       ti->sort_priority = i;
5275
5276       sprintf(identifier, "%s", value);
5277       sprintf(name, "%s", text);
5278
5279       setString(&ti->identifier, identifier);
5280       setString(&ti->name, name);
5281       setString(&ti->name_sorting, name);
5282       setString(&ti->infotext, STR_SETUP_CHOOSE_TOUCH_CONTROL);
5283
5284       pushTreeInfo(&touch_controls, ti);
5285     }
5286
5287     /* sort touch control values to start with lowest touch control value */
5288     sortTreeInfo(&touch_controls);
5289
5290     /* set current touch control value to configured touch control value */
5291     touch_control_current =
5292       getTreeInfoFromIdentifier(touch_controls, setup.touch.control_type);
5293
5294     /* if that fails, set current touch control to reliable default value */
5295     if (touch_control_current == NULL)
5296       touch_control_current =
5297         getTreeInfoFromIdentifier(touch_controls, TOUCH_CONTROL_DEFAULT);
5298
5299     /* if that also fails, set current touch control to first available value */
5300     if (touch_control_current == NULL)
5301       touch_control_current = touch_controls;
5302   }
5303
5304   if (move_distances == NULL)
5305   {
5306     int i;
5307
5308     for (i = 0; distances_list[i].value != -1; i++)
5309     {
5310       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5311       char identifier[32], name[32];
5312       int value = distances_list[i].value;
5313       char *text = distances_list[i].text;
5314
5315       ti->node_top = &move_distances;
5316       ti->sort_priority = value;
5317
5318       sprintf(identifier, "%d", value);
5319       sprintf(name, "%s", text);
5320
5321       setString(&ti->identifier, identifier);
5322       setString(&ti->name, name);
5323       setString(&ti->name_sorting, name);
5324       setString(&ti->infotext, STR_SETUP_CHOOSE_MOVE_DISTANCE);
5325
5326       pushTreeInfo(&move_distances, ti);
5327     }
5328
5329     /* sort distance values to start with lowest distance value */
5330     sortTreeInfo(&move_distances);
5331
5332     /* set current distance value to configured distance value */
5333     move_distance_current =
5334       getTreeInfoFromIdentifier(move_distances,
5335                                 i_to_a(setup.touch.move_distance));
5336
5337     /* if that fails, set current distance to reliable default value */
5338     if (move_distance_current == NULL)
5339       move_distance_current =
5340         getTreeInfoFromIdentifier(move_distances, i_to_a(1));
5341
5342     /* if that also fails, set current distance to first available value */
5343     if (move_distance_current == NULL)
5344       move_distance_current = move_distances;
5345   }
5346
5347   if (drop_distances == NULL)
5348   {
5349     int i;
5350
5351     for (i = 0; distances_list[i].value != -1; i++)
5352     {
5353       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5354       char identifier[32], name[32];
5355       int value = distances_list[i].value;
5356       char *text = distances_list[i].text;
5357
5358       ti->node_top = &drop_distances;
5359       ti->sort_priority = value;
5360
5361       sprintf(identifier, "%d", value);
5362       sprintf(name, "%s", text);
5363
5364       setString(&ti->identifier, identifier);
5365       setString(&ti->name, name);
5366       setString(&ti->name_sorting, name);
5367       setString(&ti->infotext, STR_SETUP_CHOOSE_DROP_DISTANCE);
5368
5369       pushTreeInfo(&drop_distances, ti);
5370     }
5371
5372     /* sort distance values to start with lowest distance value */
5373     sortTreeInfo(&drop_distances);
5374
5375     /* set current distance value to configured distance value */
5376     drop_distance_current =
5377       getTreeInfoFromIdentifier(drop_distances,
5378                                 i_to_a(setup.touch.drop_distance));
5379
5380     /* if that fails, set current distance to reliable default value */
5381     if (drop_distance_current == NULL)
5382       drop_distance_current =
5383         getTreeInfoFromIdentifier(drop_distances, i_to_a(1));
5384
5385     /* if that also fails, set current distance to first available value */
5386     if (drop_distance_current == NULL)
5387       drop_distance_current = drop_distances;
5388   }
5389
5390   setup.touch.control_type = touch_control_current->identifier;
5391   setup.touch.move_distance = atoi(move_distance_current->identifier);
5392   setup.touch.drop_distance = atoi(drop_distance_current->identifier);
5393
5394   /* needed for displaying volume text instead of identifier */
5395   touch_controls_text = touch_control_current->name;
5396   move_distance_text = move_distance_current->name;
5397   drop_distance_text = drop_distance_current->name;
5398
5399   setup_mode = SETUP_MODE_TOUCH;
5400
5401   DrawSetupScreen();
5402 }
5403
5404 static void execSetupArtwork()
5405 {
5406 #if 0
5407   printf("::: '%s', '%s', '%s'\n",
5408          artwork.gfx_current->subdir,
5409          artwork.gfx_current->fullpath,
5410          artwork.gfx_current->basepath);
5411 #endif
5412
5413   setup.graphics_set = artwork.gfx_current->identifier;
5414   setup.sounds_set = artwork.snd_current->identifier;
5415   setup.music_set = artwork.mus_current->identifier;
5416
5417   /* needed if last screen (setup choice) changed graphics, sounds or music */
5418   ReloadCustomArtwork(0);
5419
5420   /* needed for displaying artwork name instead of artwork identifier */
5421   graphics_set_name = artwork.gfx_current->name;
5422   sounds_set_name = artwork.snd_current->name;
5423   music_set_name = artwork.mus_current->name;
5424
5425   setup_mode = SETUP_MODE_ARTWORK;
5426
5427   DrawSetupScreen();
5428 }
5429
5430 static void execSetupChooseGraphics()
5431 {
5432   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
5433
5434   DrawSetupScreen();
5435 }
5436
5437 static void execSetupChooseSounds()
5438 {
5439   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
5440
5441   DrawSetupScreen();
5442 }
5443
5444 static void execSetupChooseMusic()
5445 {
5446   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
5447
5448   DrawSetupScreen();
5449 }
5450
5451 static void execSetupInput()
5452 {
5453   setup_mode = SETUP_MODE_INPUT;
5454
5455   DrawSetupScreen();
5456 }
5457
5458 static void execSetupShortcuts()
5459 {
5460   setup_mode = SETUP_MODE_SHORTCUTS;
5461
5462   DrawSetupScreen();
5463 }
5464
5465 static void execSetupShortcuts1()
5466 {
5467   setup_mode = SETUP_MODE_SHORTCUTS_1;
5468
5469   DrawSetupScreen();
5470 }
5471
5472 static void execSetupShortcuts2()
5473 {
5474   setup_mode = SETUP_MODE_SHORTCUTS_2;
5475
5476   DrawSetupScreen();
5477 }
5478
5479 static void execSetupShortcuts3()
5480 {
5481   setup_mode = SETUP_MODE_SHORTCUTS_3;
5482
5483   DrawSetupScreen();
5484 }
5485
5486 static void execSetupShortcuts4()
5487 {
5488   setup_mode = SETUP_MODE_SHORTCUTS_4;
5489
5490   DrawSetupScreen();
5491 }
5492
5493 static void execSetupShortcuts5()
5494 {
5495   setup_mode = SETUP_MODE_SHORTCUTS_5;
5496
5497   DrawSetupScreen();
5498 }
5499
5500 static void execExitSetup()
5501 {
5502   SetGameStatus(GAME_MODE_MAIN);
5503
5504   DrawMainMenu();
5505 }
5506
5507 static void execSaveAndExitSetup()
5508 {
5509   SaveSetup();
5510   execExitSetup();
5511 }
5512
5513 static struct
5514 {
5515   void *value;
5516   void *related_value;
5517 } hide_related_entry_list[] =
5518 {
5519   { &setup.game_frame_delay,            execSetupChooseGameSpeed        },
5520   { &setup.game_frame_delay,            &game_speed_text                },
5521
5522   { &setup.scroll_delay_value,          execSetupChooseScrollDelay      },
5523   { &setup.scroll_delay_value,          &scroll_delay_text              },
5524
5525   { &setup.engine_snapshot_mode,        execSetupChooseSnapshotMode     },
5526   { &setup.engine_snapshot_mode,        &snapshot_mode_text             },
5527
5528   { &setup.window_scaling_percent,      execSetupChooseWindowSize       },
5529   { &setup.window_scaling_percent,      &window_size_text               },
5530
5531   { &setup.window_scaling_quality,      execSetupChooseScalingType      },
5532   { &setup.window_scaling_quality,      &scaling_type_text              },
5533
5534   { &setup.screen_rendering_mode,       execSetupChooseRenderingMode    },
5535   { &setup.screen_rendering_mode,       &rendering_mode_text            },
5536
5537   { &setup.graphics_set,                execSetupChooseGraphics         },
5538   { &setup.graphics_set,                &graphics_set_name              },
5539
5540   { &setup.sounds_set,                  execSetupChooseSounds           },
5541   { &setup.sounds_set,                  &sounds_set_name                },
5542
5543   { &setup.music_set,                   execSetupChooseMusic            },
5544   { &setup.music_set,                   &music_set_name                 },
5545
5546   { &setup.volume_simple,               execSetupChooseVolumeSimple     },
5547   { &setup.volume_simple,               &volume_simple_text             },
5548
5549   { &setup.volume_loops,                execSetupChooseVolumeLoops      },
5550   { &setup.volume_loops,                &volume_loops_text              },
5551
5552   { &setup.volume_music,                execSetupChooseVolumeMusic      },
5553   { &setup.volume_music,                &volume_music_text              },
5554
5555   { &setup.touch.control_type,          execSetupChooseTouchControls    },
5556   { &setup.touch.control_type,          &touch_controls_text            },
5557
5558   { &setup.touch.move_distance,         execSetupChooseMoveDistance     },
5559   { &setup.touch.move_distance,         &move_distance_text             },
5560
5561   { &setup.touch.drop_distance,         execSetupChooseDropDistance     },
5562   { &setup.touch.drop_distance,         &drop_distance_text             },
5563
5564   { NULL,                               NULL                            }
5565 };
5566
5567 void setHideRelatedSetupEntries()
5568 {
5569   int i;
5570
5571   for (i = 0; hide_related_entry_list[i].value != NULL; i++)
5572     if (hideSetupEntry(hide_related_entry_list[i].value))
5573       setHideSetupEntry(hide_related_entry_list[i].related_value);
5574 }
5575
5576 static struct TokenInfo setup_info_main[] =
5577 {
5578   { TYPE_ENTER_MENU,    execSetupGame,          STR_SETUP_GAME          },
5579   { TYPE_ENTER_MENU,    execSetupEditor,        STR_SETUP_EDITOR        },
5580   { TYPE_ENTER_MENU,    execSetupGraphics,      STR_SETUP_GRAPHICS      },
5581   { TYPE_ENTER_MENU,    execSetupSound,         STR_SETUP_SOUND         },
5582   { TYPE_ENTER_MENU,    execSetupArtwork,       STR_SETUP_ARTWORK       },
5583   { TYPE_ENTER_MENU,    execSetupInput,         STR_SETUP_INPUT         },
5584   { TYPE_ENTER_MENU,    execSetupTouch,         STR_SETUP_TOUCH         },
5585   { TYPE_ENTER_MENU,    execSetupShortcuts,     STR_SETUP_SHORTCUTS     },
5586   { TYPE_EMPTY,         NULL,                   ""                      },
5587   { TYPE_LEAVE_MENU,    execExitSetup,          STR_SETUP_EXIT          },
5588   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   STR_SETUP_SAVE_AND_EXIT },
5589
5590   { 0,                  NULL,                   NULL                    }
5591 };
5592
5593 static struct TokenInfo setup_info_game[] =
5594 {
5595   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
5596   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
5597   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
5598   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
5599   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
5600   { TYPE_SWITCH,        &setup.increment_levels,"Increment Solved Levels:" },
5601   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
5602   { TYPE_ENTER_LIST,    execSetupChooseGameSpeed, "Game Speed:"         },
5603   { TYPE_STRING,        &game_speed_text,       ""                      },
5604 #if 1
5605   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay, "Scroll Delay:"     },
5606   { TYPE_STRING,        &scroll_delay_text,     ""                      },
5607 #endif
5608   { TYPE_ENTER_LIST, execSetupChooseSnapshotMode,"Game Engine Snapshot Mode:" },
5609   { TYPE_STRING,        &snapshot_mode_text,    ""                      },
5610   { TYPE_SWITCH,        &setup.show_snapshot_buttons,"Show Snapshot Buttons:" },
5611   { TYPE_EMPTY,         NULL,                   ""                      },
5612   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5613
5614   { 0,                  NULL,                   NULL                    }
5615 };
5616
5617 static struct TokenInfo setup_info_editor[] =
5618 {
5619 #if 0
5620   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
5621   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
5622   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
5623   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
5624   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
5625   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
5626   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
5627   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
5628   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
5629   { TYPE_SWITCH, &setup.editor.el_steel_chars, "Text Characters (Steel):" },
5630 #endif
5631   { TYPE_SWITCH,        &setup.editor.el_classic,  "Classic Elements:" },
5632   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
5633 #if 0
5634   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
5635 #endif
5636   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
5637   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
5638   { TYPE_EMPTY,         NULL,                   ""                      },
5639 #if 0
5640   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
5641   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
5642   { TYPE_EMPTY,         NULL,                   ""                      },
5643 #endif
5644   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
5645   { TYPE_EMPTY,         NULL,                   ""                      },
5646   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5647
5648   { 0,                  NULL,                   NULL                    }
5649 };
5650
5651 static struct TokenInfo setup_info_graphics[] =
5652 {
5653 #if defined(TARGET_SDL2) && !defined(PLATFORM_ANDROID)
5654   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
5655   { TYPE_ENTER_LIST,    execSetupChooseWindowSize, "Window Scaling:"    },
5656   { TYPE_STRING,        &window_size_text,      ""                      },
5657   { TYPE_ENTER_LIST,    execSetupChooseScalingType, "Anti-Aliasing:"    },
5658   { TYPE_STRING,        &scaling_type_text,     ""                      },
5659   { TYPE_ENTER_LIST,    execSetupChooseRenderingMode, "Special Rendering:" },
5660   { TYPE_STRING,        &rendering_mode_text,   ""                      },
5661 #endif
5662 #if 0
5663   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay, "Scroll Delay:"     },
5664   { TYPE_STRING,        &scroll_delay_text,     ""                      },
5665 #endif
5666   { TYPE_SWITCH,        &setup.fade_screens,    "Fade Screens:"         },
5667   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
5668   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
5669   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
5670   { TYPE_SWITCH,        &setup.toons,           "Show Menu Animations:" },
5671   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
5672   { TYPE_SWITCH, &setup.sp_show_border_elements,"Supaplex Border Elements:" },
5673   { TYPE_SWITCH,        &setup.small_game_graphics, "Small Game Graphics:" },
5674   { TYPE_EMPTY,         NULL,                   ""                      },
5675   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5676
5677   { 0,                  NULL,                   NULL                    }
5678 };
5679
5680 static struct TokenInfo setup_info_sound[] =
5681 {
5682   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
5683   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
5684   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
5685   { TYPE_EMPTY,         NULL,                   ""                      },
5686   { TYPE_ENTER_LIST,    execSetupChooseVolumeSimple, "Sound Volume (Normal):" },
5687   { TYPE_STRING,        &volume_simple_text,    ""                      },
5688   { TYPE_ENTER_LIST,    execSetupChooseVolumeLoops, "Sound Volume (Looping):" },
5689   { TYPE_STRING,        &volume_loops_text,     ""                      },
5690   { TYPE_ENTER_LIST,    execSetupChooseVolumeMusic, "Music Volume:"     },
5691   { TYPE_STRING,        &volume_music_text,     ""                      },
5692   { TYPE_EMPTY,         NULL,                   ""                      },
5693   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5694
5695   { 0,                  NULL,                   NULL                    }
5696 };
5697
5698 static struct TokenInfo setup_info_artwork[] =
5699 {
5700   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
5701   { TYPE_STRING,        &graphics_set_name,     ""                      },
5702   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
5703   { TYPE_STRING,        &sounds_set_name,       ""                      },
5704   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
5705   { TYPE_STRING,        &music_set_name,        ""                      },
5706   { TYPE_EMPTY,         NULL,                   ""                      },
5707   { TYPE_YES_NO_AUTO,&setup.override_level_graphics,"Override Level Graphics:"},
5708   { TYPE_YES_NO_AUTO,&setup.override_level_sounds,  "Override Level Sounds:"  },
5709   { TYPE_YES_NO_AUTO,&setup.override_level_music,   "Override Level Music:"   },
5710   { TYPE_EMPTY,         NULL,                   ""                      },
5711   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5712
5713   { 0,                  NULL,                   NULL                    }
5714 };
5715
5716 static struct TokenInfo setup_info_input[] =
5717 {
5718   { TYPE_SWITCH,        NULL,                   "Player:"               },
5719   { TYPE_SWITCH,        NULL,                   "Device:"               },
5720   { TYPE_SWITCH,        NULL,                   ""                      },
5721   { TYPE_SKIPPABLE,     NULL,                   ""                      },
5722   { TYPE_EMPTY,         NULL,                   ""                      },
5723   { TYPE_EMPTY,         NULL,                   ""                      },
5724   { TYPE_EMPTY,         NULL,                   ""                      },
5725   { TYPE_EMPTY,         NULL,                   ""                      },
5726   { TYPE_EMPTY,         NULL,                   ""                      },
5727   { TYPE_EMPTY,         NULL,                   ""                      },
5728   { TYPE_EMPTY,         NULL,                   ""                      },
5729   { TYPE_EMPTY,         NULL,                   ""                      },
5730   { TYPE_SKIPPABLE,     NULL,                   ""                      },
5731   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5732
5733   { 0,                  NULL,                   NULL                    }
5734 };
5735
5736 static struct TokenInfo setup_info_touch[] =
5737 {
5738   { TYPE_ENTER_LIST,    execSetupChooseTouchControls, "Touch Control Type:" },
5739   { TYPE_STRING,        &touch_controls_text,   ""                      },
5740   { TYPE_EMPTY,         NULL,                   ""                      },
5741   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5742
5743   { 0,                  NULL,                   NULL                    }
5744 };
5745
5746 static struct TokenInfo setup_info_touch_wipe_gestures[] =
5747 {
5748   { TYPE_ENTER_LIST,    execSetupChooseTouchControls, "Touch Control Type:" },
5749   { TYPE_STRING,        &touch_controls_text,   ""                      },
5750   { TYPE_EMPTY,         NULL,                   ""                      },
5751   { TYPE_ENTER_LIST,    execSetupChooseMoveDistance, "Move Trigger Distance:" },
5752   { TYPE_STRING,        &move_distance_text,    ""                      },
5753   { TYPE_ENTER_LIST,    execSetupChooseDropDistance, "Drop Trigger Distance:" },
5754   { TYPE_STRING,        &drop_distance_text,    ""                      },
5755   { TYPE_EMPTY,         NULL,                   ""                      },
5756   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5757
5758   { 0,                  NULL,                   NULL                    }
5759 };
5760
5761 static struct TokenInfo setup_info_shortcuts[] =
5762 {
5763   { TYPE_ENTER_MENU,    execSetupShortcuts1,    "Various Keys"          },
5764   { TYPE_ENTER_MENU,    execSetupShortcuts2,    "Player Focus"          },
5765   { TYPE_ENTER_MENU,    execSetupShortcuts3,    "Tape Buttons"          },
5766   { TYPE_ENTER_MENU,    execSetupShortcuts4,    "Sound & Music"         },
5767   { TYPE_ENTER_MENU,    execSetupShortcuts5,    "TAS Snap Keys"         },
5768   { TYPE_EMPTY,         NULL,                   ""                      },
5769   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5770
5771   { 0,                  NULL,                   NULL                    }
5772 };
5773
5774 static struct TokenInfo setup_info_shortcuts_1[] =
5775 {
5776   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
5777   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
5778   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
5779   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
5780   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
5781   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
5782   { TYPE_EMPTY,         NULL,                   ""                      },
5783   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
5784   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
5785   { TYPE_EMPTY,         NULL,                   ""                      },
5786   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
5787
5788   { 0,                  NULL,                   NULL                    }
5789 };
5790
5791 static struct TokenInfo setup_info_shortcuts_2[] =
5792 {
5793   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
5794   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
5795   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
5796   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
5797   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
5798   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
5799   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
5800   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
5801   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
5802   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
5803   { TYPE_EMPTY,         NULL,                   ""                      },
5804   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
5805
5806   { 0,                  NULL,                   NULL                    }
5807 };
5808
5809 static struct TokenInfo setup_info_shortcuts_3[] =
5810 {
5811   { TYPE_KEYTEXT,       NULL,                   "Eject Tape:",          },
5812   { TYPE_KEY,           &setup.shortcut.tape_eject, ""                  },
5813   { TYPE_KEYTEXT,       NULL,                   "Warp / Single Step:",  },
5814   { TYPE_KEY,           &setup.shortcut.tape_extra, ""                  },
5815   { TYPE_KEYTEXT,       NULL,                   "Stop Tape:",           },
5816   { TYPE_KEY,           &setup.shortcut.tape_stop, ""                   },
5817   { TYPE_KEYTEXT,       NULL,                   "Pause / Unpause Tape:",},
5818   { TYPE_KEY,           &setup.shortcut.tape_pause, ""                  },
5819   { TYPE_KEYTEXT,       NULL,                   "Record Tape:",         },
5820   { TYPE_KEY,           &setup.shortcut.tape_record, ""                 },
5821   { TYPE_KEYTEXT,       NULL,                   "Play Tape:",           },
5822   { TYPE_KEY,           &setup.shortcut.tape_play, ""                   },
5823   { TYPE_EMPTY,         NULL,                   ""                      },
5824   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
5825
5826   { 0,                  NULL,                   NULL                    }
5827 };
5828
5829 static struct TokenInfo setup_info_shortcuts_4[] =
5830 {
5831   { TYPE_KEYTEXT,       NULL,           "Toggle Sound Effects (Normal):", },
5832   { TYPE_KEY,           &setup.shortcut.sound_simple, ""                },
5833   { TYPE_KEYTEXT,       NULL,           "Toggle Sound Effects (Looping):", },
5834   { TYPE_KEY,           &setup.shortcut.sound_loops, ""                 },
5835   { TYPE_KEYTEXT,       NULL,           "Toggle Music:",                },
5836   { TYPE_KEY,           &setup.shortcut.sound_music, ""                 },
5837   { TYPE_EMPTY,         NULL,                   ""                      },
5838   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
5839
5840   { 0,                  NULL,                   NULL                    }
5841 };
5842
5843 static struct TokenInfo setup_info_shortcuts_5[] =
5844 {
5845   { TYPE_KEYTEXT,       NULL,                   "Snap Left:",           },
5846   { TYPE_KEY,           &setup.shortcut.snap_left, ""                   },
5847   { TYPE_KEYTEXT,       NULL,                   "Snap Right:",          },
5848   { TYPE_KEY,           &setup.shortcut.snap_right, ""                  },
5849   { TYPE_KEYTEXT,       NULL,                   "Snap Up:",             },
5850   { TYPE_KEY,           &setup.shortcut.snap_up, ""                     },
5851   { TYPE_KEYTEXT,       NULL,                   "Snap Down:",           },
5852   { TYPE_KEY,           &setup.shortcut.snap_down, ""                   },
5853   { TYPE_EMPTY,         NULL,                   ""                      },
5854   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
5855
5856   { 0,                  NULL,                   NULL                    }
5857 };
5858
5859 static Key getSetupKey()
5860 {
5861   Key key = KSYM_UNDEFINED;
5862   boolean got_key_event = FALSE;
5863
5864   while (!got_key_event)
5865   {
5866     Event event;
5867
5868     if (NextValidEvent(&event))
5869     {
5870       switch (event.type)
5871       {
5872         case EVENT_KEYPRESS:
5873           {
5874             key = GetEventKey((KeyEvent *)&event, TRUE);
5875
5876             /* press 'Escape' or 'Enter' to keep the existing key binding */
5877             if (key == KSYM_Escape || key == KSYM_Return)
5878               key = KSYM_UNDEFINED;     /* keep old value */
5879
5880             got_key_event = TRUE;
5881           }
5882           break;
5883
5884         case EVENT_KEYRELEASE:
5885           key_joystick_mapping = 0;
5886           break;
5887
5888         default:
5889           HandleOtherEvents(&event);
5890           break;
5891       }
5892     }
5893
5894     BackToFront();
5895   }
5896
5897   return key;
5898 }
5899
5900 static int getSetupValueFont(int type, void *value)
5901 {
5902   if (type & TYPE_GHOSTED)
5903     return FONT_OPTION_OFF;
5904   else if (type & TYPE_KEY)
5905     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
5906   else if (type & TYPE_STRING)
5907     return FONT_VALUE_2;
5908   else if (type & TYPE_ECS_AGA)
5909     return FONT_VALUE_1;
5910   else if (type & TYPE_BOOLEAN_STYLE)
5911     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
5912   else if (type & TYPE_YES_NO_AUTO)
5913     return (*(int *)value == AUTO  ? FONT_OPTION_ON :
5914             *(int *)value == FALSE ? FONT_OPTION_OFF : FONT_OPTION_ON);
5915   else
5916     return FONT_VALUE_1;
5917 }
5918
5919 static int getSetupValueFontNarrow(int type, int font_nr)
5920 {
5921   return (font_nr == FONT_VALUE_1    ? FONT_VALUE_NARROW :
5922           font_nr == FONT_OPTION_ON  ? FONT_OPTION_ON_NARROW :
5923           font_nr == FONT_OPTION_OFF ? FONT_OPTION_OFF_NARROW :
5924           font_nr);
5925 }
5926
5927 static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
5928 {
5929   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
5930   struct TokenInfo *si = &setup_info[si_pos];
5931   boolean font_draw_xoffset_modified = FALSE;
5932   boolean scrollbar_needed = (num_setup_info < max_setup_info);
5933   int font_draw_xoffset_old = -1;
5934   int xoffset = (scrollbar_needed ? -1 : 0);
5935   int menu_screen_value_xpos = MENU_SCREEN_VALUE_XPOS + xoffset;
5936   int menu_screen_max_xpos = MENU_SCREEN_MAX_XPOS + xoffset;
5937   int xpos = menu_screen_value_xpos;
5938   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
5939   int startx = mSX + xpos * 32;
5940   int starty = mSY + ypos * 32;
5941   int font_nr, font_nr_default, font_width_default;
5942   int type = si->type;
5943   void *value = si->value;
5944   char *value_string = getSetupValue(type, value);
5945   int i;
5946
5947   if (value_string == NULL)
5948     return;
5949
5950   if (type & TYPE_KEY)
5951   {
5952     xpos = MENU_SCREEN_START_XPOS;
5953
5954     if (type & TYPE_QUERY)
5955       value_string = "<press key>";
5956   }
5957   else if (type & TYPE_STRING)
5958   {
5959     int max_value_len = (SCR_FIELDX - 2) * 2;
5960
5961     xpos = MENU_SCREEN_START_XPOS;
5962
5963     if (strlen(value_string) > max_value_len)
5964       value_string[max_value_len] = '\0';
5965   }
5966   else if (type & TYPE_YES_NO_AUTO)
5967   {
5968     xpos = menu_screen_value_xpos - 1;
5969   }
5970
5971   startx = mSX + xpos * 32;
5972   starty = mSY + ypos * 32;
5973   font_nr_default = getSetupValueFont(type, value);
5974   font_width_default = getFontWidth(font_nr_default);
5975
5976   font_nr = font_nr_default;
5977
5978   // special check if right-side setup values moved left due to scrollbar
5979   if (scrollbar_needed && xpos > MENU_SCREEN_START_XPOS)
5980   {
5981     int max_menu_text_length = 26;      // maximum text length for classic menu
5982     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
5983     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
5984     int text_font_nr = getMenuTextFont(FONT_MENU_2);
5985     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
5986     int text_width = max_menu_text_length * getFontWidth(text_font_nr);
5987
5988     if (startx + font_xoffset < text_startx + text_width + text_font_xoffset)
5989     {
5990       xpos += 1;
5991       startx = mSX + xpos * 32;
5992
5993       font_nr = getSetupValueFontNarrow(type, font_nr);
5994     }
5995   }
5996
5997   /* downward compatibility correction for Juergen Bonhagen's menu settings */
5998   if (setup_mode != SETUP_MODE_INPUT)
5999   {
6000     int max_menu_text_length_big = (menu_screen_value_xpos -
6001                                     MENU_SCREEN_START_XPOS);
6002     int max_menu_text_length_medium = max_menu_text_length_big * 2;
6003     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
6004     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
6005     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
6006     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
6007     int text_font_nr = getMenuTextFont(FONT_MENU_2);
6008     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
6009     int text_width = max_menu_text_length_medium * getFontWidth(text_font_nr);
6010     boolean correct_font_draw_xoffset = FALSE;
6011
6012     if (xpos == MENU_SCREEN_START_XPOS &&
6013         startx + font1_xoffset < text_startx + text_font_xoffset)
6014       correct_font_draw_xoffset = TRUE;
6015
6016     if (xpos == menu_screen_value_xpos &&
6017         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
6018       correct_font_draw_xoffset = TRUE;
6019
6020     /* check if setup value would overlap with setup text when printed */
6021     /* (this can happen for extreme/wrong values for font draw offset) */
6022     if (correct_font_draw_xoffset)
6023     {
6024       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
6025       font_draw_xoffset_modified = TRUE;
6026
6027       if (type & TYPE_KEY)
6028         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
6029       else if (!(type & TYPE_STRING))
6030         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
6031           max_menu_text_length_medium * (16 - getFontWidth(text_font_nr));
6032     }
6033   }
6034
6035   for (i = 0; i <= menu_screen_max_xpos - xpos; i++)
6036     DrawText(startx + i * font_width_default, starty, " ", font_nr_default);
6037
6038   DrawText(startx, starty, value_string, font_nr);
6039
6040   if (font_draw_xoffset_modified)
6041     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
6042 }
6043
6044 static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
6045 {
6046   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
6047   struct TokenInfo *si = &setup_info[si_pos];
6048
6049   if (si->type & TYPE_BOOLEAN_STYLE)
6050   {
6051     *(boolean *)si->value ^= TRUE;
6052   }
6053   else if (si->type & TYPE_YES_NO_AUTO)
6054   {
6055     *(int *)si->value =
6056       (dx == -1 ?
6057        (*(int *)si->value == AUTO ? TRUE :
6058         *(int *)si->value == TRUE ? FALSE : AUTO) :
6059        (*(int *)si->value == TRUE ? AUTO :
6060         *(int *)si->value == AUTO ? FALSE : TRUE));
6061   }
6062   else if (si->type & TYPE_KEY)
6063   {
6064     Key key;
6065
6066     si->type |= TYPE_QUERY;
6067     drawSetupValue(screen_pos, setup_info_pos_raw);
6068     si->type &= ~TYPE_QUERY;
6069
6070     key = getSetupKey();
6071     if (key != KSYM_UNDEFINED)
6072       *(Key *)si->value = key;
6073   }
6074
6075   drawSetupValue(screen_pos, setup_info_pos_raw);
6076
6077   // fullscreen state may have changed at this point
6078   if (si->value == &setup.fullscreen)
6079     ToggleFullscreenOrChangeWindowScalingIfNeeded();
6080 }
6081
6082 static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *setup_info_orig)
6083 {
6084   static struct TokenInfo *setup_info_final = NULL;
6085   int list_size = 0;
6086   int list_pos = 0;
6087   int i;
6088
6089   /* determine maximum list size of target list */
6090   while (setup_info_orig[list_size++].type != 0);
6091
6092   /* free, allocate and clear memory for target list */
6093   checked_free(setup_info_final);
6094   setup_info_final = checked_calloc(list_size * sizeof(struct TokenInfo));
6095
6096   /* copy setup info list without setup entries marked as hidden */
6097   for (i = 0; setup_info_orig[i].type != 0; i++)
6098   {
6099     /* skip setup entries configured to be hidden */
6100     if (hideSetupEntry(setup_info_orig[i].value))
6101       continue;
6102
6103     /* skip skippable setup entries if screen is lower than usual */
6104     if (SCR_FIELDY < SCR_FIELDY_DEFAULT &&
6105         setup_info_orig[i].type == TYPE_SKIPPABLE)
6106       continue;
6107
6108     setup_info_final[list_pos++] = setup_info_orig[i];
6109   }
6110
6111   return setup_info_final;
6112 }
6113
6114 static void DrawSetupScreen_Generic()
6115 {
6116   int fade_mask = REDRAW_FIELD;
6117   boolean redraw_all = FALSE;
6118   char *title_string = NULL;
6119   int i;
6120
6121   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
6122     fade_mask = REDRAW_ALL;
6123
6124   UnmapAllGadgets();
6125   FadeMenuSoundsAndMusic();
6126
6127   FreeScreenGadgets();
6128   CreateScreenGadgets();
6129
6130   if (redraw_mask & REDRAW_ALL)
6131     redraw_all = TRUE;
6132
6133   FadeOut(fade_mask);
6134
6135   /* needed if different viewport properties defined for setup screen */
6136   ChangeViewportPropertiesIfNeeded();
6137
6138   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
6139
6140   ClearField();
6141
6142   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
6143
6144   if (setup_mode == SETUP_MODE_MAIN)
6145   {
6146     setup_info = setup_info_main;
6147     title_string = STR_SETUP_MAIN;
6148   }
6149   else if (setup_mode == SETUP_MODE_GAME)
6150   {
6151     setup_info = setup_info_game;
6152     title_string = STR_SETUP_GAME;
6153   }
6154   else if (setup_mode == SETUP_MODE_EDITOR)
6155   {
6156     setup_info = setup_info_editor;
6157     title_string = STR_SETUP_EDITOR;
6158   }
6159   else if (setup_mode == SETUP_MODE_GRAPHICS)
6160   {
6161     setup_info = setup_info_graphics;
6162     title_string = STR_SETUP_GRAPHICS;
6163   }
6164   else if (setup_mode == SETUP_MODE_SOUND)
6165   {
6166     setup_info = setup_info_sound;
6167     title_string = STR_SETUP_SOUND;
6168   }
6169   else if (setup_mode == SETUP_MODE_ARTWORK)
6170   {
6171     setup_info = setup_info_artwork;
6172     title_string = STR_SETUP_ARTWORK;
6173   }
6174   else if (setup_mode == SETUP_MODE_TOUCH)
6175   {
6176     setup_info = setup_info_touch;
6177     title_string = STR_SETUP_TOUCH;
6178
6179     if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
6180       setup_info = setup_info_touch_wipe_gestures;
6181   }
6182   else if (setup_mode == SETUP_MODE_SHORTCUTS)
6183   {
6184     setup_info = setup_info_shortcuts;
6185     title_string = STR_SETUP_SHORTCUTS;
6186   }
6187   else if (setup_mode == SETUP_MODE_SHORTCUTS_1)
6188   {
6189     setup_info = setup_info_shortcuts_1;
6190     title_string = STR_SETUP_SHORTCUTS;
6191   }
6192   else if (setup_mode == SETUP_MODE_SHORTCUTS_2)
6193   {
6194     setup_info = setup_info_shortcuts_2;
6195     title_string = STR_SETUP_SHORTCUTS;
6196   }
6197   else if (setup_mode == SETUP_MODE_SHORTCUTS_3)
6198   {
6199     setup_info = setup_info_shortcuts_3;
6200     title_string = STR_SETUP_SHORTCUTS;
6201   }
6202   else if (setup_mode == SETUP_MODE_SHORTCUTS_4)
6203   {
6204     setup_info = setup_info_shortcuts_4;
6205     title_string = STR_SETUP_SHORTCUTS;
6206   }
6207   else if (setup_mode == SETUP_MODE_SHORTCUTS_5)
6208   {
6209     setup_info = setup_info_shortcuts_5;
6210     title_string = STR_SETUP_SHORTCUTS;
6211   }
6212
6213   /* use modified setup info without setup entries marked as hidden */
6214   setup_info = getSetupInfoFinal(setup_info);
6215
6216   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
6217
6218   // determine maximal number of setup entries that can be displayed on screen
6219   num_setup_info = 0;
6220   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
6221     num_setup_info++;
6222
6223   // determine maximal number of setup entries available for this setup screen
6224   max_setup_info = 0;
6225   for (i = 0; setup_info[i].type != 0; i++)
6226     max_setup_info++;
6227
6228   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
6229
6230   MapScreenGadgets(max_setup_info);
6231
6232   if (redraw_all)
6233     redraw_mask = fade_mask = REDRAW_ALL;
6234
6235   DrawMaskedBorder(fade_mask);
6236
6237   FadeIn(fade_mask);
6238 }
6239
6240 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
6241 {
6242   menu_info = setup_info;
6243
6244   HandleMenuScreen(mx, my, dx, dy, button,
6245                    setup_mode, num_setup_info, max_setup_info);
6246 }
6247
6248 void DrawSetupScreen_Input()
6249 {
6250   int i;
6251
6252   FadeOut(REDRAW_FIELD);
6253
6254   ClearField();
6255
6256   setup_info = getSetupInfoFinal(setup_info_input);
6257
6258   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, STR_SETUP_INPUT);
6259
6260   for (i = 0; setup_info[i].type != 0; i++)
6261   {
6262     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
6263       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
6264     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
6265       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
6266     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
6267       initCursor(i, IMG_MENU_BUTTON);
6268
6269     DrawCursorAndText_Setup(i, -1, FALSE);
6270   }
6271
6272   /* create gadgets for setup input menu screen */
6273   FreeScreenGadgets();
6274   CreateScreenGadgets();
6275
6276   /* map gadgets for setup input menu screen */
6277   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
6278
6279   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
6280
6281   FadeIn(REDRAW_FIELD);
6282 }
6283
6284 static void setJoystickDeviceToNr(char *device_name, int device_nr)
6285 {
6286   if (device_name == NULL)
6287     return;
6288
6289   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
6290     device_nr = 0;
6291
6292   if (strlen(device_name) > 1)
6293   {
6294     char c1 = device_name[strlen(device_name) - 1];
6295     char c2 = device_name[strlen(device_name) - 2];
6296
6297     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
6298       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
6299   }
6300   else
6301     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
6302             strlen(device_name));
6303 }
6304
6305 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
6306 {
6307   int i;
6308   static struct SetupKeyboardInfo custom_key;
6309   static struct
6310   {
6311     Key *key;
6312     char *text;
6313   } custom[] =
6314   {
6315     { &custom_key.left,  "Axis/Pad Left"  },
6316     { &custom_key.right, "Axis/Pad Right" },
6317     { &custom_key.up,    "Axis/Pad Up"    },
6318     { &custom_key.down,  "Axis/Pad Down"  },
6319     { &custom_key.snap,  "Button 1/A/X"   },
6320     { &custom_key.drop,  "Button 2/B/Y"   }
6321   };
6322   static char *joystick_name[MAX_PLAYERS] =
6323   {
6324     "Joystick1",
6325     "Joystick2",
6326     "Joystick3",
6327     "Joystick4"
6328   };
6329   int font_nr_menu = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
6330   int font_nr_info = FONT_MENU_1;
6331   int font_nr_name = FONT_VALUE_OLD;
6332   int font_nr_on   = FONT_VALUE_1;
6333   int font_nr_off  = FONT_VALUE_OLD;
6334   int pos = 4;
6335
6336   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
6337   {
6338     font_nr_info = FONT_MENU_2;
6339     font_nr_on   = FONT_VALUE_NARROW;
6340     font_nr_off  = FONT_VALUE_OLD_NARROW;
6341   }
6342
6343   custom_key = setup.input[player_nr].key;
6344
6345   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
6346            FONT_INPUT_1_ACTIVE);
6347
6348   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
6349                              TILEX, TILEY);
6350   DrawFixedGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
6351                               PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
6352
6353   if (setup.input[player_nr].use_joystick)
6354   {
6355     char *device_name = setup.input[player_nr].joy.device_name;
6356     int joystick_nr = getJoystickNrFromDeviceName(device_name);
6357     boolean joystick_active = CheckJoystickOpened(joystick_nr);
6358     char *text = joystick_name[joystick_nr];
6359     int font_nr = (joystick_active ? font_nr_on : font_nr_off);
6360
6361     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
6362     DrawText(mSX + 32, mSY + 4 * 32, "Configure", font_nr_menu);
6363   }
6364   else
6365   {
6366     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", font_nr_on);
6367     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", font_nr_menu);
6368   }
6369
6370   if (SCR_FIELDY >= SCR_FIELDY_DEFAULT)
6371     DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", font_nr_info);
6372   else
6373     pos = 3;
6374
6375   drawCursorXY(1, pos + 0, IMG_MENU_BUTTON_LEFT);
6376   drawCursorXY(1, pos + 1, IMG_MENU_BUTTON_RIGHT);
6377   drawCursorXY(1, pos + 2, IMG_MENU_BUTTON_UP);
6378   drawCursorXY(1, pos + 3, IMG_MENU_BUTTON_DOWN);
6379
6380   DrawText(mSX + 2 * 32, mSY + (pos + 2) * 32, ":", font_nr_name);
6381   DrawText(mSX + 2 * 32, mSY + (pos + 3) * 32, ":", font_nr_name);
6382   DrawText(mSX + 2 * 32, mSY + (pos + 4) * 32, ":", font_nr_name);
6383   DrawText(mSX + 2 * 32, mSY + (pos + 5) * 32, ":", font_nr_name);
6384   DrawText(mSX + 1 * 32, mSY + (pos + 6) * 32, "Snap Field:", font_nr_name);
6385   DrawText(mSX + 1 * 32, mSY + (pos + 8) * 32, "Drop Element:", font_nr_name);
6386
6387   for (i = 0; i < 6; i++)
6388   {
6389     int ypos = (pos + 2) + i + (i > 3 ? i - 3 : 0);
6390
6391     DrawText(mSX + 3 * 32, mSY + ypos * 32,
6392              "              ", font_nr_on);
6393     DrawText(mSX + 3 * 32, mSY + ypos * 32,
6394              (setup.input[player_nr].use_joystick ?
6395               custom[i].text :
6396               getKeyNameFromKey(*custom[i].key)), font_nr_on);
6397   }
6398 }
6399
6400 static int input_player_nr = 0;
6401
6402 void HandleSetupScreen_Input_Player(int step, int direction)
6403 {
6404   int old_player_nr = input_player_nr;
6405   int new_player_nr;
6406
6407   new_player_nr = old_player_nr + step * direction;
6408   if (new_player_nr < 0)
6409     new_player_nr = 0;
6410   if (new_player_nr > MAX_PLAYERS - 1)
6411     new_player_nr = MAX_PLAYERS - 1;
6412
6413   if (new_player_nr != old_player_nr)
6414   {
6415     input_player_nr = new_player_nr;
6416
6417     drawPlayerSetupInputInfo(input_player_nr, FALSE);
6418   }
6419 }
6420
6421 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
6422 {
6423   static int choice = 0;
6424   int x = 0;
6425   int y = choice;
6426   int pos_start  = SETUPINPUT_SCREEN_POS_START;
6427   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
6428   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
6429   int pos_end    = SETUPINPUT_SCREEN_POS_END;
6430
6431   if (SCR_FIELDY < SCR_FIELDY_DEFAULT)
6432   {
6433     int i;
6434
6435     for (i = 0; setup_info_input[i].type != 0; i++)
6436     {
6437       /* adjust menu structure according to skipped setup entries */
6438       if (setup_info_input[i].type == TYPE_SKIPPABLE)
6439       {
6440         pos_empty2--;
6441         pos_end--;
6442       }
6443     }
6444   }
6445
6446   if (button == MB_MENU_INITIALIZE)
6447   {
6448     /* input setup menu may have changed size due to graphics configuration */
6449     if (choice >= pos_empty1)
6450       choice = pos_end;
6451
6452     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
6453
6454     DrawCursorAndText_Setup(choice, -1, TRUE);
6455
6456     return;
6457   }
6458   else if (button == MB_MENU_LEAVE)
6459   {
6460     setup_mode = SETUP_MODE_MAIN;
6461     DrawSetupScreen();
6462     InitJoysticks();
6463
6464     return;
6465   }
6466
6467   if (mx || my)         /* mouse input */
6468   {
6469     x = (mx - mSX) / 32;
6470     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
6471   }
6472   else if (dx || dy)    /* keyboard input */
6473   {
6474     if (dx && choice == 0)
6475       x = (dx < 0 ? 10 : 12);
6476     else if ((dx && choice == 1) ||
6477              (dx == -1 && choice == pos_end))
6478       button = MB_MENU_CHOICE;
6479     else if (dy)
6480       y = choice + dy;
6481
6482     if (y >= pos_empty1 && y <= pos_empty2)
6483       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
6484   }
6485
6486   if (y == 0 && dx != 0 && button)
6487   {
6488     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
6489   }
6490   else if (IN_VIS_FIELD(x, y) &&        // (does not use "IN_VIS_MENU()" yet)
6491            y >= pos_start && y <= pos_end &&
6492            !(y >= pos_empty1 && y <= pos_empty2))
6493   {
6494     if (button)
6495     {
6496       if (y != choice)
6497       {
6498         DrawCursorAndText_Setup(choice, -1, FALSE);
6499         DrawCursorAndText_Setup(y, -1, TRUE);
6500
6501         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
6502
6503         choice = y;
6504       }
6505     }
6506     else
6507     {
6508       if (y == 1)
6509       {
6510         char *device_name = setup.input[input_player_nr].joy.device_name;
6511
6512         if (!setup.input[input_player_nr].use_joystick)
6513         {
6514           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
6515
6516           setJoystickDeviceToNr(device_name, new_device_nr);
6517           setup.input[input_player_nr].use_joystick = TRUE;
6518         }
6519         else
6520         {
6521           int device_nr = getJoystickNrFromDeviceName(device_name);
6522           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
6523
6524           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
6525             setup.input[input_player_nr].use_joystick = FALSE;
6526           else
6527             setJoystickDeviceToNr(device_name, new_device_nr);
6528         }
6529
6530         drawPlayerSetupInputInfo(input_player_nr, FALSE);
6531       }
6532       else if (y == 2)
6533       {
6534         if (setup.input[input_player_nr].use_joystick)
6535           ConfigureJoystick(input_player_nr);
6536         else
6537           CustomizeKeyboard(input_player_nr);
6538       }
6539       else if (y == pos_end)
6540       {
6541         InitJoysticks();
6542
6543         FadeSetLeaveMenu();
6544
6545         setup_mode = SETUP_MODE_MAIN;
6546         DrawSetupScreen();
6547       }
6548     }
6549   }
6550 }
6551
6552 static boolean CustomizeKeyboardMain(int player_nr)
6553 {
6554   int i;
6555   int step_nr;
6556   boolean finished = FALSE;
6557   static struct SetupKeyboardInfo custom_key;
6558   static struct
6559   {
6560     Key *key;
6561     char *text;
6562   } customize_step[] =
6563   {
6564     { &custom_key.left,  "Move Left"    },
6565     { &custom_key.right, "Move Right"   },
6566     { &custom_key.up,    "Move Up"      },
6567     { &custom_key.down,  "Move Down"    },
6568     { &custom_key.snap,  "Snap Field"   },
6569     { &custom_key.drop,  "Drop Element" }
6570   };
6571   int font_nr_old = FONT_VALUE_OLD;
6572   int font_nr_new = FONT_VALUE_1;
6573   boolean success = FALSE;
6574
6575   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
6576   {
6577     font_nr_old = FONT_VALUE_OLD_NARROW;
6578     font_nr_new = FONT_VALUE_NARROW;
6579   }
6580
6581   /* read existing key bindings from player setup */
6582   custom_key = setup.input[player_nr].key;
6583
6584   FadeSetEnterMenu();
6585   FadeOut(REDRAW_FIELD);
6586
6587   ClearField();
6588
6589   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
6590
6591   step_nr = 0;
6592   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
6593            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
6594   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
6595            "Key:", FONT_INPUT_1_ACTIVE);
6596   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6597            getKeyNameFromKey(*customize_step[step_nr].key), font_nr_old);
6598
6599   FadeIn(REDRAW_FIELD);
6600
6601   while (!finished)
6602   {
6603     Event event;
6604
6605     if (NextValidEvent(&event))
6606     {
6607       switch (event.type)
6608       {
6609         case EVENT_KEYPRESS:
6610           {
6611             Key key = GetEventKey((KeyEvent *)&event, FALSE);
6612
6613             /* press 'Escape' to abort and keep the old key bindings */
6614             if (key == KSYM_Escape)
6615             {
6616               FadeSkipNextFadeIn();
6617
6618               finished = TRUE;
6619
6620               break;
6621             }
6622
6623             /* press 'Enter' to keep the existing key binding */
6624             if (key == KSYM_Return)
6625               key = *customize_step[step_nr].key;
6626
6627             /* check if key already used */
6628             for (i = 0; i < step_nr; i++)
6629               if (*customize_step[i].key == key)
6630                 break;
6631             if (i < step_nr)
6632               break;
6633
6634             /* got new key binding */
6635             *customize_step[step_nr].key = key;
6636             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6637                      "             ", font_nr_new);
6638             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6639                      getKeyNameFromKey(key), font_nr_new);
6640             step_nr++;
6641
6642             /* un-highlight last query */
6643             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
6644                      customize_step[step_nr - 1].text, FONT_MENU_1);
6645             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
6646                      "Key:", FONT_MENU_1);
6647
6648             /* all keys configured */
6649             if (step_nr == 6)
6650             {
6651               finished = TRUE;
6652               success = TRUE;
6653
6654               break;
6655             }
6656
6657             /* query next key binding */
6658             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
6659                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
6660             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
6661                      "Key:", FONT_INPUT_1_ACTIVE);
6662             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6663                      getKeyNameFromKey(*customize_step[step_nr].key),
6664                      font_nr_old);
6665           }
6666           break;
6667
6668         case EVENT_KEYRELEASE:
6669           key_joystick_mapping = 0;
6670           break;
6671
6672         default:
6673           HandleOtherEvents(&event);
6674           break;
6675       }
6676     }
6677
6678     BackToFront();
6679   }
6680
6681   /* write new key bindings back to player setup, if successfully finished */
6682   if (success)
6683     setup.input[player_nr].key = custom_key;
6684
6685   return success;
6686 }
6687
6688 void CustomizeKeyboard(int player_nr)
6689 {
6690   boolean success = CustomizeKeyboardMain(player_nr);
6691
6692   if (success)
6693   {
6694     int font_nr = FONT_TITLE_1;
6695     int font_height = getFontHeight(font_nr);
6696     int ypos1 = SYSIZE / 2 - font_height * 2;
6697     int ypos2 = SYSIZE / 2 - font_height * 1;
6698     unsigned int wait_frame_delay = 0;
6699     unsigned int wait_frame_delay_value = 2000;
6700
6701     ResetDelayCounter(&wait_frame_delay);
6702
6703     ClearField();
6704
6705     DrawTextSCentered(ypos1, font_nr, "Keyboard");
6706     DrawTextSCentered(ypos2, font_nr, "configured!");
6707
6708     while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
6709       BackToFront();
6710
6711     ClearEventQueue();
6712   }
6713
6714   DrawSetupScreen_Input();
6715 }
6716
6717 /* game controller mapping generator by Gabriel Jacobo <gabomdq@gmail.com> */
6718
6719 #define MARKER_BUTTON           1
6720 #define MARKER_AXIS_X           2
6721 #define MARKER_AXIS_Y           3
6722
6723 static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
6724 {
6725 #if defined(TARGET_SDL2)
6726   static boolean bitmaps_initialized = FALSE;
6727   boolean screen_initialized = FALSE;
6728   static Bitmap *controller, *button, *axis_x, *axis_y;
6729   char *name;
6730   boolean success = TRUE;
6731   boolean done = FALSE, next = FALSE;
6732   Event event;
6733   int alpha = 200, alpha_step = -1;
6734   int alpha_ticks = 0;
6735   char mapping[4096], temp[4096];
6736   int font_name = MENU_SETUP_FONT_TITLE;
6737   int font_info = MENU_SETUP_FONT_TEXT;
6738   int spacing_name = menu.line_spacing_setup[SETUP_MODE_INPUT];
6739   int spacing_line = menu.line_spacing_setup[SETUP_MODE_INPUT];
6740   int spacing_para = menu.paragraph_spacing_setup[SETUP_MODE_INPUT];
6741   int ystep_name = getMenuTextStep(spacing_name, font_name);
6742   int ystep_line = getMenuTextStep(spacing_line, font_info);
6743   int ystep_para = getMenuTextStep(spacing_para, font_info);
6744   int i, j;
6745
6746   struct
6747   {
6748     int x, y;
6749     int marker;
6750     char *field;
6751     int axis, button, hat, hat_value;
6752     char mapping[4096];
6753   }
6754   *step, *prev_step, steps[] =
6755   {
6756     { 356, 155, MARKER_BUTTON, "a",             },
6757     { 396, 122, MARKER_BUTTON, "b",             },
6758     { 320, 125, MARKER_BUTTON, "x",             },
6759     { 358,  95, MARKER_BUTTON, "y",             },
6760     { 162, 125, MARKER_BUTTON, "back",          },
6761     { 216, 125, MARKER_BUTTON, "guide",         },
6762     { 271, 125, MARKER_BUTTON, "start",         },
6763     { 110, 200, MARKER_BUTTON, "dpleft",        },
6764     { 146, 228, MARKER_BUTTON, "dpdown",        },
6765     { 178, 200, MARKER_BUTTON, "dpright",       },
6766     { 146, 172, MARKER_BUTTON, "dpup",          },
6767     {  50,  40, MARKER_BUTTON, "leftshoulder",  },
6768     {  88, -10, MARKER_AXIS_Y, "lefttrigger",   },
6769     { 382,  40, MARKER_BUTTON, "rightshoulder", },
6770     { 346, -10, MARKER_AXIS_Y, "righttrigger",  },
6771     {  73, 141, MARKER_BUTTON, "leftstick",     },
6772     { 282, 210, MARKER_BUTTON, "rightstick",    },
6773     {  73, 141, MARKER_AXIS_X, "leftx",         },
6774     {  73, 141, MARKER_AXIS_Y, "lefty",         },
6775     { 282, 210, MARKER_AXIS_X, "rightx",        },
6776     { 282, 210, MARKER_AXIS_Y, "righty",        },
6777   };
6778
6779   unsigned int event_frame_delay = 0;
6780   unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
6781
6782   ResetDelayCounter(&event_frame_delay);
6783
6784   if (!bitmaps_initialized)
6785   {
6786     controller = LoadCustomImage("joystick/controller.png");
6787     button     = LoadCustomImage("joystick/button.png");
6788     axis_x     = LoadCustomImage("joystick/axis_x.png");
6789     axis_y     = LoadCustomImage("joystick/axis_y.png");
6790
6791     bitmaps_initialized = TRUE;
6792   }
6793
6794   name = getFormattedJoystickName(SDL_JoystickName(joystick));
6795
6796 #if DEBUG_JOYSTICKS
6797   /* print info about the joystick we are watching */
6798   Error(ERR_DEBUG, "watching joystick %d: (%s)\n",
6799         SDL_JoystickInstanceID(joystick), name);
6800   Error(ERR_DEBUG, "joystick has %d axes, %d hats, %d balls, and %d buttons\n",
6801         SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
6802         SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
6803 #endif
6804
6805   /* initialize mapping with GUID and name */
6806   SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, sizeof(temp));
6807
6808   snprintf(mapping, sizeof(mapping), "%s,%s,platform:%s,",
6809            temp, name, SDL_GetPlatform());
6810
6811   /* loop through all steps (buttons and axes), getting joystick events */
6812   for (i = 0; i < SDL_arraysize(steps) && !done;)
6813   {
6814     Bitmap *marker = button;    /* initialize with reliable default value */
6815
6816     step = &steps[i];
6817     strcpy(step->mapping, mapping);
6818     step->axis = -1;
6819     step->button = -1;
6820     step->hat = -1;
6821     step->hat_value = -1;
6822
6823     marker = (step->marker == MARKER_BUTTON ? button :
6824               step->marker == MARKER_AXIS_X ? axis_x :
6825               step->marker == MARKER_AXIS_Y ? axis_y : marker);
6826
6827     next = FALSE;
6828
6829     while (!done && !next)
6830     {
6831       alpha += alpha_step * (int)(SDL_GetTicks() - alpha_ticks) / 5;
6832       alpha_ticks = SDL_GetTicks();
6833
6834       if (alpha >= 255)
6835       {
6836         alpha = 255;
6837         alpha_step = -1;
6838       }
6839       else if (alpha < 128)
6840       {
6841         alpha = 127;
6842         alpha_step = 1;
6843       }
6844
6845       int controller_x = SX + (SXSIZE - controller->width) / 2;
6846       int controller_y = SY + ystep_line;
6847
6848       int marker_x = controller_x + step->x;
6849       int marker_y = controller_y + step->y;
6850
6851       int ystart1 = mSY - 2 * SY + controller_y + controller->height;
6852       int ystart2 = ystart1 + ystep_name + ystep_line;
6853
6854       ClearField();
6855
6856       DrawTextSCentered(ystart1, font_name, name);
6857
6858       DrawTextSCentered(ystart2, font_info,
6859                         "Press buttons and move axes on");
6860       ystart2 += ystep_line;
6861       DrawTextSCentered(ystart2, font_info,
6862                         "your controller when indicated.");
6863       ystart2 += ystep_line;
6864       DrawTextSCentered(ystart2, font_info,
6865                         "(Your controller may look different.)");
6866       ystart2 += ystep_para;
6867
6868 #if defined(PLATFORM_ANDROID)
6869       DrawTextSCentered(ystart2, font_info,
6870                         "To correct a mistake,");
6871       ystart2 += ystep_line;
6872       DrawTextSCentered(ystart2, font_info,
6873                         "press the 'back' button.");
6874       ystart2 += ystep_line;
6875       DrawTextSCentered(ystart2, font_info,
6876                         "To skip a button or axis,");
6877       ystart2 += ystep_line;
6878       DrawTextSCentered(ystart2, font_info,
6879                         "press the 'menu' button.");
6880 #else
6881       DrawTextSCentered(ystart2, font_info,
6882                         "To correct a mistake,");
6883       ystart2 += ystep_line;
6884       DrawTextSCentered(ystart2, font_info,
6885                         "press the 'backspace' key.");
6886       ystart2 += ystep_line;
6887       DrawTextSCentered(ystart2, font_info,
6888                         "To skip a button or axis,");
6889       ystart2 += ystep_line;
6890       DrawTextSCentered(ystart2, font_info,
6891                         "press the 'return' key.");
6892       ystart2 += ystep_line;
6893       DrawTextSCentered(ystart2, font_info,
6894                         "To exit, press the 'escape' key.");
6895 #endif
6896
6897       BlitBitmapMasked(controller, drawto, 0, 0,
6898                        controller->width, controller->height,
6899                        controller_x, controller_y);
6900
6901       SDL_SetSurfaceAlphaMod(marker->surface_masked, alpha);
6902
6903       BlitBitmapMasked(marker, drawto, 0, 0,
6904                        marker->width, marker->height,
6905                        marker_x, marker_y);
6906
6907       if (!screen_initialized)
6908         FadeIn(REDRAW_FIELD);
6909       else
6910         BackToFront();
6911
6912       screen_initialized = TRUE;
6913
6914       while (NextValidEvent(&event))
6915       {
6916         switch (event.type)
6917         {
6918           case SDL_JOYAXISMOTION:
6919             if (event.jaxis.value > 20000 ||
6920                 event.jaxis.value < -20000)
6921             {
6922               for (j = 0; j < i; j++)
6923                 if (steps[j].axis == event.jaxis.axis)
6924                   break;
6925
6926               if (j == i)
6927               {
6928                 if (step->marker != MARKER_AXIS_X &&
6929                     step->marker != MARKER_AXIS_Y)
6930                   break;
6931
6932                 step->axis = event.jaxis.axis;
6933                 strcat(mapping, step->field);
6934                 snprintf(temp, sizeof(temp), ":a%u,", event.jaxis.axis);
6935                 strcat(mapping, temp);
6936                 i++;
6937                 next = TRUE;
6938               }
6939             }
6940
6941             break;
6942
6943           case SDL_JOYHATMOTION:
6944             /* ignore centering; we're probably just coming back
6945                to the center from the previous item we set */
6946             if (event.jhat.value == SDL_HAT_CENTERED)
6947               break;
6948
6949             for (j = 0; j < i; j++)
6950               if (steps[j].hat == event.jhat.hat &&
6951                   steps[j].hat_value == event.jhat.value)
6952                 break;
6953
6954             if (j == i)
6955             {
6956               step->hat = event.jhat.hat;
6957               step->hat_value = event.jhat.value;
6958               strcat(mapping, step->field);
6959               snprintf(temp, sizeof(temp), ":h%u.%u,",
6960                        event.jhat.hat, event.jhat.value );
6961               strcat(mapping, temp);
6962               i++;
6963               next = TRUE;
6964             }
6965
6966             break;
6967
6968           case SDL_JOYBALLMOTION:
6969             break;
6970
6971           case SDL_JOYBUTTONUP:
6972             for (j = 0; j < i; j++)
6973               if (steps[j].button == event.jbutton.button)
6974                 break;
6975
6976             if (j == i)
6977             {
6978               step->button = event.jbutton.button;
6979               strcat(mapping, step->field);
6980               snprintf(temp, sizeof(temp), ":b%u,", event.jbutton.button);
6981               strcat(mapping, temp);
6982               i++;
6983               next = TRUE;
6984             }
6985
6986             break;
6987
6988           case SDL_FINGERDOWN:
6989           case SDL_MOUSEBUTTONDOWN:
6990             /* skip this step */
6991             i++;
6992             next = TRUE;
6993
6994             break;
6995
6996           case SDL_KEYDOWN:
6997             if (event.key.keysym.sym == KSYM_BackSpace ||
6998                 event.key.keysym.sym == KSYM_Back)
6999             {
7000               if (i == 0)
7001               {
7002                 /* leave screen */
7003                 success = FALSE;
7004                 done = TRUE;
7005               }
7006
7007               /* undo this step */
7008               prev_step = &steps[i - 1];
7009               strcpy(mapping, prev_step->mapping);
7010               i--;
7011               next = TRUE;
7012
7013               break;
7014             }
7015
7016             if (event.key.keysym.sym == KSYM_space ||
7017                 event.key.keysym.sym == KSYM_Return ||
7018                 event.key.keysym.sym == KSYM_Menu)
7019             {
7020               /* skip this step */
7021               i++;
7022               next = TRUE;
7023
7024               break;
7025             }
7026
7027             if (event.key.keysym.sym == KSYM_Escape)
7028             {
7029               /* leave screen */
7030               success = FALSE;
7031               done = TRUE;
7032             }
7033
7034             break;
7035
7036           case SDL_QUIT:
7037             program.exit_function(0);
7038             break;
7039
7040           default:
7041             break;
7042         }
7043
7044         // do not handle events for longer than standard frame delay period
7045         if (DelayReached(&event_frame_delay, event_frame_delay_value))
7046           break;
7047       }
7048     }
7049   }
7050
7051   if (success)
7052   {
7053 #if DEBUG_JOYSTICKS
7054     Error(ERR_DEBUG, "New game controller mapping:\n\n%s\n\n", mapping);
7055 #endif
7056
7057     // activate mapping for this game
7058     SDL_GameControllerAddMapping(mapping);
7059
7060     // save mapping to personal mappings
7061     SaveSetup_AddGameControllerMapping(mapping);
7062   }
7063
7064   /* wait until the last pending event was removed from event queue */
7065   while (NextValidEvent(&event));
7066
7067   return success;
7068 #else
7069   return TRUE;
7070 #endif
7071 }
7072
7073 static int ConfigureJoystickMain(int player_nr)
7074 {
7075   char *device_name = setup.input[player_nr].joy.device_name;
7076   int joystick_nr = getJoystickNrFromDeviceName(device_name);
7077   boolean joystick_active = CheckJoystickOpened(joystick_nr);
7078   int success = FALSE;
7079   int i;
7080
7081   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
7082     return JOYSTICK_NOT_AVAILABLE;
7083
7084   if (!joystick_active || !setup.input[player_nr].use_joystick)
7085     return JOYSTICK_NOT_AVAILABLE;
7086
7087   FadeSetEnterMenu();
7088   FadeOut(REDRAW_FIELD);
7089
7090   // close all joystick devices (potentially opened as game controllers)
7091   for (i = 0; i < SDL_NumJoysticks(); i++)
7092     SDLCloseJoystick(i);
7093
7094   // open joystick device as plain joystick to configure as game controller
7095   SDL_Joystick *joystick = SDL_JoystickOpen(joystick_nr);
7096
7097   // as the joystick was successfully opened before, this should not happen
7098   if (joystick == NULL)
7099     return FALSE;
7100
7101   // create new game controller mapping (buttons and axes) for joystick device
7102   success = ConfigureJoystickMapButtonsAndAxes(joystick);
7103
7104   // close joystick (and maybe re-open as configured game controller later)
7105   SDL_JoystickClose(joystick);
7106
7107   // re-open all joystick devices (potentially as game controllers)
7108   for (i = 0; i < SDL_NumJoysticks(); i++)
7109     SDLOpenJoystick(i);
7110
7111   // clear all joystick input actions for all joystick devices
7112   SDLClearJoystickState();
7113
7114   return (success ? JOYSTICK_CONFIGURED : JOYSTICK_NOT_CONFIGURED);
7115 }
7116
7117 void ConfigureJoystick(int player_nr)
7118 {
7119   boolean state = ConfigureJoystickMain(player_nr);
7120
7121   if (state != JOYSTICK_NOT_CONFIGURED)
7122   {
7123     boolean success = (state == JOYSTICK_CONFIGURED);
7124     char message1[MAX_OUTPUT_LINESIZE + 1];
7125     char *message2 = (success ? "configured!" : "not available!");
7126     char *device_name = setup.input[player_nr].joy.device_name;
7127     int nr = getJoystickNrFromDeviceName(device_name) + 1;
7128     int font_nr = FONT_TITLE_1;
7129     int font_height = getFontHeight(font_nr);
7130     int ypos1 = SYSIZE / 2 - font_height * 2;
7131     int ypos2 = SYSIZE / 2 - font_height * 1;
7132     unsigned int wait_frame_delay = 0;
7133     unsigned int wait_frame_delay_value = 2000;
7134
7135     ResetDelayCounter(&wait_frame_delay);
7136
7137     ClearField();
7138
7139     sprintf(message1, "Joystick %d", nr);
7140
7141     DrawTextSCentered(ypos1, font_nr, message1);
7142     DrawTextSCentered(ypos2, font_nr, message2);
7143
7144     while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
7145       BackToFront();
7146
7147     ClearEventQueue();
7148   }
7149
7150   DrawSetupScreen_Input();
7151 }
7152
7153 void DrawSetupScreen()
7154 {
7155   if (setup_mode == SETUP_MODE_INPUT)
7156     DrawSetupScreen_Input();
7157   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
7158     DrawChooseTree(&game_speed_current);
7159   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
7160     DrawChooseTree(&scroll_delay_current);
7161   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
7162     DrawChooseTree(&snapshot_mode_current);
7163   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7164     DrawChooseTree(&window_size_current);
7165   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
7166     DrawChooseTree(&scaling_type_current);
7167   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
7168     DrawChooseTree(&rendering_mode_current);
7169   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
7170     DrawChooseTree(&artwork.gfx_current);
7171   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
7172     DrawChooseTree(&artwork.snd_current);
7173   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
7174     DrawChooseTree(&artwork.mus_current);
7175   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
7176     DrawChooseTree(&volume_simple_current);
7177   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
7178     DrawChooseTree(&volume_loops_current);
7179   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
7180     DrawChooseTree(&volume_music_current);
7181   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
7182     DrawChooseTree(&touch_control_current);
7183   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
7184     DrawChooseTree(&move_distance_current);
7185   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
7186     DrawChooseTree(&drop_distance_current);
7187   else
7188     DrawSetupScreen_Generic();
7189
7190   PlayMenuSoundsAndMusic();
7191 }
7192
7193 void RedrawSetupScreenAfterFullscreenToggle()
7194 {
7195   if (setup_mode == SETUP_MODE_GRAPHICS ||
7196       setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7197   {
7198     // update list selection from "setup.window_scaling_percent"
7199     execSetupGraphics_setWindowSizes(TRUE);
7200
7201     DrawSetupScreen();
7202   }
7203 }
7204
7205 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
7206 {
7207   if (setup_mode == SETUP_MODE_INPUT)
7208     HandleSetupScreen_Input(mx, my, dx, dy, button);
7209   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
7210     HandleChooseTree(mx, my, dx, dy, button, &game_speed_current);
7211   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
7212     HandleChooseTree(mx, my, dx, dy, button, &scroll_delay_current);
7213   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
7214     HandleChooseTree(mx, my, dx, dy, button, &snapshot_mode_current);
7215   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7216     HandleChooseTree(mx, my, dx, dy, button, &window_size_current);
7217   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
7218     HandleChooseTree(mx, my, dx, dy, button, &scaling_type_current);
7219   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
7220     HandleChooseTree(mx, my, dx, dy, button, &rendering_mode_current);
7221   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
7222     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
7223   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
7224     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
7225   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
7226     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
7227   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
7228     HandleChooseTree(mx, my, dx, dy, button, &volume_simple_current);
7229   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
7230     HandleChooseTree(mx, my, dx, dy, button, &volume_loops_current);
7231   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
7232     HandleChooseTree(mx, my, dx, dy, button, &volume_music_current);
7233   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
7234     HandleChooseTree(mx, my, dx, dy, button, &touch_control_current);
7235   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
7236     HandleChooseTree(mx, my, dx, dy, button, &move_distance_current);
7237   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
7238     HandleChooseTree(mx, my, dx, dy, button, &drop_distance_current);
7239   else
7240     HandleSetupScreen_Generic(mx, my, dx, dy, button);
7241 }
7242
7243 void HandleGameActions()
7244 {
7245   if (game.restart_game_message != NULL)
7246     RequestRestartGame(game.restart_game_message);
7247
7248   if (game_status != GAME_MODE_PLAYING)
7249     return;
7250
7251   GameActions();        /* main game loop */
7252
7253   if (tape.auto_play && !tape.playing)
7254     AutoPlayTape();     /* continue automatically playing next tape */
7255 }
7256
7257
7258 /* ---------- new screen button stuff -------------------------------------- */
7259
7260 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
7261 {
7262   switch (gadget_id)
7263   {
7264     case SCREEN_CTRL_ID_PREV_LEVEL:
7265       *x = mSX + GDI_ACTIVE_POS(menu.main.button.prev_level.x);
7266       *y = mSY + GDI_ACTIVE_POS(menu.main.button.prev_level.y);
7267       break;
7268
7269     case SCREEN_CTRL_ID_NEXT_LEVEL:
7270       *x = mSX + GDI_ACTIVE_POS(menu.main.button.next_level.x);
7271       *y = mSY + GDI_ACTIVE_POS(menu.main.button.next_level.y);
7272       break;
7273
7274     case SCREEN_CTRL_ID_PREV_PLAYER:
7275       *x = mSX + TILEX * 10;
7276       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
7277       break;
7278
7279     case SCREEN_CTRL_ID_NEXT_PLAYER:
7280       *x = mSX + TILEX * 12;
7281       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
7282       break;
7283
7284     default:
7285       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
7286   }
7287 }
7288
7289 static struct
7290 {
7291   int gfx_unpressed, gfx_pressed;
7292   void (*get_gadget_position)(int *, int *, int);
7293   int gadget_id;
7294   int screen_mask;
7295   char *infotext;
7296 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
7297 {
7298   {
7299     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
7300     getScreenMenuButtonPos,
7301     SCREEN_CTRL_ID_PREV_LEVEL,
7302     SCREEN_MASK_MAIN,
7303     "last level"
7304   },
7305   {
7306     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
7307     getScreenMenuButtonPos,
7308     SCREEN_CTRL_ID_NEXT_LEVEL,
7309     SCREEN_MASK_MAIN,
7310     "next level"
7311   },
7312   {
7313     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
7314     getScreenMenuButtonPos,
7315     SCREEN_CTRL_ID_PREV_PLAYER,
7316     SCREEN_MASK_INPUT,
7317     "last player"
7318   },
7319   {
7320     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
7321     getScreenMenuButtonPos,
7322     SCREEN_CTRL_ID_NEXT_PLAYER,
7323     SCREEN_MASK_INPUT,
7324     "next player"
7325   },
7326 };
7327
7328 static struct
7329 {
7330   int gfx_unpressed, gfx_pressed;
7331   int x, y;
7332   int gadget_id;
7333   char *infotext;
7334 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
7335 {
7336   {
7337     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
7338     -1, -1,     /* these values are not constant, but can change at runtime */
7339     SCREEN_CTRL_ID_SCROLL_UP,
7340     "scroll up"
7341   },
7342   {
7343     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
7344     -1, -1,     /* these values are not constant, but can change at runtime */
7345     SCREEN_CTRL_ID_SCROLL_DOWN,
7346     "scroll down"
7347   }
7348 };
7349
7350 static struct
7351 {
7352   int gfx_unpressed, gfx_pressed;
7353   int x, y;
7354   int width, height;
7355   int type;
7356   int gadget_id;
7357   char *infotext;
7358 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
7359 {
7360   {
7361     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
7362     -1, -1,     /* these values are not constant, but can change at runtime */
7363     -1, -1,     /* these values are not constant, but can change at runtime */
7364     GD_TYPE_SCROLLBAR_VERTICAL,
7365     SCREEN_CTRL_ID_SCROLL_VERTICAL,
7366     "scroll level series vertically"
7367   }
7368 };
7369
7370 static void CreateScreenMenubuttons()
7371 {
7372   struct GadgetInfo *gi;
7373   unsigned int event_mask;
7374   int i;
7375
7376   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
7377   {
7378     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
7379     int gfx_unpressed, gfx_pressed;
7380     int x, y, width, height;
7381     int gd_x1, gd_x2, gd_y1, gd_y2;
7382     int id = menubutton_info[i].gadget_id;
7383
7384     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
7385
7386     menubutton_info[i].get_gadget_position(&x, &y, id);
7387
7388     width = SC_MENUBUTTON_XSIZE;
7389     height = SC_MENUBUTTON_YSIZE;
7390
7391     gfx_unpressed = menubutton_info[i].gfx_unpressed;
7392     gfx_pressed   = menubutton_info[i].gfx_pressed;
7393     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
7394     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
7395     gd_x1 = graphic_info[gfx_unpressed].src_x;
7396     gd_y1 = graphic_info[gfx_unpressed].src_y;
7397     gd_x2 = graphic_info[gfx_pressed].src_x;
7398     gd_y2 = graphic_info[gfx_pressed].src_y;
7399
7400     gi = CreateGadget(GDI_CUSTOM_ID, id,
7401                       GDI_CUSTOM_TYPE_ID, i,
7402                       GDI_INFO_TEXT, menubutton_info[i].infotext,
7403                       GDI_X, x,
7404                       GDI_Y, y,
7405                       GDI_WIDTH, width,
7406                       GDI_HEIGHT, height,
7407                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
7408                       GDI_STATE, GD_BUTTON_UNPRESSED,
7409                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
7410                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
7411                       GDI_DIRECT_DRAW, FALSE,
7412                       GDI_EVENT_MASK, event_mask,
7413                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
7414                       GDI_END);
7415
7416     if (gi == NULL)
7417       Error(ERR_EXIT, "cannot create gadget");
7418
7419     screen_gadget[id] = gi;
7420   }
7421 }
7422
7423 static void CreateScreenScrollbuttons()
7424 {
7425   struct GadgetInfo *gi;
7426   unsigned int event_mask;
7427   int i;
7428
7429   /* these values are not constant, but can change at runtime */
7430   scrollbutton_info[0].x = SC_SCROLL_UP_XPOS;
7431   scrollbutton_info[0].y = SC_SCROLL_UP_YPOS;
7432   scrollbutton_info[1].x = SC_SCROLL_DOWN_XPOS;
7433   scrollbutton_info[1].y = SC_SCROLL_DOWN_YPOS;
7434
7435   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
7436   {
7437     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
7438     int gfx_unpressed, gfx_pressed;
7439     int x, y, width, height;
7440     int gd_x1, gd_x2, gd_y1, gd_y2;
7441     int id = scrollbutton_info[i].gadget_id;
7442
7443     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
7444
7445     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
7446     y = mSY + scrollbutton_info[i].y;
7447     width = SC_SCROLLBUTTON_XSIZE;
7448     height = SC_SCROLLBUTTON_YSIZE;
7449
7450     /* correct scrollbar position if placed outside menu (playfield) area */
7451     if (x > SX + SC_SCROLL_UP_XPOS)
7452       x = SX + SC_SCROLL_UP_XPOS;
7453
7454     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
7455       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
7456                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
7457
7458     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
7459     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
7460     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
7461     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
7462     gd_x1 = graphic_info[gfx_unpressed].src_x;
7463     gd_y1 = graphic_info[gfx_unpressed].src_y;
7464     gd_x2 = graphic_info[gfx_pressed].src_x;
7465     gd_y2 = graphic_info[gfx_pressed].src_y;
7466
7467     gi = CreateGadget(GDI_CUSTOM_ID, id,
7468                       GDI_CUSTOM_TYPE_ID, i,
7469                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
7470                       GDI_X, x,
7471                       GDI_Y, y,
7472                       GDI_WIDTH, width,
7473                       GDI_HEIGHT, height,
7474                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
7475                       GDI_STATE, GD_BUTTON_UNPRESSED,
7476                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
7477                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
7478                       GDI_DIRECT_DRAW, FALSE,
7479                       GDI_EVENT_MASK, event_mask,
7480                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
7481                       GDI_END);
7482
7483     if (gi == NULL)
7484       Error(ERR_EXIT, "cannot create gadget");
7485
7486     screen_gadget[id] = gi;
7487   }
7488 }
7489
7490 static void CreateScreenScrollbars()
7491 {
7492   int i;
7493
7494   /* these values are not constant, but can change at runtime */
7495   scrollbar_info[0].x = SC_SCROLL_VERTICAL_XPOS;
7496   scrollbar_info[0].y = SC_SCROLL_VERTICAL_YPOS;
7497   scrollbar_info[0].width  = SC_SCROLL_VERTICAL_XSIZE;
7498   scrollbar_info[0].height = SC_SCROLL_VERTICAL_YSIZE;
7499
7500   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
7501   {
7502     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
7503     int gfx_unpressed, gfx_pressed;
7504     int x, y, width, height;
7505     int gd_x1, gd_x2, gd_y1, gd_y2;
7506     struct GadgetInfo *gi;
7507     int items_max, items_visible, item_position;
7508     unsigned int event_mask;
7509     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
7510     int id = scrollbar_info[i].gadget_id;
7511
7512     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
7513
7514     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
7515     y = mSY + scrollbar_info[i].y;
7516     width  = scrollbar_info[i].width;
7517     height = scrollbar_info[i].height;
7518
7519     /* correct scrollbar position if placed outside menu (playfield) area */
7520     if (x > SX + SC_SCROLL_VERTICAL_XPOS)
7521       x = SX + SC_SCROLL_VERTICAL_XPOS;
7522
7523     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
7524       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
7525
7526     items_max = num_page_entries;
7527     items_visible = num_page_entries;
7528     item_position = 0;
7529
7530     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
7531     gfx_pressed   = scrollbar_info[i].gfx_pressed;
7532     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
7533     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
7534     gd_x1 = graphic_info[gfx_unpressed].src_x;
7535     gd_y1 = graphic_info[gfx_unpressed].src_y;
7536     gd_x2 = graphic_info[gfx_pressed].src_x;
7537     gd_y2 = graphic_info[gfx_pressed].src_y;
7538
7539     gi = CreateGadget(GDI_CUSTOM_ID, id,
7540                       GDI_CUSTOM_TYPE_ID, i,
7541                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
7542                       GDI_X, x,
7543                       GDI_Y, y,
7544                       GDI_WIDTH, width,
7545                       GDI_HEIGHT, height,
7546                       GDI_TYPE, scrollbar_info[i].type,
7547                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
7548                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
7549                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
7550                       GDI_WHEEL_AREA_X, SX,
7551                       GDI_WHEEL_AREA_Y, SY,
7552                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
7553                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
7554                       GDI_STATE, GD_BUTTON_UNPRESSED,
7555                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
7556                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
7557                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
7558                       GDI_DIRECT_DRAW, FALSE,
7559                       GDI_EVENT_MASK, event_mask,
7560                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
7561                       GDI_END);
7562
7563     if (gi == NULL)
7564       Error(ERR_EXIT, "cannot create gadget");
7565
7566     screen_gadget[id] = gi;
7567   }
7568 }
7569
7570 void CreateScreenGadgets()
7571 {
7572   CreateScreenMenubuttons();
7573
7574   CreateScreenScrollbuttons();
7575   CreateScreenScrollbars();
7576 }
7577
7578 void FreeScreenGadgets()
7579 {
7580   int i;
7581
7582   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
7583     FreeGadget(screen_gadget[i]);
7584 }
7585
7586 void MapScreenMenuGadgets(int screen_mask)
7587 {
7588   int i;
7589
7590   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
7591     if (screen_mask & menubutton_info[i].screen_mask)
7592       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
7593 }
7594
7595 void MapScreenGadgets(int num_entries)
7596 {
7597   int i;
7598
7599   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
7600     return;
7601
7602   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
7603     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
7604
7605   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
7606     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
7607 }
7608
7609 void MapScreenTreeGadgets(TreeInfo *ti)
7610 {
7611   MapScreenGadgets(numTreeInfoInGroup(ti));
7612 }
7613
7614 static void HandleScreenGadgets(struct GadgetInfo *gi)
7615 {
7616   int id = gi->custom_id;
7617   int button = gi->event.button;
7618   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
7619
7620   switch (id)
7621   {
7622     case SCREEN_CTRL_ID_PREV_LEVEL:
7623       HandleMainMenu_SelectLevel(step, -1, NO_DIRECT_LEVEL_SELECT);
7624       break;
7625
7626     case SCREEN_CTRL_ID_NEXT_LEVEL:
7627       HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT);
7628       break;
7629
7630     case SCREEN_CTRL_ID_PREV_PLAYER:
7631       HandleSetupScreen_Input_Player(step, -1);
7632       break;
7633
7634     case SCREEN_CTRL_ID_NEXT_PLAYER:
7635       HandleSetupScreen_Input_Player(step, +1);
7636       break;
7637
7638     case SCREEN_CTRL_ID_SCROLL_UP:
7639       if (game_status == GAME_MODE_LEVELS)
7640         HandleChooseLevelSet(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
7641       else if (game_status == GAME_MODE_LEVELNR)
7642         HandleChooseLevelNr(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
7643       else if (game_status == GAME_MODE_SETUP)
7644         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
7645       else if (game_status == GAME_MODE_INFO)
7646         HandleInfoScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
7647       break;
7648
7649     case SCREEN_CTRL_ID_SCROLL_DOWN:
7650       if (game_status == GAME_MODE_LEVELS)
7651         HandleChooseLevelSet(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
7652       else if (game_status == GAME_MODE_LEVELNR)
7653         HandleChooseLevelNr(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
7654       else if (game_status == GAME_MODE_SETUP)
7655         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
7656       else if (game_status == GAME_MODE_INFO)
7657         HandleInfoScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
7658       break;
7659
7660     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
7661       if (game_status == GAME_MODE_LEVELS)
7662         HandleChooseLevelSet(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
7663       else if (game_status == GAME_MODE_LEVELNR)
7664         HandleChooseLevelNr(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
7665       else if (game_status == GAME_MODE_SETUP)
7666         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
7667       else if (game_status == GAME_MODE_INFO)
7668         HandleInfoScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
7669       break;
7670
7671     default:
7672       break;
7673   }
7674 }