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