added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_HEAD_UNUSED  1       // unused tape header bytes
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169
170   {
171     -1,                                 SAVE_CONF_ALWAYS,
172     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
173     &li.fieldx,                         STD_LEV_FIELDX
174   },
175   {
176     -1,                                 SAVE_CONF_ALWAYS,
177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
178     &li.fieldy,                         STD_LEV_FIELDY
179   },
180
181   {
182     -1,                                 SAVE_CONF_ALWAYS,
183     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
184     &li.time,                           100
185   },
186
187   {
188     -1,                                 SAVE_CONF_ALWAYS,
189     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
190     &li.gems_needed,                    0
191   },
192
193   {
194     -1,                                 -1,
195     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
196     &li.random_seed,                    0
197   },
198
199   {
200     -1,                                 -1,
201     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
202     &li.use_step_counter,               FALSE
203   },
204
205   {
206     -1,                                 -1,
207     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
208     &li.wind_direction_initial,         MV_NONE
209   },
210
211   {
212     -1,                                 -1,
213     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
214     &li.em_slippery_gems,               FALSE
215   },
216
217   {
218     -1,                                 -1,
219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
220     &li.use_custom_template,            FALSE
221   },
222
223   {
224     -1,                                 -1,
225     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
226     &li.can_move_into_acid_bits,        ~0      // default: everything can
227   },
228
229   {
230     -1,                                 -1,
231     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
232     &li.dont_collide_with_bits,         ~0      // default: always deadly
233   },
234
235   {
236     -1,                                 -1,
237     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
238     &li.em_explodes_by_fire,            FALSE
239   },
240
241   {
242     -1,                                 -1,
243     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
244     &li.score[SC_TIME_BONUS],           1
245   },
246
247   {
248     -1,                                 -1,
249     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
250     &li.auto_exit_sokoban,              FALSE
251   },
252
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
256     &li.auto_count_gems,                FALSE
257   },
258
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
262     &li.solved_by_one_player,           FALSE
263   },
264
265   {
266     -1,                                 -1,
267     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
268     &li.time_score_base,                1
269   },
270
271   {
272     -1,                                 -1,
273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
274     &li.rate_time_over_score,           FALSE
275   },
276
277   {
278     -1,                                 -1,
279     -1,                                 -1,
280     NULL,                               -1
281   }
282 };
283
284 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 {
286   // (these values are the same for each player)
287   {
288     EL_PLAYER_1,                        -1,
289     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
290     &li.block_last_field,               FALSE   // default case for EM levels
291   },
292   {
293     EL_PLAYER_1,                        -1,
294     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
295     &li.sp_block_last_field,            TRUE    // default case for SP levels
296   },
297   {
298     EL_PLAYER_1,                        -1,
299     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
300     &li.instant_relocation,             FALSE
301   },
302   {
303     EL_PLAYER_1,                        -1,
304     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
305     &li.can_pass_to_walkable,           FALSE
306   },
307   {
308     EL_PLAYER_1,                        -1,
309     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
310     &li.block_snap_field,               TRUE
311   },
312   {
313     EL_PLAYER_1,                        -1,
314     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
315     &li.continuous_snapping,            TRUE
316   },
317   {
318     EL_PLAYER_1,                        -1,
319     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
320     &li.shifted_relocation,             FALSE
321   },
322   {
323     EL_PLAYER_1,                        -1,
324     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
325     &li.lazy_relocation,                FALSE
326   },
327   {
328     EL_PLAYER_1,                        -1,
329     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
330     &li.finish_dig_collect,             TRUE
331   },
332   {
333     EL_PLAYER_1,                        -1,
334     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
335     &li.keep_walkable_ce,               FALSE
336   },
337
338   // (these values are different for each player)
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
342     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
347     &li.initial_player_gravity[0],      FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
352     &li.use_start_element[0],           FALSE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
357     &li.start_element[0],               EL_PLAYER_1
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
362     &li.use_artwork_element[0],         FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
367     &li.artwork_element[0],             EL_PLAYER_1
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
372     &li.use_explosion_element[0],       FALSE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
377     &li.explosion_element[0],           EL_PLAYER_1
378   },
379   {
380     EL_PLAYER_1,                        -1,
381     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
382     &li.use_initial_inventory[0],       FALSE
383   },
384   {
385     EL_PLAYER_1,                        -1,
386     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
387     &li.initial_inventory_size[0],      1
388   },
389   {
390     EL_PLAYER_1,                        -1,
391     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
392     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
393     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
394   },
395
396   {
397     EL_PLAYER_2,                        -1,
398     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
399     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
400   },
401   {
402     EL_PLAYER_2,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
404     &li.initial_player_gravity[1],      FALSE
405   },
406   {
407     EL_PLAYER_2,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
409     &li.use_start_element[1],           FALSE
410   },
411   {
412     EL_PLAYER_2,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
414     &li.start_element[1],               EL_PLAYER_2
415   },
416   {
417     EL_PLAYER_2,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
419     &li.use_artwork_element[1],         FALSE
420   },
421   {
422     EL_PLAYER_2,                        -1,
423     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
424     &li.artwork_element[1],             EL_PLAYER_2
425   },
426   {
427     EL_PLAYER_2,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
429     &li.use_explosion_element[1],       FALSE
430   },
431   {
432     EL_PLAYER_2,                        -1,
433     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
434     &li.explosion_element[1],           EL_PLAYER_2
435   },
436   {
437     EL_PLAYER_2,                        -1,
438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
439     &li.use_initial_inventory[1],       FALSE
440   },
441   {
442     EL_PLAYER_2,                        -1,
443     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
444     &li.initial_inventory_size[1],      1
445   },
446   {
447     EL_PLAYER_2,                        -1,
448     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
449     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
450     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
451   },
452
453   {
454     EL_PLAYER_3,                        -1,
455     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
456     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
457   },
458   {
459     EL_PLAYER_3,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
461     &li.initial_player_gravity[2],      FALSE
462   },
463   {
464     EL_PLAYER_3,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
466     &li.use_start_element[2],           FALSE
467   },
468   {
469     EL_PLAYER_3,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
471     &li.start_element[2],               EL_PLAYER_3
472   },
473   {
474     EL_PLAYER_3,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
476     &li.use_artwork_element[2],         FALSE
477   },
478   {
479     EL_PLAYER_3,                        -1,
480     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
481     &li.artwork_element[2],             EL_PLAYER_3
482   },
483   {
484     EL_PLAYER_3,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
486     &li.use_explosion_element[2],       FALSE
487   },
488   {
489     EL_PLAYER_3,                        -1,
490     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
491     &li.explosion_element[2],           EL_PLAYER_3
492   },
493   {
494     EL_PLAYER_3,                        -1,
495     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
496     &li.use_initial_inventory[2],       FALSE
497   },
498   {
499     EL_PLAYER_3,                        -1,
500     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
501     &li.initial_inventory_size[2],      1
502   },
503   {
504     EL_PLAYER_3,                        -1,
505     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
506     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
507     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
508   },
509
510   {
511     EL_PLAYER_4,                        -1,
512     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
513     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
514   },
515   {
516     EL_PLAYER_4,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
518     &li.initial_player_gravity[3],      FALSE
519   },
520   {
521     EL_PLAYER_4,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
523     &li.use_start_element[3],           FALSE
524   },
525   {
526     EL_PLAYER_4,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
528     &li.start_element[3],               EL_PLAYER_4
529   },
530   {
531     EL_PLAYER_4,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
533     &li.use_artwork_element[3],         FALSE
534   },
535   {
536     EL_PLAYER_4,                        -1,
537     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
538     &li.artwork_element[3],             EL_PLAYER_4
539   },
540   {
541     EL_PLAYER_4,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
543     &li.use_explosion_element[3],       FALSE
544   },
545   {
546     EL_PLAYER_4,                        -1,
547     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
548     &li.explosion_element[3],           EL_PLAYER_4
549   },
550   {
551     EL_PLAYER_4,                        -1,
552     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
553     &li.use_initial_inventory[3],       FALSE
554   },
555   {
556     EL_PLAYER_4,                        -1,
557     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
558     &li.initial_inventory_size[3],      1
559   },
560   {
561     EL_PLAYER_4,                        -1,
562     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
563     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
564     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
565   },
566
567   {
568     EL_EMERALD,                         -1,
569     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
570     &li.score[SC_EMERALD],              10
571   },
572
573   {
574     EL_DIAMOND,                         -1,
575     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
576     &li.score[SC_DIAMOND],              10
577   },
578
579   {
580     EL_BUG,                             -1,
581     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
582     &li.score[SC_BUG],                  10
583   },
584
585   {
586     EL_SPACESHIP,                       -1,
587     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
588     &li.score[SC_SPACESHIP],            10
589   },
590
591   {
592     EL_PACMAN,                          -1,
593     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
594     &li.score[SC_PACMAN],               10
595   },
596
597   {
598     EL_NUT,                             -1,
599     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
600     &li.score[SC_NUT],                  10
601   },
602
603   {
604     EL_DYNAMITE,                        -1,
605     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
606     &li.score[SC_DYNAMITE],             10
607   },
608
609   {
610     EL_KEY_1,                           -1,
611     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
612     &li.score[SC_KEY],                  10
613   },
614
615   {
616     EL_PEARL,                           -1,
617     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
618     &li.score[SC_PEARL],                10
619   },
620
621   {
622     EL_CRYSTAL,                         -1,
623     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
624     &li.score[SC_CRYSTAL],              10
625   },
626
627   {
628     EL_BD_AMOEBA,                       -1,
629     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
630     &li.amoeba_content,                 EL_DIAMOND
631   },
632   {
633     EL_BD_AMOEBA,                       -1,
634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
635     &li.amoeba_speed,                   10
636   },
637   {
638     EL_BD_AMOEBA,                       -1,
639     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
640     &li.grow_into_diggable,             TRUE
641   },
642
643   {
644     EL_YAMYAM,                          -1,
645     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
646     &li.yamyam_content,                 EL_ROCK, NULL,
647     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
648   },
649   {
650     EL_YAMYAM,                          -1,
651     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
652     &li.score[SC_YAMYAM],               10
653   },
654
655   {
656     EL_ROBOT,                           -1,
657     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
658     &li.score[SC_ROBOT],                10
659   },
660   {
661     EL_ROBOT,                           -1,
662     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
663     &li.slurp_score,                    10
664   },
665
666   {
667     EL_ROBOT_WHEEL,                     -1,
668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
669     &li.time_wheel,                     10
670   },
671
672   {
673     EL_MAGIC_WALL,                      -1,
674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
675     &li.time_magic_wall,                10
676   },
677
678   {
679     EL_GAME_OF_LIFE,                    -1,
680     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
681     &li.game_of_life[0],                2
682   },
683   {
684     EL_GAME_OF_LIFE,                    -1,
685     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
686     &li.game_of_life[1],                3
687   },
688   {
689     EL_GAME_OF_LIFE,                    -1,
690     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
691     &li.game_of_life[2],                3
692   },
693   {
694     EL_GAME_OF_LIFE,                    -1,
695     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
696     &li.game_of_life[3],                3
697   },
698   {
699     EL_GAME_OF_LIFE,                    -1,
700     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
701     &li.use_life_bugs,                  FALSE
702   },
703
704   {
705     EL_BIOMAZE,                         -1,
706     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
707     &li.biomaze[0],                     2
708   },
709   {
710     EL_BIOMAZE,                         -1,
711     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
712     &li.biomaze[1],                     3
713   },
714   {
715     EL_BIOMAZE,                         -1,
716     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
717     &li.biomaze[2],                     3
718   },
719   {
720     EL_BIOMAZE,                         -1,
721     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
722     &li.biomaze[3],                     3
723   },
724
725   {
726     EL_TIMEGATE_SWITCH,                 -1,
727     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
728     &li.time_timegate,                  10
729   },
730
731   {
732     EL_LIGHT_SWITCH_ACTIVE,             -1,
733     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
734     &li.time_light,                     10
735   },
736
737   {
738     EL_SHIELD_NORMAL,                   -1,
739     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
740     &li.shield_normal_time,             10
741   },
742   {
743     EL_SHIELD_NORMAL,                   -1,
744     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
745     &li.score[SC_SHIELD],               10
746   },
747
748   {
749     EL_SHIELD_DEADLY,                   -1,
750     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
751     &li.shield_deadly_time,             10
752   },
753   {
754     EL_SHIELD_DEADLY,                   -1,
755     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
756     &li.score[SC_SHIELD],               10
757   },
758
759   {
760     EL_EXTRA_TIME,                      -1,
761     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
762     &li.extra_time,                     10
763   },
764   {
765     EL_EXTRA_TIME,                      -1,
766     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
767     &li.extra_time_score,               10
768   },
769
770   {
771     EL_TIME_ORB_FULL,                   -1,
772     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
773     &li.time_orb_time,                  10
774   },
775   {
776     EL_TIME_ORB_FULL,                   -1,
777     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
778     &li.use_time_orb_bug,               FALSE
779   },
780
781   {
782     EL_SPRING,                          -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
784     &li.use_spring_bug,                 FALSE
785   },
786
787   {
788     EL_EMC_ANDROID,                     -1,
789     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
790     &li.android_move_time,              10
791   },
792   {
793     EL_EMC_ANDROID,                     -1,
794     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
795     &li.android_clone_time,             10
796   },
797   {
798     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
799     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
800     &li.android_clone_element[0],       EL_EMPTY, NULL,
801     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
802   },
803   {
804     EL_EMC_ANDROID,                     -1,
805     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
806     &li.android_clone_element[0],       EL_EMPTY, NULL,
807     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
808   },
809
810   {
811     EL_EMC_LENSES,                      -1,
812     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
813     &li.lenses_score,                   10
814   },
815   {
816     EL_EMC_LENSES,                      -1,
817     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
818     &li.lenses_time,                    10
819   },
820
821   {
822     EL_EMC_MAGNIFIER,                   -1,
823     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
824     &li.magnify_score,                  10
825   },
826   {
827     EL_EMC_MAGNIFIER,                   -1,
828     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
829     &li.magnify_time,                   10
830   },
831
832   {
833     EL_EMC_MAGIC_BALL,                  -1,
834     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
835     &li.ball_time,                      10
836   },
837   {
838     EL_EMC_MAGIC_BALL,                  -1,
839     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
840     &li.ball_random,                    FALSE
841   },
842   {
843     EL_EMC_MAGIC_BALL,                  -1,
844     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
845     &li.ball_active_initial,            FALSE
846   },
847   {
848     EL_EMC_MAGIC_BALL,                  -1,
849     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
850     &li.ball_content,                   EL_EMPTY, NULL,
851     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
852   },
853
854   {
855     EL_SOKOBAN_FIELD_EMPTY,             -1,
856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
857     &li.sb_fields_needed,               TRUE
858   },
859
860   {
861     EL_SOKOBAN_OBJECT,                  -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
863     &li.sb_objects_needed,              TRUE
864   },
865
866   {
867     EL_MM_MCDUFFIN,                     -1,
868     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
869     &li.mm_laser_red,                   FALSE
870   },
871   {
872     EL_MM_MCDUFFIN,                     -1,
873     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
874     &li.mm_laser_green,                 FALSE
875   },
876   {
877     EL_MM_MCDUFFIN,                     -1,
878     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
879     &li.mm_laser_blue,                  TRUE
880   },
881
882   {
883     EL_DF_LASER,                        -1,
884     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
885     &li.df_laser_red,                   TRUE
886   },
887   {
888     EL_DF_LASER,                        -1,
889     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
890     &li.df_laser_green,                 TRUE
891   },
892   {
893     EL_DF_LASER,                        -1,
894     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
895     &li.df_laser_blue,                  FALSE
896   },
897
898   {
899     EL_MM_FUSE_ACTIVE,                  -1,
900     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
901     &li.mm_time_fuse,                   25
902   },
903   {
904     EL_MM_BOMB,                         -1,
905     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
906     &li.mm_time_bomb,                   75
907   },
908   {
909     EL_MM_GRAY_BALL,                    -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
911     &li.mm_time_ball,                   75
912   },
913   {
914     EL_MM_STEEL_BLOCK,                  -1,
915     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
916     &li.mm_time_block,                  75
917   },
918   {
919     EL_MM_LIGHTBALL,                    -1,
920     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
921     &li.score[SC_ELEM_BONUS],           10
922   },
923
924   // ---------- unused values -------------------------------------------------
925
926   {
927     EL_UNKNOWN,                         SAVE_CONF_NEVER,
928     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
929     &li.score[SC_UNKNOWN_15],           10
930   },
931
932   {
933     -1,                                 -1,
934     -1,                                 -1,
935     NULL,                               -1
936   }
937 };
938
939 static struct LevelFileConfigInfo chunk_config_NOTE[] =
940 {
941   {
942     -1,                                 -1,
943     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
944     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
945   },
946   {
947     -1,                                 -1,
948     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
949     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
950   },
951
952   {
953     -1,                                 -1,
954     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
955     &xx_envelope.autowrap,              FALSE
956   },
957   {
958     -1,                                 -1,
959     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
960     &xx_envelope.centered,              FALSE
961   },
962
963   {
964     -1,                                 -1,
965     TYPE_STRING,                        CONF_VALUE_BYTES(1),
966     &xx_envelope.text,                  -1, NULL,
967     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
968     &xx_default_string_empty[0]
969   },
970
971   {
972     -1,                                 -1,
973     -1,                                 -1,
974     NULL,                               -1
975   }
976 };
977
978 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
979 {
980   {
981     -1,                                 -1,
982     TYPE_STRING,                        CONF_VALUE_BYTES(1),
983     &xx_ei.description[0],              -1,
984     &yy_ei.description[0],
985     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
986     &xx_default_description[0]
987   },
988
989   {
990     -1,                                 -1,
991     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
992     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
993     &yy_ei.properties[EP_BITFIELD_BASE_NR]
994   },
995 #if ENABLE_RESERVED_CODE
996   // (reserved for later use)
997   {
998     -1,                                 -1,
999     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1000     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1001     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1002   },
1003 #endif
1004
1005   {
1006     -1,                                 -1,
1007     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1008     &xx_ei.use_gfx_element,             FALSE,
1009     &yy_ei.use_gfx_element
1010   },
1011   {
1012     -1,                                 -1,
1013     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1014     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1015     &yy_ei.gfx_element_initial
1016   },
1017
1018   {
1019     -1,                                 -1,
1020     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1021     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1022     &yy_ei.access_direction
1023   },
1024
1025   {
1026     -1,                                 -1,
1027     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1028     &xx_ei.collect_score_initial,       10,
1029     &yy_ei.collect_score_initial
1030   },
1031   {
1032     -1,                                 -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1034     &xx_ei.collect_count_initial,       1,
1035     &yy_ei.collect_count_initial
1036   },
1037
1038   {
1039     -1,                                 -1,
1040     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1041     &xx_ei.ce_value_fixed_initial,      0,
1042     &yy_ei.ce_value_fixed_initial
1043   },
1044   {
1045     -1,                                 -1,
1046     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1047     &xx_ei.ce_value_random_initial,     0,
1048     &yy_ei.ce_value_random_initial
1049   },
1050   {
1051     -1,                                 -1,
1052     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1053     &xx_ei.use_last_ce_value,           FALSE,
1054     &yy_ei.use_last_ce_value
1055   },
1056
1057   {
1058     -1,                                 -1,
1059     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1060     &xx_ei.push_delay_fixed,            8,
1061     &yy_ei.push_delay_fixed
1062   },
1063   {
1064     -1,                                 -1,
1065     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1066     &xx_ei.push_delay_random,           8,
1067     &yy_ei.push_delay_random
1068   },
1069   {
1070     -1,                                 -1,
1071     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1072     &xx_ei.drop_delay_fixed,            0,
1073     &yy_ei.drop_delay_fixed
1074   },
1075   {
1076     -1,                                 -1,
1077     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1078     &xx_ei.drop_delay_random,           0,
1079     &yy_ei.drop_delay_random
1080   },
1081   {
1082     -1,                                 -1,
1083     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1084     &xx_ei.move_delay_fixed,            0,
1085     &yy_ei.move_delay_fixed
1086   },
1087   {
1088     -1,                                 -1,
1089     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1090     &xx_ei.move_delay_random,           0,
1091     &yy_ei.move_delay_random
1092   },
1093   {
1094     -1,                                 -1,
1095     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1096     &xx_ei.step_delay_fixed,            0,
1097     &yy_ei.step_delay_fixed
1098   },
1099   {
1100     -1,                                 -1,
1101     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1102     &xx_ei.step_delay_random,           0,
1103     &yy_ei.step_delay_random
1104   },
1105
1106   {
1107     -1,                                 -1,
1108     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1109     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1110     &yy_ei.move_pattern
1111   },
1112   {
1113     -1,                                 -1,
1114     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1115     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1116     &yy_ei.move_direction_initial
1117   },
1118   {
1119     -1,                                 -1,
1120     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1121     &xx_ei.move_stepsize,               TILEX / 8,
1122     &yy_ei.move_stepsize
1123   },
1124
1125   {
1126     -1,                                 -1,
1127     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1128     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1129     &yy_ei.move_enter_element
1130   },
1131   {
1132     -1,                                 -1,
1133     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1134     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1135     &yy_ei.move_leave_element
1136   },
1137   {
1138     -1,                                 -1,
1139     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1140     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1141     &yy_ei.move_leave_type
1142   },
1143
1144   {
1145     -1,                                 -1,
1146     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1147     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1148     &yy_ei.slippery_type
1149   },
1150
1151   {
1152     -1,                                 -1,
1153     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1154     &xx_ei.explosion_type,              EXPLODES_3X3,
1155     &yy_ei.explosion_type
1156   },
1157   {
1158     -1,                                 -1,
1159     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1160     &xx_ei.explosion_delay,             16,
1161     &yy_ei.explosion_delay
1162   },
1163   {
1164     -1,                                 -1,
1165     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1166     &xx_ei.ignition_delay,              8,
1167     &yy_ei.ignition_delay
1168   },
1169
1170   {
1171     -1,                                 -1,
1172     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1173     &xx_ei.content,                     EL_EMPTY_SPACE,
1174     &yy_ei.content,
1175     &xx_num_contents,                   1, 1
1176   },
1177
1178   // ---------- "num_change_pages" must be the last entry ---------------------
1179
1180   {
1181     -1,                                 SAVE_CONF_ALWAYS,
1182     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1183     &xx_ei.num_change_pages,            1,
1184     &yy_ei.num_change_pages
1185   },
1186
1187   {
1188     -1,                                 -1,
1189     -1,                                 -1,
1190     NULL,                               -1,
1191     NULL
1192   }
1193 };
1194
1195 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1196 {
1197   // ---------- "current_change_page" must be the first entry -----------------
1198
1199   {
1200     -1,                                 SAVE_CONF_ALWAYS,
1201     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1202     &xx_current_change_page,            -1
1203   },
1204
1205   // ---------- (the remaining entries can be in any order) -------------------
1206
1207   {
1208     -1,                                 -1,
1209     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1210     &xx_change.can_change,              FALSE
1211   },
1212
1213   {
1214     -1,                                 -1,
1215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1216     &xx_event_bits[0],                  0
1217   },
1218   {
1219     -1,                                 -1,
1220     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1221     &xx_event_bits[1],                  0
1222   },
1223
1224   {
1225     -1,                                 -1,
1226     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1227     &xx_change.trigger_player,          CH_PLAYER_ANY
1228   },
1229   {
1230     -1,                                 -1,
1231     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1232     &xx_change.trigger_side,            CH_SIDE_ANY
1233   },
1234   {
1235     -1,                                 -1,
1236     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1237     &xx_change.trigger_page,            CH_PAGE_ANY
1238   },
1239
1240   {
1241     -1,                                 -1,
1242     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1243     &xx_change.target_element,          EL_EMPTY_SPACE
1244   },
1245
1246   {
1247     -1,                                 -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1249     &xx_change.delay_fixed,             0
1250   },
1251   {
1252     -1,                                 -1,
1253     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1254     &xx_change.delay_random,            0
1255   },
1256   {
1257     -1,                                 -1,
1258     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1259     &xx_change.delay_frames,            FRAMES_PER_SECOND
1260   },
1261
1262   {
1263     -1,                                 -1,
1264     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1265     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1266   },
1267
1268   {
1269     -1,                                 -1,
1270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1271     &xx_change.explode,                 FALSE
1272   },
1273   {
1274     -1,                                 -1,
1275     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1276     &xx_change.use_target_content,      FALSE
1277   },
1278   {
1279     -1,                                 -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1281     &xx_change.only_if_complete,        FALSE
1282   },
1283   {
1284     -1,                                 -1,
1285     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1286     &xx_change.use_random_replace,      FALSE
1287   },
1288   {
1289     -1,                                 -1,
1290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1291     &xx_change.random_percentage,       100
1292   },
1293   {
1294     -1,                                 -1,
1295     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1296     &xx_change.replace_when,            CP_WHEN_EMPTY
1297   },
1298
1299   {
1300     -1,                                 -1,
1301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1302     &xx_change.has_action,              FALSE
1303   },
1304   {
1305     -1,                                 -1,
1306     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1307     &xx_change.action_type,             CA_NO_ACTION
1308   },
1309   {
1310     -1,                                 -1,
1311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1312     &xx_change.action_mode,             CA_MODE_UNDEFINED
1313   },
1314   {
1315     -1,                                 -1,
1316     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1317     &xx_change.action_arg,              CA_ARG_UNDEFINED
1318   },
1319
1320   {
1321     -1,                                 -1,
1322     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1323     &xx_change.action_element,          EL_EMPTY_SPACE
1324   },
1325
1326   {
1327     -1,                                 -1,
1328     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1329     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1330     &xx_num_contents,                   1, 1
1331   },
1332
1333   {
1334     -1,                                 -1,
1335     -1,                                 -1,
1336     NULL,                               -1
1337   }
1338 };
1339
1340 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1341 {
1342   {
1343     -1,                                 -1,
1344     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1345     &xx_ei.description[0],              -1, NULL,
1346     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1347     &xx_default_description[0]
1348   },
1349
1350   {
1351     -1,                                 -1,
1352     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1353     &xx_ei.use_gfx_element,             FALSE
1354   },
1355   {
1356     -1,                                 -1,
1357     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1358     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1359   },
1360
1361   {
1362     -1,                                 -1,
1363     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1364     &xx_group.choice_mode,              ANIM_RANDOM
1365   },
1366
1367   {
1368     -1,                                 -1,
1369     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1370     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1371     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1372   },
1373
1374   {
1375     -1,                                 -1,
1376     -1,                                 -1,
1377     NULL,                               -1
1378   }
1379 };
1380
1381 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1382 {
1383   {
1384     -1,                                 -1,
1385     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1386     &xx_ei.use_gfx_element,             FALSE
1387   },
1388   {
1389     -1,                                 -1,
1390     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1391     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1392   },
1393
1394   {
1395     -1,                                 -1,
1396     -1,                                 -1,
1397     NULL,                               -1
1398   }
1399 };
1400
1401 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1402 {
1403   {
1404     EL_PLAYER_1,                        -1,
1405     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1406     &li.block_snap_field,               TRUE
1407   },
1408   {
1409     EL_PLAYER_1,                        -1,
1410     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1411     &li.continuous_snapping,            TRUE
1412   },
1413   {
1414     EL_PLAYER_1,                        -1,
1415     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1416     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1417   },
1418   {
1419     EL_PLAYER_1,                        -1,
1420     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1421     &li.use_start_element[0],           FALSE
1422   },
1423   {
1424     EL_PLAYER_1,                        -1,
1425     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1426     &li.start_element[0],               EL_PLAYER_1
1427   },
1428   {
1429     EL_PLAYER_1,                        -1,
1430     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1431     &li.use_artwork_element[0],         FALSE
1432   },
1433   {
1434     EL_PLAYER_1,                        -1,
1435     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1436     &li.artwork_element[0],             EL_PLAYER_1
1437   },
1438   {
1439     EL_PLAYER_1,                        -1,
1440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1441     &li.use_explosion_element[0],       FALSE
1442   },
1443   {
1444     EL_PLAYER_1,                        -1,
1445     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1446     &li.explosion_element[0],           EL_PLAYER_1
1447   },
1448
1449   {
1450     -1,                                 -1,
1451     -1,                                 -1,
1452     NULL,                               -1
1453   }
1454 };
1455
1456 static struct
1457 {
1458   int filetype;
1459   char *id;
1460 }
1461 filetype_id_list[] =
1462 {
1463   { LEVEL_FILE_TYPE_RND,        "RND"   },
1464   { LEVEL_FILE_TYPE_BD,         "BD"    },
1465   { LEVEL_FILE_TYPE_EM,         "EM"    },
1466   { LEVEL_FILE_TYPE_SP,         "SP"    },
1467   { LEVEL_FILE_TYPE_DX,         "DX"    },
1468   { LEVEL_FILE_TYPE_SB,         "SB"    },
1469   { LEVEL_FILE_TYPE_DC,         "DC"    },
1470   { LEVEL_FILE_TYPE_MM,         "MM"    },
1471   { LEVEL_FILE_TYPE_MM,         "DF"    },
1472   { -1,                         NULL    },
1473 };
1474
1475
1476 // ============================================================================
1477 // level file functions
1478 // ============================================================================
1479
1480 static boolean check_special_flags(char *flag)
1481 {
1482   if (strEqual(options.special_flags, flag) ||
1483       strEqual(leveldir_current->special_flags, flag))
1484     return TRUE;
1485
1486   return FALSE;
1487 }
1488
1489 static struct DateInfo getCurrentDate(void)
1490 {
1491   time_t epoch_seconds = time(NULL);
1492   struct tm *now = localtime(&epoch_seconds);
1493   struct DateInfo date;
1494
1495   date.year  = now->tm_year + 1900;
1496   date.month = now->tm_mon  + 1;
1497   date.day   = now->tm_mday;
1498
1499   date.src   = DATE_SRC_CLOCK;
1500
1501   return date;
1502 }
1503
1504 static void resetEventFlags(struct ElementChangeInfo *change)
1505 {
1506   int i;
1507
1508   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1509     change->has_event[i] = FALSE;
1510 }
1511
1512 static void resetEventBits(void)
1513 {
1514   int i;
1515
1516   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1517     xx_event_bits[i] = 0;
1518 }
1519
1520 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1521 {
1522   int i;
1523
1524   /* important: only change event flag if corresponding event bit is set
1525      (this is because all xx_event_bits[] values are loaded separately,
1526      and all xx_event_bits[] values are set back to zero before loading
1527      another value xx_event_bits[x] (each value representing 32 flags)) */
1528
1529   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1530     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1531       change->has_event[i] = TRUE;
1532 }
1533
1534 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1535 {
1536   int i;
1537
1538   /* in contrast to the above function setEventFlagsFromEventBits(), it
1539      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1540      depending on the corresponding change->has_event[i] values here, as
1541      all xx_event_bits[] values are reset in resetEventBits() before */
1542
1543   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1544     if (change->has_event[i])
1545       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1546 }
1547
1548 static char *getDefaultElementDescription(struct ElementInfo *ei)
1549 {
1550   static char description[MAX_ELEMENT_NAME_LEN + 1];
1551   char *default_description = (ei->custom_description != NULL ?
1552                                ei->custom_description :
1553                                ei->editor_description);
1554   int i;
1555
1556   // always start with reliable default values
1557   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1558     description[i] = '\0';
1559
1560   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1561   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1562
1563   return &description[0];
1564 }
1565
1566 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1567 {
1568   char *default_description = getDefaultElementDescription(ei);
1569   int i;
1570
1571   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1572     ei->description[i] = default_description[i];
1573 }
1574
1575 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1576 {
1577   int i;
1578
1579   for (i = 0; conf[i].data_type != -1; i++)
1580   {
1581     int default_value = conf[i].default_value;
1582     int data_type = conf[i].data_type;
1583     int conf_type = conf[i].conf_type;
1584     int byte_mask = conf_type & CONF_MASK_BYTES;
1585
1586     if (byte_mask == CONF_MASK_MULTI_BYTES)
1587     {
1588       int default_num_entities = conf[i].default_num_entities;
1589       int max_num_entities = conf[i].max_num_entities;
1590
1591       *(int *)(conf[i].num_entities) = default_num_entities;
1592
1593       if (data_type == TYPE_STRING)
1594       {
1595         char *default_string = conf[i].default_string;
1596         char *string = (char *)(conf[i].value);
1597
1598         strncpy(string, default_string, max_num_entities);
1599       }
1600       else if (data_type == TYPE_ELEMENT_LIST)
1601       {
1602         int *element_array = (int *)(conf[i].value);
1603         int j;
1604
1605         for (j = 0; j < max_num_entities; j++)
1606           element_array[j] = default_value;
1607       }
1608       else if (data_type == TYPE_CONTENT_LIST)
1609       {
1610         struct Content *content = (struct Content *)(conf[i].value);
1611         int c, x, y;
1612
1613         for (c = 0; c < max_num_entities; c++)
1614           for (y = 0; y < 3; y++)
1615             for (x = 0; x < 3; x++)
1616               content[c].e[x][y] = default_value;
1617       }
1618     }
1619     else        // constant size configuration data (1, 2 or 4 bytes)
1620     {
1621       if (data_type == TYPE_BOOLEAN)
1622         *(boolean *)(conf[i].value) = default_value;
1623       else
1624         *(int *)    (conf[i].value) = default_value;
1625     }
1626   }
1627 }
1628
1629 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1630 {
1631   int i;
1632
1633   for (i = 0; conf[i].data_type != -1; i++)
1634   {
1635     int data_type = conf[i].data_type;
1636     int conf_type = conf[i].conf_type;
1637     int byte_mask = conf_type & CONF_MASK_BYTES;
1638
1639     if (byte_mask == CONF_MASK_MULTI_BYTES)
1640     {
1641       int max_num_entities = conf[i].max_num_entities;
1642
1643       if (data_type == TYPE_STRING)
1644       {
1645         char *string      = (char *)(conf[i].value);
1646         char *string_copy = (char *)(conf[i].value_copy);
1647
1648         strncpy(string_copy, string, max_num_entities);
1649       }
1650       else if (data_type == TYPE_ELEMENT_LIST)
1651       {
1652         int *element_array      = (int *)(conf[i].value);
1653         int *element_array_copy = (int *)(conf[i].value_copy);
1654         int j;
1655
1656         for (j = 0; j < max_num_entities; j++)
1657           element_array_copy[j] = element_array[j];
1658       }
1659       else if (data_type == TYPE_CONTENT_LIST)
1660       {
1661         struct Content *content      = (struct Content *)(conf[i].value);
1662         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1663         int c, x, y;
1664
1665         for (c = 0; c < max_num_entities; c++)
1666           for (y = 0; y < 3; y++)
1667             for (x = 0; x < 3; x++)
1668               content_copy[c].e[x][y] = content[c].e[x][y];
1669       }
1670     }
1671     else        // constant size configuration data (1, 2 or 4 bytes)
1672     {
1673       if (data_type == TYPE_BOOLEAN)
1674         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1675       else
1676         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1677     }
1678   }
1679 }
1680
1681 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1682 {
1683   int i;
1684
1685   xx_ei = *ei_from;     // copy element data into temporary buffer
1686   yy_ei = *ei_to;       // copy element data into temporary buffer
1687
1688   copyConfigFromConfigList(chunk_config_CUSX_base);
1689
1690   *ei_from = xx_ei;
1691   *ei_to   = yy_ei;
1692
1693   // ---------- reinitialize and copy change pages ----------
1694
1695   ei_to->num_change_pages = ei_from->num_change_pages;
1696   ei_to->current_change_page = ei_from->current_change_page;
1697
1698   setElementChangePages(ei_to, ei_to->num_change_pages);
1699
1700   for (i = 0; i < ei_to->num_change_pages; i++)
1701     ei_to->change_page[i] = ei_from->change_page[i];
1702
1703   // ---------- copy group element info ----------
1704   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
1705     *ei_to->group = *ei_from->group;
1706
1707   // mark this custom element as modified
1708   ei_to->modified_settings = TRUE;
1709 }
1710
1711 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1712 {
1713   int change_page_size = sizeof(struct ElementChangeInfo);
1714
1715   ei->num_change_pages = MAX(1, change_pages);
1716
1717   ei->change_page =
1718     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1719
1720   if (ei->current_change_page >= ei->num_change_pages)
1721     ei->current_change_page = ei->num_change_pages - 1;
1722
1723   ei->change = &ei->change_page[ei->current_change_page];
1724 }
1725
1726 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1727 {
1728   xx_change = *change;          // copy change data into temporary buffer
1729
1730   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1731
1732   *change = xx_change;
1733
1734   resetEventFlags(change);
1735
1736   change->direct_action = 0;
1737   change->other_action = 0;
1738
1739   change->pre_change_function = NULL;
1740   change->change_function = NULL;
1741   change->post_change_function = NULL;
1742 }
1743
1744 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1745 {
1746   int i, x, y;
1747
1748   li = *level;          // copy level data into temporary buffer
1749   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1750   *level = li;          // copy temporary buffer back to level data
1751
1752   setLevelInfoToDefaults_EM();
1753   setLevelInfoToDefaults_SP();
1754   setLevelInfoToDefaults_MM();
1755
1756   level->native_em_level = &native_em_level;
1757   level->native_sp_level = &native_sp_level;
1758   level->native_mm_level = &native_mm_level;
1759
1760   level->file_version = FILE_VERSION_ACTUAL;
1761   level->game_version = GAME_VERSION_ACTUAL;
1762
1763   level->creation_date = getCurrentDate();
1764
1765   level->encoding_16bit_field  = TRUE;
1766   level->encoding_16bit_yamyam = TRUE;
1767   level->encoding_16bit_amoeba = TRUE;
1768
1769   // clear level name and level author string buffers
1770   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1771     level->name[i] = '\0';
1772   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1773     level->author[i] = '\0';
1774
1775   // set level name and level author to default values
1776   strcpy(level->name, NAMELESS_LEVEL_NAME);
1777   strcpy(level->author, ANONYMOUS_NAME);
1778
1779   // set level playfield to playable default level with player and exit
1780   for (x = 0; x < MAX_LEV_FIELDX; x++)
1781     for (y = 0; y < MAX_LEV_FIELDY; y++)
1782       level->field[x][y] = EL_SAND;
1783
1784   level->field[0][0] = EL_PLAYER_1;
1785   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1786
1787   BorderElement = EL_STEELWALL;
1788
1789   // detect custom elements when loading them
1790   level->file_has_custom_elements = FALSE;
1791
1792   // set all bug compatibility flags to "false" => do not emulate this bug
1793   level->use_action_after_change_bug = FALSE;
1794
1795   if (leveldir_current)
1796   {
1797     // try to determine better author name than 'anonymous'
1798     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1799     {
1800       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1801       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1802     }
1803     else
1804     {
1805       switch (LEVELCLASS(leveldir_current))
1806       {
1807         case LEVELCLASS_TUTORIAL:
1808           strcpy(level->author, PROGRAM_AUTHOR_STRING);
1809           break;
1810
1811         case LEVELCLASS_CONTRIB:
1812           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1813           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1814           break;
1815
1816         case LEVELCLASS_PRIVATE:
1817           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1818           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1819           break;
1820
1821         default:
1822           // keep default value
1823           break;
1824       }
1825     }
1826   }
1827 }
1828
1829 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1830 {
1831   static boolean clipboard_elements_initialized = FALSE;
1832   int i;
1833
1834   InitElementPropertiesStatic();
1835
1836   li = *level;          // copy level data into temporary buffer
1837   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1838   *level = li;          // copy temporary buffer back to level data
1839
1840   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1841   {
1842     int element = i;
1843     struct ElementInfo *ei = &element_info[element];
1844
1845     // never initialize clipboard elements after the very first time
1846     // (to be able to use clipboard elements between several levels)
1847     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1848       continue;
1849
1850     if (IS_ENVELOPE(element))
1851     {
1852       int envelope_nr = element - EL_ENVELOPE_1;
1853
1854       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1855
1856       level->envelope[envelope_nr] = xx_envelope;
1857     }
1858
1859     if (IS_CUSTOM_ELEMENT(element) ||
1860         IS_GROUP_ELEMENT(element) ||
1861         IS_INTERNAL_ELEMENT(element))
1862     {
1863       xx_ei = *ei;      // copy element data into temporary buffer
1864
1865       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1866
1867       *ei = xx_ei;
1868     }
1869
1870     setElementChangePages(ei, 1);
1871     setElementChangeInfoToDefaults(ei->change);
1872
1873     if (IS_CUSTOM_ELEMENT(element) ||
1874         IS_GROUP_ELEMENT(element) ||
1875         IS_INTERNAL_ELEMENT(element))
1876     {
1877       setElementDescriptionToDefault(ei);
1878
1879       ei->modified_settings = FALSE;
1880     }
1881
1882     if (IS_CUSTOM_ELEMENT(element) ||
1883         IS_INTERNAL_ELEMENT(element))
1884     {
1885       // internal values used in level editor
1886
1887       ei->access_type = 0;
1888       ei->access_layer = 0;
1889       ei->access_protected = 0;
1890       ei->walk_to_action = 0;
1891       ei->smash_targets = 0;
1892       ei->deadliness = 0;
1893
1894       ei->can_explode_by_fire = FALSE;
1895       ei->can_explode_smashed = FALSE;
1896       ei->can_explode_impact = FALSE;
1897
1898       ei->current_change_page = 0;
1899     }
1900
1901     if (IS_GROUP_ELEMENT(element) ||
1902         IS_INTERNAL_ELEMENT(element))
1903     {
1904       struct ElementGroupInfo *group;
1905
1906       // initialize memory for list of elements in group
1907       if (ei->group == NULL)
1908         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1909
1910       group = ei->group;
1911
1912       xx_group = *group;        // copy group data into temporary buffer
1913
1914       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1915
1916       *group = xx_group;
1917     }
1918
1919     if (IS_EMPTY_ELEMENT(element) ||
1920         IS_INTERNAL_ELEMENT(element))
1921     {
1922       xx_ei = *ei;              // copy element data into temporary buffer
1923
1924       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1925
1926       *ei = xx_ei;
1927     }
1928   }
1929
1930   clipboard_elements_initialized = TRUE;
1931 }
1932
1933 static void setLevelInfoToDefaults(struct LevelInfo *level,
1934                                    boolean level_info_only,
1935                                    boolean reset_file_status)
1936 {
1937   setLevelInfoToDefaults_Level(level);
1938
1939   if (!level_info_only)
1940     setLevelInfoToDefaults_Elements(level);
1941
1942   if (reset_file_status)
1943   {
1944     level->no_valid_file = FALSE;
1945     level->no_level_file = FALSE;
1946   }
1947
1948   level->changed = FALSE;
1949 }
1950
1951 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1952 {
1953   level_file_info->nr = 0;
1954   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1955   level_file_info->packed = FALSE;
1956
1957   setString(&level_file_info->basename, NULL);
1958   setString(&level_file_info->filename, NULL);
1959 }
1960
1961 int getMappedElement_SB(int, boolean);
1962
1963 static void ActivateLevelTemplate(void)
1964 {
1965   int x, y;
1966
1967   if (check_special_flags("load_xsb_to_ces"))
1968   {
1969     // fill smaller playfields with padding "beyond border wall" elements
1970     if (level.fieldx < level_template.fieldx ||
1971         level.fieldy < level_template.fieldy)
1972     {
1973       short field[level.fieldx][level.fieldy];
1974       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1975       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1976       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1977       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1978
1979       // copy old playfield (which is smaller than the visible area)
1980       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1981         field[x][y] = level.field[x][y];
1982
1983       // fill new, larger playfield with "beyond border wall" elements
1984       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1985         level.field[x][y] = getMappedElement_SB('_', TRUE);
1986
1987       // copy the old playfield to the middle of the new playfield
1988       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1989         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1990
1991       level.fieldx = new_fieldx;
1992       level.fieldy = new_fieldy;
1993     }
1994   }
1995
1996   // Currently there is no special action needed to activate the template
1997   // data, because 'element_info' property settings overwrite the original
1998   // level data, while all other variables do not change.
1999
2000   // Exception: 'from_level_template' elements in the original level playfield
2001   // are overwritten with the corresponding elements at the same position in
2002   // playfield from the level template.
2003
2004   for (x = 0; x < level.fieldx; x++)
2005     for (y = 0; y < level.fieldy; y++)
2006       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2007         level.field[x][y] = level_template.field[x][y];
2008
2009   if (check_special_flags("load_xsb_to_ces"))
2010   {
2011     struct LevelInfo level_backup = level;
2012
2013     // overwrite all individual level settings from template level settings
2014     level = level_template;
2015
2016     // restore level file info
2017     level.file_info = level_backup.file_info;
2018
2019     // restore playfield size
2020     level.fieldx = level_backup.fieldx;
2021     level.fieldy = level_backup.fieldy;
2022
2023     // restore playfield content
2024     for (x = 0; x < level.fieldx; x++)
2025       for (y = 0; y < level.fieldy; y++)
2026         level.field[x][y] = level_backup.field[x][y];
2027
2028     // restore name and author from individual level
2029     strcpy(level.name,   level_backup.name);
2030     strcpy(level.author, level_backup.author);
2031
2032     // restore flag "use_custom_template"
2033     level.use_custom_template = level_backup.use_custom_template;
2034   }
2035 }
2036
2037 static char *getLevelFilenameFromBasename(char *basename)
2038 {
2039   static char *filename = NULL;
2040
2041   checked_free(filename);
2042
2043   filename = getPath2(getCurrentLevelDir(), basename);
2044
2045   return filename;
2046 }
2047
2048 static int getFileTypeFromBasename(char *basename)
2049 {
2050   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2051
2052   static char *filename = NULL;
2053   struct stat file_status;
2054
2055   // ---------- try to determine file type from filename ----------
2056
2057   // check for typical filename of a Supaplex level package file
2058   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2059     return LEVEL_FILE_TYPE_SP;
2060
2061   // check for typical filename of a Diamond Caves II level package file
2062   if (strSuffixLower(basename, ".dc") ||
2063       strSuffixLower(basename, ".dc2"))
2064     return LEVEL_FILE_TYPE_DC;
2065
2066   // check for typical filename of a Sokoban level package file
2067   if (strSuffixLower(basename, ".xsb") &&
2068       strchr(basename, '%') == NULL)
2069     return LEVEL_FILE_TYPE_SB;
2070
2071   // ---------- try to determine file type from filesize ----------
2072
2073   checked_free(filename);
2074   filename = getPath2(getCurrentLevelDir(), basename);
2075
2076   if (stat(filename, &file_status) == 0)
2077   {
2078     // check for typical filesize of a Supaplex level package file
2079     if (file_status.st_size == 170496)
2080       return LEVEL_FILE_TYPE_SP;
2081   }
2082
2083   return LEVEL_FILE_TYPE_UNKNOWN;
2084 }
2085
2086 static int getFileTypeFromMagicBytes(char *filename, int type)
2087 {
2088   File *file;
2089
2090   if ((file = openFile(filename, MODE_READ)))
2091   {
2092     char chunk_name[CHUNK_ID_LEN + 1];
2093
2094     getFileChunkBE(file, chunk_name, NULL);
2095
2096     if (strEqual(chunk_name, "MMII") ||
2097         strEqual(chunk_name, "MIRR"))
2098       type = LEVEL_FILE_TYPE_MM;
2099
2100     closeFile(file);
2101   }
2102
2103   return type;
2104 }
2105
2106 static boolean checkForPackageFromBasename(char *basename)
2107 {
2108   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2109   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2110
2111   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2112 }
2113
2114 static char *getSingleLevelBasenameExt(int nr, char *extension)
2115 {
2116   static char basename[MAX_FILENAME_LEN];
2117
2118   if (nr < 0)
2119     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2120   else
2121     sprintf(basename, "%03d.%s", nr, extension);
2122
2123   return basename;
2124 }
2125
2126 static char *getSingleLevelBasename(int nr)
2127 {
2128   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2129 }
2130
2131 static char *getPackedLevelBasename(int type)
2132 {
2133   static char basename[MAX_FILENAME_LEN];
2134   char *directory = getCurrentLevelDir();
2135   Directory *dir;
2136   DirectoryEntry *dir_entry;
2137
2138   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2139
2140   if ((dir = openDirectory(directory)) == NULL)
2141   {
2142     Warn("cannot read current level directory '%s'", directory);
2143
2144     return basename;
2145   }
2146
2147   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2148   {
2149     char *entry_basename = dir_entry->basename;
2150     int entry_type = getFileTypeFromBasename(entry_basename);
2151
2152     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2153     {
2154       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2155           type == entry_type)
2156       {
2157         strcpy(basename, entry_basename);
2158
2159         break;
2160       }
2161     }
2162   }
2163
2164   closeDirectory(dir);
2165
2166   return basename;
2167 }
2168
2169 static char *getSingleLevelFilename(int nr)
2170 {
2171   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2172 }
2173
2174 #if ENABLE_UNUSED_CODE
2175 static char *getPackedLevelFilename(int type)
2176 {
2177   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2178 }
2179 #endif
2180
2181 char *getDefaultLevelFilename(int nr)
2182 {
2183   return getSingleLevelFilename(nr);
2184 }
2185
2186 #if ENABLE_UNUSED_CODE
2187 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2188                                                  int type)
2189 {
2190   lfi->type = type;
2191   lfi->packed = FALSE;
2192
2193   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2194   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2195 }
2196 #endif
2197
2198 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2199                                                  int type, char *format, ...)
2200 {
2201   static char basename[MAX_FILENAME_LEN];
2202   va_list ap;
2203
2204   va_start(ap, format);
2205   vsprintf(basename, format, ap);
2206   va_end(ap);
2207
2208   lfi->type = type;
2209   lfi->packed = FALSE;
2210
2211   setString(&lfi->basename, basename);
2212   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2213 }
2214
2215 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2216                                                  int type)
2217 {
2218   lfi->type = type;
2219   lfi->packed = TRUE;
2220
2221   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2222   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2223 }
2224
2225 static int getFiletypeFromID(char *filetype_id)
2226 {
2227   char *filetype_id_lower;
2228   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2229   int i;
2230
2231   if (filetype_id == NULL)
2232     return LEVEL_FILE_TYPE_UNKNOWN;
2233
2234   filetype_id_lower = getStringToLower(filetype_id);
2235
2236   for (i = 0; filetype_id_list[i].id != NULL; i++)
2237   {
2238     char *id_lower = getStringToLower(filetype_id_list[i].id);
2239     
2240     if (strEqual(filetype_id_lower, id_lower))
2241       filetype = filetype_id_list[i].filetype;
2242
2243     free(id_lower);
2244
2245     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2246       break;
2247   }
2248
2249   free(filetype_id_lower);
2250
2251   return filetype;
2252 }
2253
2254 char *getLocalLevelTemplateFilename(void)
2255 {
2256   return getDefaultLevelFilename(-1);
2257 }
2258
2259 char *getGlobalLevelTemplateFilename(void)
2260 {
2261   // global variable "leveldir_current" must be modified in the loop below
2262   LevelDirTree *leveldir_current_last = leveldir_current;
2263   char *filename = NULL;
2264
2265   // check for template level in path from current to topmost tree node
2266
2267   while (leveldir_current != NULL)
2268   {
2269     filename = getDefaultLevelFilename(-1);
2270
2271     if (fileExists(filename))
2272       break;
2273
2274     leveldir_current = leveldir_current->node_parent;
2275   }
2276
2277   // restore global variable "leveldir_current" modified in above loop
2278   leveldir_current = leveldir_current_last;
2279
2280   return filename;
2281 }
2282
2283 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2284 {
2285   int nr = lfi->nr;
2286
2287   // special case: level number is negative => check for level template file
2288   if (nr < 0)
2289   {
2290     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2291                                          getSingleLevelBasename(-1));
2292
2293     // replace local level template filename with global template filename
2294     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2295
2296     // no fallback if template file not existing
2297     return;
2298   }
2299
2300   // special case: check for file name/pattern specified in "levelinfo.conf"
2301   if (leveldir_current->level_filename != NULL)
2302   {
2303     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2304
2305     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2306                                          leveldir_current->level_filename, nr);
2307
2308     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2309
2310     if (fileExists(lfi->filename))
2311       return;
2312   }
2313   else if (leveldir_current->level_filetype != NULL)
2314   {
2315     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2316
2317     // check for specified native level file with standard file name
2318     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2319                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2320     if (fileExists(lfi->filename))
2321       return;
2322   }
2323
2324   // check for native Rocks'n'Diamonds level file
2325   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2326                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2327   if (fileExists(lfi->filename))
2328     return;
2329
2330   // check for Emerald Mine level file (V1)
2331   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2332                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2333   if (fileExists(lfi->filename))
2334     return;
2335   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2336                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2337   if (fileExists(lfi->filename))
2338     return;
2339
2340   // check for Emerald Mine level file (V2 to V5)
2341   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2342   if (fileExists(lfi->filename))
2343     return;
2344
2345   // check for Emerald Mine level file (V6 / single mode)
2346   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2347   if (fileExists(lfi->filename))
2348     return;
2349   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2350   if (fileExists(lfi->filename))
2351     return;
2352
2353   // check for Emerald Mine level file (V6 / teamwork mode)
2354   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2355   if (fileExists(lfi->filename))
2356     return;
2357   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2358   if (fileExists(lfi->filename))
2359     return;
2360
2361   // check for various packed level file formats
2362   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2363   if (fileExists(lfi->filename))
2364     return;
2365
2366   // no known level file found -- use default values (and fail later)
2367   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2368                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2369 }
2370
2371 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2372 {
2373   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2374     lfi->type = getFileTypeFromBasename(lfi->basename);
2375
2376   if (lfi->type == LEVEL_FILE_TYPE_RND)
2377     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2378 }
2379
2380 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2381 {
2382   // always start with reliable default values
2383   setFileInfoToDefaults(level_file_info);
2384
2385   level_file_info->nr = nr;     // set requested level number
2386
2387   determineLevelFileInfo_Filename(level_file_info);
2388   determineLevelFileInfo_Filetype(level_file_info);
2389 }
2390
2391 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2392                               struct LevelFileInfo *lfi_to)
2393 {
2394   lfi_to->nr     = lfi_from->nr;
2395   lfi_to->type   = lfi_from->type;
2396   lfi_to->packed = lfi_from->packed;
2397
2398   setString(&lfi_to->basename, lfi_from->basename);
2399   setString(&lfi_to->filename, lfi_from->filename);
2400 }
2401
2402 // ----------------------------------------------------------------------------
2403 // functions for loading R'n'D level
2404 // ----------------------------------------------------------------------------
2405
2406 int getMappedElement(int element)
2407 {
2408   // remap some (historic, now obsolete) elements
2409
2410   switch (element)
2411   {
2412     case EL_PLAYER_OBSOLETE:
2413       element = EL_PLAYER_1;
2414       break;
2415
2416     case EL_KEY_OBSOLETE:
2417       element = EL_KEY_1;
2418       break;
2419
2420     case EL_EM_KEY_1_FILE_OBSOLETE:
2421       element = EL_EM_KEY_1;
2422       break;
2423
2424     case EL_EM_KEY_2_FILE_OBSOLETE:
2425       element = EL_EM_KEY_2;
2426       break;
2427
2428     case EL_EM_KEY_3_FILE_OBSOLETE:
2429       element = EL_EM_KEY_3;
2430       break;
2431
2432     case EL_EM_KEY_4_FILE_OBSOLETE:
2433       element = EL_EM_KEY_4;
2434       break;
2435
2436     case EL_ENVELOPE_OBSOLETE:
2437       element = EL_ENVELOPE_1;
2438       break;
2439
2440     case EL_SP_EMPTY:
2441       element = EL_EMPTY;
2442       break;
2443
2444     default:
2445       if (element >= NUM_FILE_ELEMENTS)
2446       {
2447         Warn("invalid level element %d", element);
2448
2449         element = EL_UNKNOWN;
2450       }
2451       break;
2452   }
2453
2454   return element;
2455 }
2456
2457 static int getMappedElementByVersion(int element, int game_version)
2458 {
2459   // remap some elements due to certain game version
2460
2461   if (game_version <= VERSION_IDENT(2,2,0,0))
2462   {
2463     // map game font elements
2464     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2465                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2466                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2467                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2468   }
2469
2470   if (game_version < VERSION_IDENT(3,0,0,0))
2471   {
2472     // map Supaplex gravity tube elements
2473     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2474                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2475                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2476                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2477                element);
2478   }
2479
2480   return element;
2481 }
2482
2483 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2484 {
2485   level->file_version = getFileVersion(file);
2486   level->game_version = getFileVersion(file);
2487
2488   return chunk_size;
2489 }
2490
2491 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2492 {
2493   level->creation_date.year  = getFile16BitBE(file);
2494   level->creation_date.month = getFile8Bit(file);
2495   level->creation_date.day   = getFile8Bit(file);
2496
2497   level->creation_date.src   = DATE_SRC_LEVELFILE;
2498
2499   return chunk_size;
2500 }
2501
2502 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2503 {
2504   int initial_player_stepsize;
2505   int initial_player_gravity;
2506   int i, x, y;
2507
2508   level->fieldx = getFile8Bit(file);
2509   level->fieldy = getFile8Bit(file);
2510
2511   level->time           = getFile16BitBE(file);
2512   level->gems_needed    = getFile16BitBE(file);
2513
2514   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2515     level->name[i] = getFile8Bit(file);
2516   level->name[MAX_LEVEL_NAME_LEN] = 0;
2517
2518   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2519     level->score[i] = getFile8Bit(file);
2520
2521   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2522   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2523     for (y = 0; y < 3; y++)
2524       for (x = 0; x < 3; x++)
2525         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2526
2527   level->amoeba_speed           = getFile8Bit(file);
2528   level->time_magic_wall        = getFile8Bit(file);
2529   level->time_wheel             = getFile8Bit(file);
2530   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2531
2532   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2533                                    STEPSIZE_NORMAL);
2534
2535   for (i = 0; i < MAX_PLAYERS; i++)
2536     level->initial_player_stepsize[i] = initial_player_stepsize;
2537
2538   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2539
2540   for (i = 0; i < MAX_PLAYERS; i++)
2541     level->initial_player_gravity[i] = initial_player_gravity;
2542
2543   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2544   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2545
2546   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2547
2548   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2549   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2550   level->can_move_into_acid_bits = getFile32BitBE(file);
2551   level->dont_collide_with_bits = getFile8Bit(file);
2552
2553   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2554   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2555
2556   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2557   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2558   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2559
2560   level->game_engine_type       = getFile8Bit(file);
2561
2562   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2563
2564   return chunk_size;
2565 }
2566
2567 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2568 {
2569   int i;
2570
2571   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2572     level->name[i] = getFile8Bit(file);
2573   level->name[MAX_LEVEL_NAME_LEN] = 0;
2574
2575   return chunk_size;
2576 }
2577
2578 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2579 {
2580   int i;
2581
2582   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2583     level->author[i] = getFile8Bit(file);
2584   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2585
2586   return chunk_size;
2587 }
2588
2589 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2590 {
2591   int x, y;
2592   int chunk_size_expected = level->fieldx * level->fieldy;
2593
2594   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2595      stored with 16-bit encoding (and should be twice as big then).
2596      Even worse, playfield data was stored 16-bit when only yamyam content
2597      contained 16-bit elements and vice versa. */
2598
2599   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2600     chunk_size_expected *= 2;
2601
2602   if (chunk_size_expected != chunk_size)
2603   {
2604     ReadUnusedBytesFromFile(file, chunk_size);
2605     return chunk_size_expected;
2606   }
2607
2608   for (y = 0; y < level->fieldy; y++)
2609     for (x = 0; x < level->fieldx; x++)
2610       level->field[x][y] =
2611         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2612                          getFile8Bit(file));
2613   return chunk_size;
2614 }
2615
2616 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2617 {
2618   int i, x, y;
2619   int header_size = 4;
2620   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2621   int chunk_size_expected = header_size + content_size;
2622
2623   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2624      stored with 16-bit encoding (and should be twice as big then).
2625      Even worse, playfield data was stored 16-bit when only yamyam content
2626      contained 16-bit elements and vice versa. */
2627
2628   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2629     chunk_size_expected += content_size;
2630
2631   if (chunk_size_expected != chunk_size)
2632   {
2633     ReadUnusedBytesFromFile(file, chunk_size);
2634     return chunk_size_expected;
2635   }
2636
2637   getFile8Bit(file);
2638   level->num_yamyam_contents = getFile8Bit(file);
2639   getFile8Bit(file);
2640   getFile8Bit(file);
2641
2642   // correct invalid number of content fields -- should never happen
2643   if (level->num_yamyam_contents < 1 ||
2644       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2645     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2646
2647   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2648     for (y = 0; y < 3; y++)
2649       for (x = 0; x < 3; x++)
2650         level->yamyam_content[i].e[x][y] =
2651           getMappedElement(level->encoding_16bit_field ?
2652                            getFile16BitBE(file) : getFile8Bit(file));
2653   return chunk_size;
2654 }
2655
2656 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2657 {
2658   int i, x, y;
2659   int element;
2660   int num_contents;
2661   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2662
2663   element = getMappedElement(getFile16BitBE(file));
2664   num_contents = getFile8Bit(file);
2665
2666   getFile8Bit(file);    // content x size (unused)
2667   getFile8Bit(file);    // content y size (unused)
2668
2669   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2670
2671   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2672     for (y = 0; y < 3; y++)
2673       for (x = 0; x < 3; x++)
2674         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2675
2676   // correct invalid number of content fields -- should never happen
2677   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2678     num_contents = STD_ELEMENT_CONTENTS;
2679
2680   if (element == EL_YAMYAM)
2681   {
2682     level->num_yamyam_contents = num_contents;
2683
2684     for (i = 0; i < num_contents; i++)
2685       for (y = 0; y < 3; y++)
2686         for (x = 0; x < 3; x++)
2687           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2688   }
2689   else if (element == EL_BD_AMOEBA)
2690   {
2691     level->amoeba_content = content_array[0][0][0];
2692   }
2693   else
2694   {
2695     Warn("cannot load content for element '%d'", element);
2696   }
2697
2698   return chunk_size;
2699 }
2700
2701 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2702 {
2703   int i;
2704   int element;
2705   int envelope_nr;
2706   int envelope_len;
2707   int chunk_size_expected;
2708
2709   element = getMappedElement(getFile16BitBE(file));
2710   if (!IS_ENVELOPE(element))
2711     element = EL_ENVELOPE_1;
2712
2713   envelope_nr = element - EL_ENVELOPE_1;
2714
2715   envelope_len = getFile16BitBE(file);
2716
2717   level->envelope[envelope_nr].xsize = getFile8Bit(file);
2718   level->envelope[envelope_nr].ysize = getFile8Bit(file);
2719
2720   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2721
2722   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2723   if (chunk_size_expected != chunk_size)
2724   {
2725     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2726     return chunk_size_expected;
2727   }
2728
2729   for (i = 0; i < envelope_len; i++)
2730     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2731
2732   return chunk_size;
2733 }
2734
2735 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2736 {
2737   int num_changed_custom_elements = getFile16BitBE(file);
2738   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2739   int i;
2740
2741   if (chunk_size_expected != chunk_size)
2742   {
2743     ReadUnusedBytesFromFile(file, chunk_size - 2);
2744     return chunk_size_expected;
2745   }
2746
2747   for (i = 0; i < num_changed_custom_elements; i++)
2748   {
2749     int element = getMappedElement(getFile16BitBE(file));
2750     int properties = getFile32BitBE(file);
2751
2752     if (IS_CUSTOM_ELEMENT(element))
2753       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2754     else
2755       Warn("invalid custom element number %d", element);
2756
2757     // older game versions that wrote level files with CUS1 chunks used
2758     // different default push delay values (not yet stored in level file)
2759     element_info[element].push_delay_fixed = 2;
2760     element_info[element].push_delay_random = 8;
2761   }
2762
2763   level->file_has_custom_elements = TRUE;
2764
2765   return chunk_size;
2766 }
2767
2768 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2769 {
2770   int num_changed_custom_elements = getFile16BitBE(file);
2771   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2772   int i;
2773
2774   if (chunk_size_expected != chunk_size)
2775   {
2776     ReadUnusedBytesFromFile(file, chunk_size - 2);
2777     return chunk_size_expected;
2778   }
2779
2780   for (i = 0; i < num_changed_custom_elements; i++)
2781   {
2782     int element = getMappedElement(getFile16BitBE(file));
2783     int custom_target_element = getMappedElement(getFile16BitBE(file));
2784
2785     if (IS_CUSTOM_ELEMENT(element))
2786       element_info[element].change->target_element = custom_target_element;
2787     else
2788       Warn("invalid custom element number %d", element);
2789   }
2790
2791   level->file_has_custom_elements = TRUE;
2792
2793   return chunk_size;
2794 }
2795
2796 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2797 {
2798   int num_changed_custom_elements = getFile16BitBE(file);
2799   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2800   int i, j, x, y;
2801
2802   if (chunk_size_expected != chunk_size)
2803   {
2804     ReadUnusedBytesFromFile(file, chunk_size - 2);
2805     return chunk_size_expected;
2806   }
2807
2808   for (i = 0; i < num_changed_custom_elements; i++)
2809   {
2810     int element = getMappedElement(getFile16BitBE(file));
2811     struct ElementInfo *ei = &element_info[element];
2812     unsigned int event_bits;
2813
2814     if (!IS_CUSTOM_ELEMENT(element))
2815     {
2816       Warn("invalid custom element number %d", element);
2817
2818       element = EL_INTERNAL_DUMMY;
2819     }
2820
2821     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2822       ei->description[j] = getFile8Bit(file);
2823     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2824
2825     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2826
2827     // some free bytes for future properties and padding
2828     ReadUnusedBytesFromFile(file, 7);
2829
2830     ei->use_gfx_element = getFile8Bit(file);
2831     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2832
2833     ei->collect_score_initial = getFile8Bit(file);
2834     ei->collect_count_initial = getFile8Bit(file);
2835
2836     ei->push_delay_fixed = getFile16BitBE(file);
2837     ei->push_delay_random = getFile16BitBE(file);
2838     ei->move_delay_fixed = getFile16BitBE(file);
2839     ei->move_delay_random = getFile16BitBE(file);
2840
2841     ei->move_pattern = getFile16BitBE(file);
2842     ei->move_direction_initial = getFile8Bit(file);
2843     ei->move_stepsize = getFile8Bit(file);
2844
2845     for (y = 0; y < 3; y++)
2846       for (x = 0; x < 3; x++)
2847         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2848
2849     // bits 0 - 31 of "has_event[]"
2850     event_bits = getFile32BitBE(file);
2851     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2852       if (event_bits & (1 << j))
2853         ei->change->has_event[j] = TRUE;
2854
2855     ei->change->target_element = getMappedElement(getFile16BitBE(file));
2856
2857     ei->change->delay_fixed = getFile16BitBE(file);
2858     ei->change->delay_random = getFile16BitBE(file);
2859     ei->change->delay_frames = getFile16BitBE(file);
2860
2861     ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2862
2863     ei->change->explode = getFile8Bit(file);
2864     ei->change->use_target_content = getFile8Bit(file);
2865     ei->change->only_if_complete = getFile8Bit(file);
2866     ei->change->use_random_replace = getFile8Bit(file);
2867
2868     ei->change->random_percentage = getFile8Bit(file);
2869     ei->change->replace_when = getFile8Bit(file);
2870
2871     for (y = 0; y < 3; y++)
2872       for (x = 0; x < 3; x++)
2873         ei->change->target_content.e[x][y] =
2874           getMappedElement(getFile16BitBE(file));
2875
2876     ei->slippery_type = getFile8Bit(file);
2877
2878     // some free bytes for future properties and padding
2879     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2880
2881     // mark that this custom element has been modified
2882     ei->modified_settings = TRUE;
2883   }
2884
2885   level->file_has_custom_elements = TRUE;
2886
2887   return chunk_size;
2888 }
2889
2890 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2891 {
2892   struct ElementInfo *ei;
2893   int chunk_size_expected;
2894   int element;
2895   int i, j, x, y;
2896
2897   // ---------- custom element base property values (96 bytes) ----------------
2898
2899   element = getMappedElement(getFile16BitBE(file));
2900
2901   if (!IS_CUSTOM_ELEMENT(element))
2902   {
2903     Warn("invalid custom element number %d", element);
2904
2905     ReadUnusedBytesFromFile(file, chunk_size - 2);
2906
2907     return chunk_size;
2908   }
2909
2910   ei = &element_info[element];
2911
2912   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2913     ei->description[i] = getFile8Bit(file);
2914   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2915
2916   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2917
2918   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
2919
2920   ei->num_change_pages = getFile8Bit(file);
2921
2922   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2923   if (chunk_size_expected != chunk_size)
2924   {
2925     ReadUnusedBytesFromFile(file, chunk_size - 43);
2926     return chunk_size_expected;
2927   }
2928
2929   ei->ce_value_fixed_initial = getFile16BitBE(file);
2930   ei->ce_value_random_initial = getFile16BitBE(file);
2931   ei->use_last_ce_value = getFile8Bit(file);
2932
2933   ei->use_gfx_element = getFile8Bit(file);
2934   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2935
2936   ei->collect_score_initial = getFile8Bit(file);
2937   ei->collect_count_initial = getFile8Bit(file);
2938
2939   ei->drop_delay_fixed = getFile8Bit(file);
2940   ei->push_delay_fixed = getFile8Bit(file);
2941   ei->drop_delay_random = getFile8Bit(file);
2942   ei->push_delay_random = getFile8Bit(file);
2943   ei->move_delay_fixed = getFile16BitBE(file);
2944   ei->move_delay_random = getFile16BitBE(file);
2945
2946   // bits 0 - 15 of "move_pattern" ...
2947   ei->move_pattern = getFile16BitBE(file);
2948   ei->move_direction_initial = getFile8Bit(file);
2949   ei->move_stepsize = getFile8Bit(file);
2950
2951   ei->slippery_type = getFile8Bit(file);
2952
2953   for (y = 0; y < 3; y++)
2954     for (x = 0; x < 3; x++)
2955       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2956
2957   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2958   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2959   ei->move_leave_type = getFile8Bit(file);
2960
2961   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2962   ei->move_pattern |= (getFile16BitBE(file) << 16);
2963
2964   ei->access_direction = getFile8Bit(file);
2965
2966   ei->explosion_delay = getFile8Bit(file);
2967   ei->ignition_delay = getFile8Bit(file);
2968   ei->explosion_type = getFile8Bit(file);
2969
2970   // some free bytes for future custom property values and padding
2971   ReadUnusedBytesFromFile(file, 1);
2972
2973   // ---------- change page property values (48 bytes) ------------------------
2974
2975   setElementChangePages(ei, ei->num_change_pages);
2976
2977   for (i = 0; i < ei->num_change_pages; i++)
2978   {
2979     struct ElementChangeInfo *change = &ei->change_page[i];
2980     unsigned int event_bits;
2981
2982     // always start with reliable default values
2983     setElementChangeInfoToDefaults(change);
2984
2985     // bits 0 - 31 of "has_event[]" ...
2986     event_bits = getFile32BitBE(file);
2987     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2988       if (event_bits & (1 << j))
2989         change->has_event[j] = TRUE;
2990
2991     change->target_element = getMappedElement(getFile16BitBE(file));
2992
2993     change->delay_fixed = getFile16BitBE(file);
2994     change->delay_random = getFile16BitBE(file);
2995     change->delay_frames = getFile16BitBE(file);
2996
2997     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2998
2999     change->explode = getFile8Bit(file);
3000     change->use_target_content = getFile8Bit(file);
3001     change->only_if_complete = getFile8Bit(file);
3002     change->use_random_replace = getFile8Bit(file);
3003
3004     change->random_percentage = getFile8Bit(file);
3005     change->replace_when = getFile8Bit(file);
3006
3007     for (y = 0; y < 3; y++)
3008       for (x = 0; x < 3; x++)
3009         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3010
3011     change->can_change = getFile8Bit(file);
3012
3013     change->trigger_side = getFile8Bit(file);
3014
3015     change->trigger_player = getFile8Bit(file);
3016     change->trigger_page = getFile8Bit(file);
3017
3018     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3019                             CH_PAGE_ANY : (1 << change->trigger_page));
3020
3021     change->has_action = getFile8Bit(file);
3022     change->action_type = getFile8Bit(file);
3023     change->action_mode = getFile8Bit(file);
3024     change->action_arg = getFile16BitBE(file);
3025
3026     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3027     event_bits = getFile8Bit(file);
3028     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3029       if (event_bits & (1 << (j - 32)))
3030         change->has_event[j] = TRUE;
3031   }
3032
3033   // mark this custom element as modified
3034   ei->modified_settings = TRUE;
3035
3036   level->file_has_custom_elements = TRUE;
3037
3038   return chunk_size;
3039 }
3040
3041 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3042 {
3043   struct ElementInfo *ei;
3044   struct ElementGroupInfo *group;
3045   int element;
3046   int i;
3047
3048   element = getMappedElement(getFile16BitBE(file));
3049
3050   if (!IS_GROUP_ELEMENT(element))
3051   {
3052     Warn("invalid group element number %d", element);
3053
3054     ReadUnusedBytesFromFile(file, chunk_size - 2);
3055
3056     return chunk_size;
3057   }
3058
3059   ei = &element_info[element];
3060
3061   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3062     ei->description[i] = getFile8Bit(file);
3063   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3064
3065   group = element_info[element].group;
3066
3067   group->num_elements = getFile8Bit(file);
3068
3069   ei->use_gfx_element = getFile8Bit(file);
3070   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3071
3072   group->choice_mode = getFile8Bit(file);
3073
3074   // some free bytes for future values and padding
3075   ReadUnusedBytesFromFile(file, 3);
3076
3077   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3078     group->element[i] = getMappedElement(getFile16BitBE(file));
3079
3080   // mark this group element as modified
3081   element_info[element].modified_settings = TRUE;
3082
3083   level->file_has_custom_elements = TRUE;
3084
3085   return chunk_size;
3086 }
3087
3088 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3089                                 int element, int real_element)
3090 {
3091   int micro_chunk_size = 0;
3092   int conf_type = getFile8Bit(file);
3093   int byte_mask = conf_type & CONF_MASK_BYTES;
3094   boolean element_found = FALSE;
3095   int i;
3096
3097   micro_chunk_size += 1;
3098
3099   if (byte_mask == CONF_MASK_MULTI_BYTES)
3100   {
3101     int num_bytes = getFile16BitBE(file);
3102     byte *buffer = checked_malloc(num_bytes);
3103
3104     ReadBytesFromFile(file, buffer, num_bytes);
3105
3106     for (i = 0; conf[i].data_type != -1; i++)
3107     {
3108       if (conf[i].element == element &&
3109           conf[i].conf_type == conf_type)
3110       {
3111         int data_type = conf[i].data_type;
3112         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3113         int max_num_entities = conf[i].max_num_entities;
3114
3115         if (num_entities > max_num_entities)
3116         {
3117           Warn("truncating number of entities for element %d from %d to %d",
3118                element, num_entities, max_num_entities);
3119
3120           num_entities = max_num_entities;
3121         }
3122
3123         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3124                                   data_type == TYPE_CONTENT_LIST))
3125         {
3126           // for element and content lists, zero entities are not allowed
3127           Warn("found empty list of entities for element %d", element);
3128
3129           // do not set "num_entities" here to prevent reading behind buffer
3130
3131           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3132         }
3133         else
3134         {
3135           *(int *)(conf[i].num_entities) = num_entities;
3136         }
3137
3138         element_found = TRUE;
3139
3140         if (data_type == TYPE_STRING)
3141         {
3142           char *string = (char *)(conf[i].value);
3143           int j;
3144
3145           for (j = 0; j < max_num_entities; j++)
3146             string[j] = (j < num_entities ? buffer[j] : '\0');
3147         }
3148         else if (data_type == TYPE_ELEMENT_LIST)
3149         {
3150           int *element_array = (int *)(conf[i].value);
3151           int j;
3152
3153           for (j = 0; j < num_entities; j++)
3154             element_array[j] =
3155               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3156         }
3157         else if (data_type == TYPE_CONTENT_LIST)
3158         {
3159           struct Content *content= (struct Content *)(conf[i].value);
3160           int c, x, y;
3161
3162           for (c = 0; c < num_entities; c++)
3163             for (y = 0; y < 3; y++)
3164               for (x = 0; x < 3; x++)
3165                 content[c].e[x][y] =
3166                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3167         }
3168         else
3169           element_found = FALSE;
3170
3171         break;
3172       }
3173     }
3174
3175     checked_free(buffer);
3176
3177     micro_chunk_size += 2 + num_bytes;
3178   }
3179   else          // constant size configuration data (1, 2 or 4 bytes)
3180   {
3181     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3182                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3183                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3184
3185     for (i = 0; conf[i].data_type != -1; i++)
3186     {
3187       if (conf[i].element == element &&
3188           conf[i].conf_type == conf_type)
3189       {
3190         int data_type = conf[i].data_type;
3191
3192         if (data_type == TYPE_ELEMENT)
3193           value = getMappedElement(value);
3194
3195         if (data_type == TYPE_BOOLEAN)
3196           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3197         else
3198           *(int *)    (conf[i].value) = value;
3199
3200         element_found = TRUE;
3201
3202         break;
3203       }
3204     }
3205
3206     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3207   }
3208
3209   if (!element_found)
3210   {
3211     char *error_conf_chunk_bytes =
3212       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3213        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3214        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3215     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3216     int error_element = real_element;
3217
3218     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3219          error_conf_chunk_bytes, error_conf_chunk_token,
3220          error_element, EL_NAME(error_element));
3221   }
3222
3223   return micro_chunk_size;
3224 }
3225
3226 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3227 {
3228   int real_chunk_size = 0;
3229
3230   li = *level;          // copy level data into temporary buffer
3231
3232   while (!checkEndOfFile(file))
3233   {
3234     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3235
3236     if (real_chunk_size >= chunk_size)
3237       break;
3238   }
3239
3240   *level = li;          // copy temporary buffer back to level data
3241
3242   return real_chunk_size;
3243 }
3244
3245 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3246 {
3247   int real_chunk_size = 0;
3248
3249   li = *level;          // copy level data into temporary buffer
3250
3251   while (!checkEndOfFile(file))
3252   {
3253     int element = getMappedElement(getFile16BitBE(file));
3254
3255     real_chunk_size += 2;
3256     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3257                                             element, element);
3258     if (real_chunk_size >= chunk_size)
3259       break;
3260   }
3261
3262   *level = li;          // copy temporary buffer back to level data
3263
3264   return real_chunk_size;
3265 }
3266
3267 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3268 {
3269   int real_chunk_size = 0;
3270
3271   li = *level;          // copy level data into temporary buffer
3272
3273   while (!checkEndOfFile(file))
3274   {
3275     int element = getMappedElement(getFile16BitBE(file));
3276
3277     real_chunk_size += 2;
3278     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3279                                             element, element);
3280     if (real_chunk_size >= chunk_size)
3281       break;
3282   }
3283
3284   *level = li;          // copy temporary buffer back to level data
3285
3286   return real_chunk_size;
3287 }
3288
3289 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3290 {
3291   int element = getMappedElement(getFile16BitBE(file));
3292   int envelope_nr = element - EL_ENVELOPE_1;
3293   int real_chunk_size = 2;
3294
3295   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3296
3297   while (!checkEndOfFile(file))
3298   {
3299     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3300                                             -1, element);
3301
3302     if (real_chunk_size >= chunk_size)
3303       break;
3304   }
3305
3306   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3307
3308   return real_chunk_size;
3309 }
3310
3311 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3312 {
3313   int element = getMappedElement(getFile16BitBE(file));
3314   int real_chunk_size = 2;
3315   struct ElementInfo *ei = &element_info[element];
3316   int i;
3317
3318   xx_ei = *ei;          // copy element data into temporary buffer
3319
3320   xx_ei.num_change_pages = -1;
3321
3322   while (!checkEndOfFile(file))
3323   {
3324     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3325                                             -1, element);
3326     if (xx_ei.num_change_pages != -1)
3327       break;
3328
3329     if (real_chunk_size >= chunk_size)
3330       break;
3331   }
3332
3333   *ei = xx_ei;
3334
3335   if (ei->num_change_pages == -1)
3336   {
3337     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3338          EL_NAME(element));
3339
3340     ei->num_change_pages = 1;
3341
3342     setElementChangePages(ei, 1);
3343     setElementChangeInfoToDefaults(ei->change);
3344
3345     return real_chunk_size;
3346   }
3347
3348   // initialize number of change pages stored for this custom element
3349   setElementChangePages(ei, ei->num_change_pages);
3350   for (i = 0; i < ei->num_change_pages; i++)
3351     setElementChangeInfoToDefaults(&ei->change_page[i]);
3352
3353   // start with reading properties for the first change page
3354   xx_current_change_page = 0;
3355
3356   while (!checkEndOfFile(file))
3357   {
3358     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3359
3360     xx_change = *change;        // copy change data into temporary buffer
3361
3362     resetEventBits();           // reset bits; change page might have changed
3363
3364     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3365                                             -1, element);
3366
3367     *change = xx_change;
3368
3369     setEventFlagsFromEventBits(change);
3370
3371     if (real_chunk_size >= chunk_size)
3372       break;
3373   }
3374
3375   level->file_has_custom_elements = TRUE;
3376
3377   return real_chunk_size;
3378 }
3379
3380 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3381 {
3382   int element = getMappedElement(getFile16BitBE(file));
3383   int real_chunk_size = 2;
3384   struct ElementInfo *ei = &element_info[element];
3385   struct ElementGroupInfo *group = ei->group;
3386
3387   xx_ei = *ei;          // copy element data into temporary buffer
3388   xx_group = *group;    // copy group data into temporary buffer
3389
3390   while (!checkEndOfFile(file))
3391   {
3392     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3393                                             -1, element);
3394
3395     if (real_chunk_size >= chunk_size)
3396       break;
3397   }
3398
3399   *ei = xx_ei;
3400   *group = xx_group;
3401
3402   level->file_has_custom_elements = TRUE;
3403
3404   return real_chunk_size;
3405 }
3406
3407 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3408 {
3409   int element = getMappedElement(getFile16BitBE(file));
3410   int real_chunk_size = 2;
3411   struct ElementInfo *ei = &element_info[element];
3412
3413   xx_ei = *ei;          // copy element data into temporary buffer
3414
3415   while (!checkEndOfFile(file))
3416   {
3417     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3418                                             -1, element);
3419
3420     if (real_chunk_size >= chunk_size)
3421       break;
3422   }
3423
3424   *ei = xx_ei;
3425
3426   level->file_has_custom_elements = TRUE;
3427
3428   return real_chunk_size;
3429 }
3430
3431 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3432                                       struct LevelFileInfo *level_file_info,
3433                                       boolean level_info_only)
3434 {
3435   char *filename = level_file_info->filename;
3436   char cookie[MAX_LINE_LEN];
3437   char chunk_name[CHUNK_ID_LEN + 1];
3438   int chunk_size;
3439   File *file;
3440
3441   if (!(file = openFile(filename, MODE_READ)))
3442   {
3443     level->no_valid_file = TRUE;
3444     level->no_level_file = TRUE;
3445
3446     if (level_info_only)
3447       return;
3448
3449     Warn("cannot read level '%s' -- using empty level", filename);
3450
3451     if (!setup.editor.use_template_for_new_levels)
3452       return;
3453
3454     // if level file not found, try to initialize level data from template
3455     filename = getGlobalLevelTemplateFilename();
3456
3457     if (!(file = openFile(filename, MODE_READ)))
3458       return;
3459
3460     // default: for empty levels, use level template for custom elements
3461     level->use_custom_template = TRUE;
3462
3463     level->no_valid_file = FALSE;
3464   }
3465
3466   getFileChunkBE(file, chunk_name, NULL);
3467   if (strEqual(chunk_name, "RND1"))
3468   {
3469     getFile32BitBE(file);               // not used
3470
3471     getFileChunkBE(file, chunk_name, NULL);
3472     if (!strEqual(chunk_name, "CAVE"))
3473     {
3474       level->no_valid_file = TRUE;
3475
3476       Warn("unknown format of level file '%s'", filename);
3477
3478       closeFile(file);
3479
3480       return;
3481     }
3482   }
3483   else  // check for pre-2.0 file format with cookie string
3484   {
3485     strcpy(cookie, chunk_name);
3486     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3487       cookie[4] = '\0';
3488     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3489       cookie[strlen(cookie) - 1] = '\0';
3490
3491     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3492     {
3493       level->no_valid_file = TRUE;
3494
3495       Warn("unknown format of level file '%s'", filename);
3496
3497       closeFile(file);
3498
3499       return;
3500     }
3501
3502     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3503     {
3504       level->no_valid_file = TRUE;
3505
3506       Warn("unsupported version of level file '%s'", filename);
3507
3508       closeFile(file);
3509
3510       return;
3511     }
3512
3513     // pre-2.0 level files have no game version, so use file version here
3514     level->game_version = level->file_version;
3515   }
3516
3517   if (level->file_version < FILE_VERSION_1_2)
3518   {
3519     // level files from versions before 1.2.0 without chunk structure
3520     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3521     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3522   }
3523   else
3524   {
3525     static struct
3526     {
3527       char *name;
3528       int size;
3529       int (*loader)(File *, int, struct LevelInfo *);
3530     }
3531     chunk_info[] =
3532     {
3533       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3534       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3535       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3536       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3537       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3538       { "INFO", -1,                     LoadLevel_INFO },
3539       { "BODY", -1,                     LoadLevel_BODY },
3540       { "CONT", -1,                     LoadLevel_CONT },
3541       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3542       { "CNT3", -1,                     LoadLevel_CNT3 },
3543       { "CUS1", -1,                     LoadLevel_CUS1 },
3544       { "CUS2", -1,                     LoadLevel_CUS2 },
3545       { "CUS3", -1,                     LoadLevel_CUS3 },
3546       { "CUS4", -1,                     LoadLevel_CUS4 },
3547       { "GRP1", -1,                     LoadLevel_GRP1 },
3548       { "CONF", -1,                     LoadLevel_CONF },
3549       { "ELEM", -1,                     LoadLevel_ELEM },
3550       { "NOTE", -1,                     LoadLevel_NOTE },
3551       { "CUSX", -1,                     LoadLevel_CUSX },
3552       { "GRPX", -1,                     LoadLevel_GRPX },
3553       { "EMPX", -1,                     LoadLevel_EMPX },
3554
3555       {  NULL,  0,                      NULL }
3556     };
3557
3558     while (getFileChunkBE(file, chunk_name, &chunk_size))
3559     {
3560       int i = 0;
3561
3562       while (chunk_info[i].name != NULL &&
3563              !strEqual(chunk_name, chunk_info[i].name))
3564         i++;
3565
3566       if (chunk_info[i].name == NULL)
3567       {
3568         Warn("unknown chunk '%s' in level file '%s'",
3569              chunk_name, filename);
3570
3571         ReadUnusedBytesFromFile(file, chunk_size);
3572       }
3573       else if (chunk_info[i].size != -1 &&
3574                chunk_info[i].size != chunk_size)
3575       {
3576         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3577              chunk_size, chunk_name, filename);
3578
3579         ReadUnusedBytesFromFile(file, chunk_size);
3580       }
3581       else
3582       {
3583         // call function to load this level chunk
3584         int chunk_size_expected =
3585           (chunk_info[i].loader)(file, chunk_size, level);
3586
3587         // the size of some chunks cannot be checked before reading other
3588         // chunks first (like "HEAD" and "BODY") that contain some header
3589         // information, so check them here
3590         if (chunk_size_expected != chunk_size)
3591         {
3592           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3593                chunk_size, chunk_name, filename);
3594         }
3595       }
3596     }
3597   }
3598
3599   closeFile(file);
3600 }
3601
3602
3603 // ----------------------------------------------------------------------------
3604 // functions for loading EM level
3605 // ----------------------------------------------------------------------------
3606
3607 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3608 {
3609   static int ball_xy[8][2] =
3610   {
3611     { 0, 0 },
3612     { 1, 0 },
3613     { 2, 0 },
3614     { 0, 1 },
3615     { 2, 1 },
3616     { 0, 2 },
3617     { 1, 2 },
3618     { 2, 2 },
3619   };
3620   struct LevelInfo_EM *level_em = level->native_em_level;
3621   struct CAVE *cav = level_em->cav;
3622   int i, j, x, y;
3623
3624   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3625   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3626
3627   cav->time_seconds     = level->time;
3628   cav->gems_needed      = level->gems_needed;
3629
3630   cav->emerald_score    = level->score[SC_EMERALD];
3631   cav->diamond_score    = level->score[SC_DIAMOND];
3632   cav->alien_score      = level->score[SC_ROBOT];
3633   cav->tank_score       = level->score[SC_SPACESHIP];
3634   cav->bug_score        = level->score[SC_BUG];
3635   cav->eater_score      = level->score[SC_YAMYAM];
3636   cav->nut_score        = level->score[SC_NUT];
3637   cav->dynamite_score   = level->score[SC_DYNAMITE];
3638   cav->key_score        = level->score[SC_KEY];
3639   cav->exit_score       = level->score[SC_TIME_BONUS];
3640
3641   cav->num_eater_arrays = level->num_yamyam_contents;
3642
3643   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3644     for (y = 0; y < 3; y++)
3645       for (x = 0; x < 3; x++)
3646         cav->eater_array[i][y * 3 + x] =
3647           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3648
3649   cav->amoeba_time              = level->amoeba_speed;
3650   cav->wonderwall_time          = level->time_magic_wall;
3651   cav->wheel_time               = level->time_wheel;
3652
3653   cav->android_move_time        = level->android_move_time;
3654   cav->android_clone_time       = level->android_clone_time;
3655   cav->ball_random              = level->ball_random;
3656   cav->ball_active              = level->ball_active_initial;
3657   cav->ball_time                = level->ball_time;
3658   cav->num_ball_arrays          = level->num_ball_contents;
3659
3660   cav->lenses_score             = level->lenses_score;
3661   cav->magnify_score            = level->magnify_score;
3662   cav->slurp_score              = level->slurp_score;
3663
3664   cav->lenses_time              = level->lenses_time;
3665   cav->magnify_time             = level->magnify_time;
3666
3667   cav->wind_direction =
3668     map_direction_RND_to_EM(level->wind_direction_initial);
3669
3670   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3671     for (j = 0; j < 8; j++)
3672       cav->ball_array[i][j] =
3673         map_element_RND_to_EM_cave(level->ball_content[i].
3674                                    e[ball_xy[j][0]][ball_xy[j][1]]);
3675
3676   map_android_clone_elements_RND_to_EM(level);
3677
3678   // first fill the complete playfield with the empty space element
3679   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3680     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3681       cav->cave[x][y] = Cblank;
3682
3683   // then copy the real level contents from level file into the playfield
3684   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3685   {
3686     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3687
3688     if (level->field[x][y] == EL_AMOEBA_DEAD)
3689       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3690
3691     cav->cave[x][y] = new_element;
3692   }
3693
3694   for (i = 0; i < MAX_PLAYERS; i++)
3695   {
3696     cav->player_x[i] = -1;
3697     cav->player_y[i] = -1;
3698   }
3699
3700   // initialize player positions and delete players from the playfield
3701   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3702   {
3703     if (IS_PLAYER_ELEMENT(level->field[x][y]))
3704     {
3705       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3706
3707       cav->player_x[player_nr] = x;
3708       cav->player_y[player_nr] = y;
3709
3710       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3711     }
3712   }
3713 }
3714
3715 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3716 {
3717   static int ball_xy[8][2] =
3718   {
3719     { 0, 0 },
3720     { 1, 0 },
3721     { 2, 0 },
3722     { 0, 1 },
3723     { 2, 1 },
3724     { 0, 2 },
3725     { 1, 2 },
3726     { 2, 2 },
3727   };
3728   struct LevelInfo_EM *level_em = level->native_em_level;
3729   struct CAVE *cav = level_em->cav;
3730   int i, j, x, y;
3731
3732   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
3733   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3734
3735   level->time        = cav->time_seconds;
3736   level->gems_needed = cav->gems_needed;
3737
3738   sprintf(level->name, "Level %d", level->file_info.nr);
3739
3740   level->score[SC_EMERALD]      = cav->emerald_score;
3741   level->score[SC_DIAMOND]      = cav->diamond_score;
3742   level->score[SC_ROBOT]        = cav->alien_score;
3743   level->score[SC_SPACESHIP]    = cav->tank_score;
3744   level->score[SC_BUG]          = cav->bug_score;
3745   level->score[SC_YAMYAM]       = cav->eater_score;
3746   level->score[SC_NUT]          = cav->nut_score;
3747   level->score[SC_DYNAMITE]     = cav->dynamite_score;
3748   level->score[SC_KEY]          = cav->key_score;
3749   level->score[SC_TIME_BONUS]   = cav->exit_score;
3750
3751   level->num_yamyam_contents    = cav->num_eater_arrays;
3752
3753   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3754     for (y = 0; y < 3; y++)
3755       for (x = 0; x < 3; x++)
3756         level->yamyam_content[i].e[x][y] =
3757           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3758
3759   level->amoeba_speed           = cav->amoeba_time;
3760   level->time_magic_wall        = cav->wonderwall_time;
3761   level->time_wheel             = cav->wheel_time;
3762
3763   level->android_move_time      = cav->android_move_time;
3764   level->android_clone_time     = cav->android_clone_time;
3765   level->ball_random            = cav->ball_random;
3766   level->ball_active_initial    = cav->ball_active;
3767   level->ball_time              = cav->ball_time;
3768   level->num_ball_contents      = cav->num_ball_arrays;
3769
3770   level->lenses_score           = cav->lenses_score;
3771   level->magnify_score          = cav->magnify_score;
3772   level->slurp_score            = cav->slurp_score;
3773
3774   level->lenses_time            = cav->lenses_time;
3775   level->magnify_time           = cav->magnify_time;
3776
3777   level->wind_direction_initial =
3778     map_direction_EM_to_RND(cav->wind_direction);
3779
3780   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3781     for (j = 0; j < 8; j++)
3782       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3783         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3784
3785   map_android_clone_elements_EM_to_RND(level);
3786
3787   // convert the playfield (some elements need special treatment)
3788   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3789   {
3790     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3791
3792     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3793       new_element = EL_AMOEBA_DEAD;
3794
3795     level->field[x][y] = new_element;
3796   }
3797
3798   for (i = 0; i < MAX_PLAYERS; i++)
3799   {
3800     // in case of all players set to the same field, use the first player
3801     int nr = MAX_PLAYERS - i - 1;
3802     int jx = cav->player_x[nr];
3803     int jy = cav->player_y[nr];
3804
3805     if (jx != -1 && jy != -1)
3806       level->field[jx][jy] = EL_PLAYER_1 + nr;
3807   }
3808
3809   // time score is counted for each 10 seconds left in Emerald Mine levels
3810   level->time_score_base = 10;
3811 }
3812
3813
3814 // ----------------------------------------------------------------------------
3815 // functions for loading SP level
3816 // ----------------------------------------------------------------------------
3817
3818 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3819 {
3820   struct LevelInfo_SP *level_sp = level->native_sp_level;
3821   LevelInfoType *header = &level_sp->header;
3822   int i, x, y;
3823
3824   level_sp->width  = level->fieldx;
3825   level_sp->height = level->fieldy;
3826
3827   for (x = 0; x < level->fieldx; x++)
3828     for (y = 0; y < level->fieldy; y++)
3829       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3830
3831   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3832
3833   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3834     header->LevelTitle[i] = level->name[i];
3835   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3836
3837   header->InfotronsNeeded = level->gems_needed;
3838
3839   header->SpecialPortCount = 0;
3840
3841   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3842   {
3843     boolean gravity_port_found = FALSE;
3844     boolean gravity_port_valid = FALSE;
3845     int gravity_port_flag;
3846     int gravity_port_base_element;
3847     int element = level->field[x][y];
3848
3849     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3850         element <= EL_SP_GRAVITY_ON_PORT_UP)
3851     {
3852       gravity_port_found = TRUE;
3853       gravity_port_valid = TRUE;
3854       gravity_port_flag = 1;
3855       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3856     }
3857     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3858              element <= EL_SP_GRAVITY_OFF_PORT_UP)
3859     {
3860       gravity_port_found = TRUE;
3861       gravity_port_valid = TRUE;
3862       gravity_port_flag = 0;
3863       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3864     }
3865     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3866              element <= EL_SP_GRAVITY_PORT_UP)
3867     {
3868       // change R'n'D style gravity inverting special port to normal port
3869       // (there are no gravity inverting ports in native Supaplex engine)
3870
3871       gravity_port_found = TRUE;
3872       gravity_port_valid = FALSE;
3873       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3874     }
3875
3876     if (gravity_port_found)
3877     {
3878       if (gravity_port_valid &&
3879           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3880       {
3881         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3882
3883         port->PortLocation = (y * level->fieldx + x) * 2;
3884         port->Gravity = gravity_port_flag;
3885
3886         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3887
3888         header->SpecialPortCount++;
3889       }
3890       else
3891       {
3892         // change special gravity port to normal port
3893
3894         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3895       }
3896
3897       level_sp->playfield[x][y] = element - EL_SP_START;
3898     }
3899   }
3900 }
3901
3902 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3903 {
3904   struct LevelInfo_SP *level_sp = level->native_sp_level;
3905   LevelInfoType *header = &level_sp->header;
3906   boolean num_invalid_elements = 0;
3907   int i, j, x, y;
3908
3909   level->fieldx = level_sp->width;
3910   level->fieldy = level_sp->height;
3911
3912   for (x = 0; x < level->fieldx; x++)
3913   {
3914     for (y = 0; y < level->fieldy; y++)
3915     {
3916       int element_old = level_sp->playfield[x][y];
3917       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3918
3919       if (element_new == EL_UNKNOWN)
3920       {
3921         num_invalid_elements++;
3922
3923         Debug("level:native:SP", "invalid element %d at position %d, %d",
3924               element_old, x, y);
3925       }
3926
3927       level->field[x][y] = element_new;
3928     }
3929   }
3930
3931   if (num_invalid_elements > 0)
3932     Warn("found %d invalid elements%s", num_invalid_elements,
3933          (!options.debug ? " (use '--debug' for more details)" : ""));
3934
3935   for (i = 0; i < MAX_PLAYERS; i++)
3936     level->initial_player_gravity[i] =
3937       (header->InitialGravity == 1 ? TRUE : FALSE);
3938
3939   // skip leading spaces
3940   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3941     if (header->LevelTitle[i] != ' ')
3942       break;
3943
3944   // copy level title
3945   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3946     level->name[j] = header->LevelTitle[i];
3947   level->name[j] = '\0';
3948
3949   // cut trailing spaces
3950   for (; j > 0; j--)
3951     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3952       level->name[j - 1] = '\0';
3953
3954   level->gems_needed = header->InfotronsNeeded;
3955
3956   for (i = 0; i < header->SpecialPortCount; i++)
3957   {
3958     SpecialPortType *port = &header->SpecialPort[i];
3959     int port_location = port->PortLocation;
3960     int gravity = port->Gravity;
3961     int port_x, port_y, port_element;
3962
3963     port_x = (port_location / 2) % level->fieldx;
3964     port_y = (port_location / 2) / level->fieldx;
3965
3966     if (port_x < 0 || port_x >= level->fieldx ||
3967         port_y < 0 || port_y >= level->fieldy)
3968     {
3969       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3970
3971       continue;
3972     }
3973
3974     port_element = level->field[port_x][port_y];
3975
3976     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3977         port_element > EL_SP_GRAVITY_PORT_UP)
3978     {
3979       Warn("no special port at position (%d, %d)", port_x, port_y);
3980
3981       continue;
3982     }
3983
3984     // change previous (wrong) gravity inverting special port to either
3985     // gravity enabling special port or gravity disabling special port
3986     level->field[port_x][port_y] +=
3987       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3988        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3989   }
3990
3991   // change special gravity ports without database entries to normal ports
3992   for (x = 0; x < level->fieldx; x++)
3993     for (y = 0; y < level->fieldy; y++)
3994       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3995           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3996         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3997
3998   level->time = 0;                      // no time limit
3999   level->amoeba_speed = 0;
4000   level->time_magic_wall = 0;
4001   level->time_wheel = 0;
4002   level->amoeba_content = EL_EMPTY;
4003
4004   // original Supaplex does not use score values -- rate by playing time
4005   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4006     level->score[i] = 0;
4007
4008   level->rate_time_over_score = TRUE;
4009
4010   // there are no yamyams in supaplex levels
4011   for (i = 0; i < level->num_yamyam_contents; i++)
4012     for (x = 0; x < 3; x++)
4013       for (y = 0; y < 3; y++)
4014         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4015 }
4016
4017 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4018 {
4019   struct LevelInfo_SP *level_sp = level->native_sp_level;
4020   struct DemoInfo_SP *demo = &level_sp->demo;
4021   int i, j;
4022
4023   // always start with reliable default values
4024   demo->is_available = FALSE;
4025   demo->length = 0;
4026
4027   if (TAPE_IS_EMPTY(tape))
4028     return;
4029
4030   demo->level_nr = tape.level_nr;       // (currently not used)
4031
4032   level_sp->header.DemoRandomSeed = tape.random_seed;
4033
4034   demo->length = 0;
4035
4036   for (i = 0; i < tape.length; i++)
4037   {
4038     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4039     int demo_repeat = tape.pos[i].delay;
4040     int demo_entries = (demo_repeat + 15) / 16;
4041
4042     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4043     {
4044       Warn("tape truncated: size exceeds maximum SP demo size %d",
4045            SP_MAX_TAPE_LEN);
4046
4047       break;
4048     }
4049
4050     for (j = 0; j < demo_repeat / 16; j++)
4051       demo->data[demo->length++] = 0xf0 | demo_action;
4052
4053     if (demo_repeat % 16)
4054       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4055   }
4056
4057   demo->is_available = TRUE;
4058 }
4059
4060 static void setTapeInfoToDefaults(void);
4061
4062 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4063 {
4064   struct LevelInfo_SP *level_sp = level->native_sp_level;
4065   struct DemoInfo_SP *demo = &level_sp->demo;
4066   char *filename = level->file_info.filename;
4067   int i;
4068
4069   // always start with reliable default values
4070   setTapeInfoToDefaults();
4071
4072   if (!demo->is_available)
4073     return;
4074
4075   tape.level_nr = demo->level_nr;       // (currently not used)
4076   tape.random_seed = level_sp->header.DemoRandomSeed;
4077
4078   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4079
4080   tape.counter = 0;
4081   tape.pos[tape.counter].delay = 0;
4082
4083   for (i = 0; i < demo->length; i++)
4084   {
4085     int demo_action = demo->data[i] & 0x0f;
4086     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4087     int tape_action = map_key_SP_to_RND(demo_action);
4088     int tape_repeat = demo_repeat + 1;
4089     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4090     boolean success = 0;
4091     int j;
4092
4093     for (j = 0; j < tape_repeat; j++)
4094       success = TapeAddAction(action);
4095
4096     if (!success)
4097     {
4098       Warn("SP demo truncated: size exceeds maximum tape size %d",
4099            MAX_TAPE_LEN);
4100
4101       break;
4102     }
4103   }
4104
4105   TapeHaltRecording();
4106 }
4107
4108
4109 // ----------------------------------------------------------------------------
4110 // functions for loading MM level
4111 // ----------------------------------------------------------------------------
4112
4113 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4114 {
4115   struct LevelInfo_MM *level_mm = level->native_mm_level;
4116   int x, y;
4117
4118   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4119   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4120
4121   level_mm->time = level->time;
4122   level_mm->kettles_needed = level->gems_needed;
4123   level_mm->auto_count_kettles = level->auto_count_gems;
4124
4125   level_mm->laser_red = level->mm_laser_red;
4126   level_mm->laser_green = level->mm_laser_green;
4127   level_mm->laser_blue = level->mm_laser_blue;
4128
4129   strcpy(level_mm->name, level->name);
4130   strcpy(level_mm->author, level->author);
4131
4132   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4133   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4134   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4135   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4136   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4137
4138   level_mm->amoeba_speed = level->amoeba_speed;
4139   level_mm->time_fuse    = level->mm_time_fuse;
4140   level_mm->time_bomb    = level->mm_time_bomb;
4141   level_mm->time_ball    = level->mm_time_ball;
4142   level_mm->time_block   = level->mm_time_block;
4143
4144   for (x = 0; x < level->fieldx; x++)
4145     for (y = 0; y < level->fieldy; y++)
4146       Ur[x][y] =
4147         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4148 }
4149
4150 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4151 {
4152   struct LevelInfo_MM *level_mm = level->native_mm_level;
4153   int x, y;
4154
4155   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4156   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4157
4158   level->time = level_mm->time;
4159   level->gems_needed = level_mm->kettles_needed;
4160   level->auto_count_gems = level_mm->auto_count_kettles;
4161
4162   level->mm_laser_red = level_mm->laser_red;
4163   level->mm_laser_green = level_mm->laser_green;
4164   level->mm_laser_blue = level_mm->laser_blue;
4165
4166   strcpy(level->name, level_mm->name);
4167
4168   // only overwrite author from 'levelinfo.conf' if author defined in level
4169   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4170     strcpy(level->author, level_mm->author);
4171
4172   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4173   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4174   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4175   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4176   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4177
4178   level->amoeba_speed  = level_mm->amoeba_speed;
4179   level->mm_time_fuse  = level_mm->time_fuse;
4180   level->mm_time_bomb  = level_mm->time_bomb;
4181   level->mm_time_ball  = level_mm->time_ball;
4182   level->mm_time_block = level_mm->time_block;
4183
4184   for (x = 0; x < level->fieldx; x++)
4185     for (y = 0; y < level->fieldy; y++)
4186       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4187 }
4188
4189
4190 // ----------------------------------------------------------------------------
4191 // functions for loading DC level
4192 // ----------------------------------------------------------------------------
4193
4194 #define DC_LEVEL_HEADER_SIZE            344
4195
4196 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4197                                         boolean init)
4198 {
4199   static int last_data_encoded;
4200   static int offset1;
4201   static int offset2;
4202   int diff;
4203   int diff_hi, diff_lo;
4204   int data_hi, data_lo;
4205   unsigned short data_decoded;
4206
4207   if (init)
4208   {
4209     last_data_encoded = 0;
4210     offset1 = -1;
4211     offset2 = 0;
4212
4213     return 0;
4214   }
4215
4216   diff = data_encoded - last_data_encoded;
4217   diff_hi = diff & ~0xff;
4218   diff_lo = diff &  0xff;
4219
4220   offset2 += diff_lo;
4221
4222   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4223   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4224   data_hi = data_hi & 0xff00;
4225
4226   data_decoded = data_hi | data_lo;
4227
4228   last_data_encoded = data_encoded;
4229
4230   offset1 = (offset1 + 1) % 31;
4231   offset2 = offset2 & 0xff;
4232
4233   return data_decoded;
4234 }
4235
4236 static int getMappedElement_DC(int element)
4237 {
4238   switch (element)
4239   {
4240     case 0x0000:
4241       element = EL_ROCK;
4242       break;
4243
4244       // 0x0117 - 0x036e: (?)
4245       // EL_DIAMOND
4246
4247       // 0x042d - 0x0684: (?)
4248       // EL_EMERALD
4249
4250     case 0x06f1:
4251       element = EL_NUT;
4252       break;
4253
4254     case 0x074c:
4255       element = EL_BOMB;
4256       break;
4257
4258     case 0x07a4:
4259       element = EL_PEARL;
4260       break;
4261
4262     case 0x0823:
4263       element = EL_CRYSTAL;
4264       break;
4265
4266     case 0x0e77:        // quicksand (boulder)
4267       element = EL_QUICKSAND_FAST_FULL;
4268       break;
4269
4270     case 0x0e99:        // slow quicksand (boulder)
4271       element = EL_QUICKSAND_FULL;
4272       break;
4273
4274     case 0x0ed2:
4275       element = EL_EM_EXIT_OPEN;
4276       break;
4277
4278     case 0x0ee3:
4279       element = EL_EM_EXIT_CLOSED;
4280       break;
4281
4282     case 0x0eeb:
4283       element = EL_EM_STEEL_EXIT_OPEN;
4284       break;
4285
4286     case 0x0efc:
4287       element = EL_EM_STEEL_EXIT_CLOSED;
4288       break;
4289
4290     case 0x0f4f:        // dynamite (lit 1)
4291       element = EL_EM_DYNAMITE_ACTIVE;
4292       break;
4293
4294     case 0x0f57:        // dynamite (lit 2)
4295       element = EL_EM_DYNAMITE_ACTIVE;
4296       break;
4297
4298     case 0x0f5f:        // dynamite (lit 3)
4299       element = EL_EM_DYNAMITE_ACTIVE;
4300       break;
4301
4302     case 0x0f67:        // dynamite (lit 4)
4303       element = EL_EM_DYNAMITE_ACTIVE;
4304       break;
4305
4306     case 0x0f81:
4307     case 0x0f82:
4308     case 0x0f83:
4309     case 0x0f84:
4310       element = EL_AMOEBA_WET;
4311       break;
4312
4313     case 0x0f85:
4314       element = EL_AMOEBA_DROP;
4315       break;
4316
4317     case 0x0fb9:
4318       element = EL_DC_MAGIC_WALL;
4319       break;
4320
4321     case 0x0fd0:
4322       element = EL_SPACESHIP_UP;
4323       break;
4324
4325     case 0x0fd9:
4326       element = EL_SPACESHIP_DOWN;
4327       break;
4328
4329     case 0x0ff1:
4330       element = EL_SPACESHIP_LEFT;
4331       break;
4332
4333     case 0x0ff9:
4334       element = EL_SPACESHIP_RIGHT;
4335       break;
4336
4337     case 0x1057:
4338       element = EL_BUG_UP;
4339       break;
4340
4341     case 0x1060:
4342       element = EL_BUG_DOWN;
4343       break;
4344
4345     case 0x1078:
4346       element = EL_BUG_LEFT;
4347       break;
4348
4349     case 0x1080:
4350       element = EL_BUG_RIGHT;
4351       break;
4352
4353     case 0x10de:
4354       element = EL_MOLE_UP;
4355       break;
4356
4357     case 0x10e7:
4358       element = EL_MOLE_DOWN;
4359       break;
4360
4361     case 0x10ff:
4362       element = EL_MOLE_LEFT;
4363       break;
4364
4365     case 0x1107:
4366       element = EL_MOLE_RIGHT;
4367       break;
4368
4369     case 0x11c0:
4370       element = EL_ROBOT;
4371       break;
4372
4373     case 0x13f5:
4374       element = EL_YAMYAM_UP;
4375       break;
4376
4377     case 0x1425:
4378       element = EL_SWITCHGATE_OPEN;
4379       break;
4380
4381     case 0x1426:
4382       element = EL_SWITCHGATE_CLOSED;
4383       break;
4384
4385     case 0x1437:
4386       element = EL_DC_SWITCHGATE_SWITCH_UP;
4387       break;
4388
4389     case 0x143a:
4390       element = EL_TIMEGATE_CLOSED;
4391       break;
4392
4393     case 0x144c:        // conveyor belt switch (green)
4394       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4395       break;
4396
4397     case 0x144f:        // conveyor belt switch (red)
4398       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4399       break;
4400
4401     case 0x1452:        // conveyor belt switch (blue)
4402       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4403       break;
4404
4405     case 0x145b:
4406       element = EL_CONVEYOR_BELT_3_MIDDLE;
4407       break;
4408
4409     case 0x1463:
4410       element = EL_CONVEYOR_BELT_3_LEFT;
4411       break;
4412
4413     case 0x146b:
4414       element = EL_CONVEYOR_BELT_3_RIGHT;
4415       break;
4416
4417     case 0x1473:
4418       element = EL_CONVEYOR_BELT_1_MIDDLE;
4419       break;
4420
4421     case 0x147b:
4422       element = EL_CONVEYOR_BELT_1_LEFT;
4423       break;
4424
4425     case 0x1483:
4426       element = EL_CONVEYOR_BELT_1_RIGHT;
4427       break;
4428
4429     case 0x148b:
4430       element = EL_CONVEYOR_BELT_4_MIDDLE;
4431       break;
4432
4433     case 0x1493:
4434       element = EL_CONVEYOR_BELT_4_LEFT;
4435       break;
4436
4437     case 0x149b:
4438       element = EL_CONVEYOR_BELT_4_RIGHT;
4439       break;
4440
4441     case 0x14ac:
4442       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4443       break;
4444
4445     case 0x14bd:
4446       element = EL_EXPANDABLE_WALL_VERTICAL;
4447       break;
4448
4449     case 0x14c6:
4450       element = EL_EXPANDABLE_WALL_ANY;
4451       break;
4452
4453     case 0x14ce:        // growing steel wall (left/right)
4454       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4455       break;
4456
4457     case 0x14df:        // growing steel wall (up/down)
4458       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4459       break;
4460
4461     case 0x14e8:        // growing steel wall (up/down/left/right)
4462       element = EL_EXPANDABLE_STEELWALL_ANY;
4463       break;
4464
4465     case 0x14e9:
4466       element = EL_SHIELD_DEADLY;
4467       break;
4468
4469     case 0x1501:
4470       element = EL_EXTRA_TIME;
4471       break;
4472
4473     case 0x154f:
4474       element = EL_ACID;
4475       break;
4476
4477     case 0x1577:
4478       element = EL_EMPTY_SPACE;
4479       break;
4480
4481     case 0x1578:        // quicksand (empty)
4482       element = EL_QUICKSAND_FAST_EMPTY;
4483       break;
4484
4485     case 0x1579:        // slow quicksand (empty)
4486       element = EL_QUICKSAND_EMPTY;
4487       break;
4488
4489       // 0x157c - 0x158b:
4490       // EL_SAND
4491
4492       // 0x1590 - 0x159f:
4493       // EL_DC_LANDMINE
4494
4495     case 0x15a0:
4496       element = EL_EM_DYNAMITE;
4497       break;
4498
4499     case 0x15a1:        // key (red)
4500       element = EL_EM_KEY_1;
4501       break;
4502
4503     case 0x15a2:        // key (yellow)
4504       element = EL_EM_KEY_2;
4505       break;
4506
4507     case 0x15a3:        // key (blue)
4508       element = EL_EM_KEY_4;
4509       break;
4510
4511     case 0x15a4:        // key (green)
4512       element = EL_EM_KEY_3;
4513       break;
4514
4515     case 0x15a5:        // key (white)
4516       element = EL_DC_KEY_WHITE;
4517       break;
4518
4519     case 0x15a6:
4520       element = EL_WALL_SLIPPERY;
4521       break;
4522
4523     case 0x15a7:
4524       element = EL_WALL;
4525       break;
4526
4527     case 0x15a8:        // wall (not round)
4528       element = EL_WALL;
4529       break;
4530
4531     case 0x15a9:        // (blue)
4532       element = EL_CHAR_A;
4533       break;
4534
4535     case 0x15aa:        // (blue)
4536       element = EL_CHAR_B;
4537       break;
4538
4539     case 0x15ab:        // (blue)
4540       element = EL_CHAR_C;
4541       break;
4542
4543     case 0x15ac:        // (blue)
4544       element = EL_CHAR_D;
4545       break;
4546
4547     case 0x15ad:        // (blue)
4548       element = EL_CHAR_E;
4549       break;
4550
4551     case 0x15ae:        // (blue)
4552       element = EL_CHAR_F;
4553       break;
4554
4555     case 0x15af:        // (blue)
4556       element = EL_CHAR_G;
4557       break;
4558
4559     case 0x15b0:        // (blue)
4560       element = EL_CHAR_H;
4561       break;
4562
4563     case 0x15b1:        // (blue)
4564       element = EL_CHAR_I;
4565       break;
4566
4567     case 0x15b2:        // (blue)
4568       element = EL_CHAR_J;
4569       break;
4570
4571     case 0x15b3:        // (blue)
4572       element = EL_CHAR_K;
4573       break;
4574
4575     case 0x15b4:        // (blue)
4576       element = EL_CHAR_L;
4577       break;
4578
4579     case 0x15b5:        // (blue)
4580       element = EL_CHAR_M;
4581       break;
4582
4583     case 0x15b6:        // (blue)
4584       element = EL_CHAR_N;
4585       break;
4586
4587     case 0x15b7:        // (blue)
4588       element = EL_CHAR_O;
4589       break;
4590
4591     case 0x15b8:        // (blue)
4592       element = EL_CHAR_P;
4593       break;
4594
4595     case 0x15b9:        // (blue)
4596       element = EL_CHAR_Q;
4597       break;
4598
4599     case 0x15ba:        // (blue)
4600       element = EL_CHAR_R;
4601       break;
4602
4603     case 0x15bb:        // (blue)
4604       element = EL_CHAR_S;
4605       break;
4606
4607     case 0x15bc:        // (blue)
4608       element = EL_CHAR_T;
4609       break;
4610
4611     case 0x15bd:        // (blue)
4612       element = EL_CHAR_U;
4613       break;
4614
4615     case 0x15be:        // (blue)
4616       element = EL_CHAR_V;
4617       break;
4618
4619     case 0x15bf:        // (blue)
4620       element = EL_CHAR_W;
4621       break;
4622
4623     case 0x15c0:        // (blue)
4624       element = EL_CHAR_X;
4625       break;
4626
4627     case 0x15c1:        // (blue)
4628       element = EL_CHAR_Y;
4629       break;
4630
4631     case 0x15c2:        // (blue)
4632       element = EL_CHAR_Z;
4633       break;
4634
4635     case 0x15c3:        // (blue)
4636       element = EL_CHAR_AUMLAUT;
4637       break;
4638
4639     case 0x15c4:        // (blue)
4640       element = EL_CHAR_OUMLAUT;
4641       break;
4642
4643     case 0x15c5:        // (blue)
4644       element = EL_CHAR_UUMLAUT;
4645       break;
4646
4647     case 0x15c6:        // (blue)
4648       element = EL_CHAR_0;
4649       break;
4650
4651     case 0x15c7:        // (blue)
4652       element = EL_CHAR_1;
4653       break;
4654
4655     case 0x15c8:        // (blue)
4656       element = EL_CHAR_2;
4657       break;
4658
4659     case 0x15c9:        // (blue)
4660       element = EL_CHAR_3;
4661       break;
4662
4663     case 0x15ca:        // (blue)
4664       element = EL_CHAR_4;
4665       break;
4666
4667     case 0x15cb:        // (blue)
4668       element = EL_CHAR_5;
4669       break;
4670
4671     case 0x15cc:        // (blue)
4672       element = EL_CHAR_6;
4673       break;
4674
4675     case 0x15cd:        // (blue)
4676       element = EL_CHAR_7;
4677       break;
4678
4679     case 0x15ce:        // (blue)
4680       element = EL_CHAR_8;
4681       break;
4682
4683     case 0x15cf:        // (blue)
4684       element = EL_CHAR_9;
4685       break;
4686
4687     case 0x15d0:        // (blue)
4688       element = EL_CHAR_PERIOD;
4689       break;
4690
4691     case 0x15d1:        // (blue)
4692       element = EL_CHAR_EXCLAM;
4693       break;
4694
4695     case 0x15d2:        // (blue)
4696       element = EL_CHAR_COLON;
4697       break;
4698
4699     case 0x15d3:        // (blue)
4700       element = EL_CHAR_LESS;
4701       break;
4702
4703     case 0x15d4:        // (blue)
4704       element = EL_CHAR_GREATER;
4705       break;
4706
4707     case 0x15d5:        // (blue)
4708       element = EL_CHAR_QUESTION;
4709       break;
4710
4711     case 0x15d6:        // (blue)
4712       element = EL_CHAR_COPYRIGHT;
4713       break;
4714
4715     case 0x15d7:        // (blue)
4716       element = EL_CHAR_UP;
4717       break;
4718
4719     case 0x15d8:        // (blue)
4720       element = EL_CHAR_DOWN;
4721       break;
4722
4723     case 0x15d9:        // (blue)
4724       element = EL_CHAR_BUTTON;
4725       break;
4726
4727     case 0x15da:        // (blue)
4728       element = EL_CHAR_PLUS;
4729       break;
4730
4731     case 0x15db:        // (blue)
4732       element = EL_CHAR_MINUS;
4733       break;
4734
4735     case 0x15dc:        // (blue)
4736       element = EL_CHAR_APOSTROPHE;
4737       break;
4738
4739     case 0x15dd:        // (blue)
4740       element = EL_CHAR_PARENLEFT;
4741       break;
4742
4743     case 0x15de:        // (blue)
4744       element = EL_CHAR_PARENRIGHT;
4745       break;
4746
4747     case 0x15df:        // (green)
4748       element = EL_CHAR_A;
4749       break;
4750
4751     case 0x15e0:        // (green)
4752       element = EL_CHAR_B;
4753       break;
4754
4755     case 0x15e1:        // (green)
4756       element = EL_CHAR_C;
4757       break;
4758
4759     case 0x15e2:        // (green)
4760       element = EL_CHAR_D;
4761       break;
4762
4763     case 0x15e3:        // (green)
4764       element = EL_CHAR_E;
4765       break;
4766
4767     case 0x15e4:        // (green)
4768       element = EL_CHAR_F;
4769       break;
4770
4771     case 0x15e5:        // (green)
4772       element = EL_CHAR_G;
4773       break;
4774
4775     case 0x15e6:        // (green)
4776       element = EL_CHAR_H;
4777       break;
4778
4779     case 0x15e7:        // (green)
4780       element = EL_CHAR_I;
4781       break;
4782
4783     case 0x15e8:        // (green)
4784       element = EL_CHAR_J;
4785       break;
4786
4787     case 0x15e9:        // (green)
4788       element = EL_CHAR_K;
4789       break;
4790
4791     case 0x15ea:        // (green)
4792       element = EL_CHAR_L;
4793       break;
4794
4795     case 0x15eb:        // (green)
4796       element = EL_CHAR_M;
4797       break;
4798
4799     case 0x15ec:        // (green)
4800       element = EL_CHAR_N;
4801       break;
4802
4803     case 0x15ed:        // (green)
4804       element = EL_CHAR_O;
4805       break;
4806
4807     case 0x15ee:        // (green)
4808       element = EL_CHAR_P;
4809       break;
4810
4811     case 0x15ef:        // (green)
4812       element = EL_CHAR_Q;
4813       break;
4814
4815     case 0x15f0:        // (green)
4816       element = EL_CHAR_R;
4817       break;
4818
4819     case 0x15f1:        // (green)
4820       element = EL_CHAR_S;
4821       break;
4822
4823     case 0x15f2:        // (green)
4824       element = EL_CHAR_T;
4825       break;
4826
4827     case 0x15f3:        // (green)
4828       element = EL_CHAR_U;
4829       break;
4830
4831     case 0x15f4:        // (green)
4832       element = EL_CHAR_V;
4833       break;
4834
4835     case 0x15f5:        // (green)
4836       element = EL_CHAR_W;
4837       break;
4838
4839     case 0x15f6:        // (green)
4840       element = EL_CHAR_X;
4841       break;
4842
4843     case 0x15f7:        // (green)
4844       element = EL_CHAR_Y;
4845       break;
4846
4847     case 0x15f8:        // (green)
4848       element = EL_CHAR_Z;
4849       break;
4850
4851     case 0x15f9:        // (green)
4852       element = EL_CHAR_AUMLAUT;
4853       break;
4854
4855     case 0x15fa:        // (green)
4856       element = EL_CHAR_OUMLAUT;
4857       break;
4858
4859     case 0x15fb:        // (green)
4860       element = EL_CHAR_UUMLAUT;
4861       break;
4862
4863     case 0x15fc:        // (green)
4864       element = EL_CHAR_0;
4865       break;
4866
4867     case 0x15fd:        // (green)
4868       element = EL_CHAR_1;
4869       break;
4870
4871     case 0x15fe:        // (green)
4872       element = EL_CHAR_2;
4873       break;
4874
4875     case 0x15ff:        // (green)
4876       element = EL_CHAR_3;
4877       break;
4878
4879     case 0x1600:        // (green)
4880       element = EL_CHAR_4;
4881       break;
4882
4883     case 0x1601:        // (green)
4884       element = EL_CHAR_5;
4885       break;
4886
4887     case 0x1602:        // (green)
4888       element = EL_CHAR_6;
4889       break;
4890
4891     case 0x1603:        // (green)
4892       element = EL_CHAR_7;
4893       break;
4894
4895     case 0x1604:        // (green)
4896       element = EL_CHAR_8;
4897       break;
4898
4899     case 0x1605:        // (green)
4900       element = EL_CHAR_9;
4901       break;
4902
4903     case 0x1606:        // (green)
4904       element = EL_CHAR_PERIOD;
4905       break;
4906
4907     case 0x1607:        // (green)
4908       element = EL_CHAR_EXCLAM;
4909       break;
4910
4911     case 0x1608:        // (green)
4912       element = EL_CHAR_COLON;
4913       break;
4914
4915     case 0x1609:        // (green)
4916       element = EL_CHAR_LESS;
4917       break;
4918
4919     case 0x160a:        // (green)
4920       element = EL_CHAR_GREATER;
4921       break;
4922
4923     case 0x160b:        // (green)
4924       element = EL_CHAR_QUESTION;
4925       break;
4926
4927     case 0x160c:        // (green)
4928       element = EL_CHAR_COPYRIGHT;
4929       break;
4930
4931     case 0x160d:        // (green)
4932       element = EL_CHAR_UP;
4933       break;
4934
4935     case 0x160e:        // (green)
4936       element = EL_CHAR_DOWN;
4937       break;
4938
4939     case 0x160f:        // (green)
4940       element = EL_CHAR_BUTTON;
4941       break;
4942
4943     case 0x1610:        // (green)
4944       element = EL_CHAR_PLUS;
4945       break;
4946
4947     case 0x1611:        // (green)
4948       element = EL_CHAR_MINUS;
4949       break;
4950
4951     case 0x1612:        // (green)
4952       element = EL_CHAR_APOSTROPHE;
4953       break;
4954
4955     case 0x1613:        // (green)
4956       element = EL_CHAR_PARENLEFT;
4957       break;
4958
4959     case 0x1614:        // (green)
4960       element = EL_CHAR_PARENRIGHT;
4961       break;
4962
4963     case 0x1615:        // (blue steel)
4964       element = EL_STEEL_CHAR_A;
4965       break;
4966
4967     case 0x1616:        // (blue steel)
4968       element = EL_STEEL_CHAR_B;
4969       break;
4970
4971     case 0x1617:        // (blue steel)
4972       element = EL_STEEL_CHAR_C;
4973       break;
4974
4975     case 0x1618:        // (blue steel)
4976       element = EL_STEEL_CHAR_D;
4977       break;
4978
4979     case 0x1619:        // (blue steel)
4980       element = EL_STEEL_CHAR_E;
4981       break;
4982
4983     case 0x161a:        // (blue steel)
4984       element = EL_STEEL_CHAR_F;
4985       break;
4986
4987     case 0x161b:        // (blue steel)
4988       element = EL_STEEL_CHAR_G;
4989       break;
4990
4991     case 0x161c:        // (blue steel)
4992       element = EL_STEEL_CHAR_H;
4993       break;
4994
4995     case 0x161d:        // (blue steel)
4996       element = EL_STEEL_CHAR_I;
4997       break;
4998
4999     case 0x161e:        // (blue steel)
5000       element = EL_STEEL_CHAR_J;
5001       break;
5002
5003     case 0x161f:        // (blue steel)
5004       element = EL_STEEL_CHAR_K;
5005       break;
5006
5007     case 0x1620:        // (blue steel)
5008       element = EL_STEEL_CHAR_L;
5009       break;
5010
5011     case 0x1621:        // (blue steel)
5012       element = EL_STEEL_CHAR_M;
5013       break;
5014
5015     case 0x1622:        // (blue steel)
5016       element = EL_STEEL_CHAR_N;
5017       break;
5018
5019     case 0x1623:        // (blue steel)
5020       element = EL_STEEL_CHAR_O;
5021       break;
5022
5023     case 0x1624:        // (blue steel)
5024       element = EL_STEEL_CHAR_P;
5025       break;
5026
5027     case 0x1625:        // (blue steel)
5028       element = EL_STEEL_CHAR_Q;
5029       break;
5030
5031     case 0x1626:        // (blue steel)
5032       element = EL_STEEL_CHAR_R;
5033       break;
5034
5035     case 0x1627:        // (blue steel)
5036       element = EL_STEEL_CHAR_S;
5037       break;
5038
5039     case 0x1628:        // (blue steel)
5040       element = EL_STEEL_CHAR_T;
5041       break;
5042
5043     case 0x1629:        // (blue steel)
5044       element = EL_STEEL_CHAR_U;
5045       break;
5046
5047     case 0x162a:        // (blue steel)
5048       element = EL_STEEL_CHAR_V;
5049       break;
5050
5051     case 0x162b:        // (blue steel)
5052       element = EL_STEEL_CHAR_W;
5053       break;
5054
5055     case 0x162c:        // (blue steel)
5056       element = EL_STEEL_CHAR_X;
5057       break;
5058
5059     case 0x162d:        // (blue steel)
5060       element = EL_STEEL_CHAR_Y;
5061       break;
5062
5063     case 0x162e:        // (blue steel)
5064       element = EL_STEEL_CHAR_Z;
5065       break;
5066
5067     case 0x162f:        // (blue steel)
5068       element = EL_STEEL_CHAR_AUMLAUT;
5069       break;
5070
5071     case 0x1630:        // (blue steel)
5072       element = EL_STEEL_CHAR_OUMLAUT;
5073       break;
5074
5075     case 0x1631:        // (blue steel)
5076       element = EL_STEEL_CHAR_UUMLAUT;
5077       break;
5078
5079     case 0x1632:        // (blue steel)
5080       element = EL_STEEL_CHAR_0;
5081       break;
5082
5083     case 0x1633:        // (blue steel)
5084       element = EL_STEEL_CHAR_1;
5085       break;
5086
5087     case 0x1634:        // (blue steel)
5088       element = EL_STEEL_CHAR_2;
5089       break;
5090
5091     case 0x1635:        // (blue steel)
5092       element = EL_STEEL_CHAR_3;
5093       break;
5094
5095     case 0x1636:        // (blue steel)
5096       element = EL_STEEL_CHAR_4;
5097       break;
5098
5099     case 0x1637:        // (blue steel)
5100       element = EL_STEEL_CHAR_5;
5101       break;
5102
5103     case 0x1638:        // (blue steel)
5104       element = EL_STEEL_CHAR_6;
5105       break;
5106
5107     case 0x1639:        // (blue steel)
5108       element = EL_STEEL_CHAR_7;
5109       break;
5110
5111     case 0x163a:        // (blue steel)
5112       element = EL_STEEL_CHAR_8;
5113       break;
5114
5115     case 0x163b:        // (blue steel)
5116       element = EL_STEEL_CHAR_9;
5117       break;
5118
5119     case 0x163c:        // (blue steel)
5120       element = EL_STEEL_CHAR_PERIOD;
5121       break;
5122
5123     case 0x163d:        // (blue steel)
5124       element = EL_STEEL_CHAR_EXCLAM;
5125       break;
5126
5127     case 0x163e:        // (blue steel)
5128       element = EL_STEEL_CHAR_COLON;
5129       break;
5130
5131     case 0x163f:        // (blue steel)
5132       element = EL_STEEL_CHAR_LESS;
5133       break;
5134
5135     case 0x1640:        // (blue steel)
5136       element = EL_STEEL_CHAR_GREATER;
5137       break;
5138
5139     case 0x1641:        // (blue steel)
5140       element = EL_STEEL_CHAR_QUESTION;
5141       break;
5142
5143     case 0x1642:        // (blue steel)
5144       element = EL_STEEL_CHAR_COPYRIGHT;
5145       break;
5146
5147     case 0x1643:        // (blue steel)
5148       element = EL_STEEL_CHAR_UP;
5149       break;
5150
5151     case 0x1644:        // (blue steel)
5152       element = EL_STEEL_CHAR_DOWN;
5153       break;
5154
5155     case 0x1645:        // (blue steel)
5156       element = EL_STEEL_CHAR_BUTTON;
5157       break;
5158
5159     case 0x1646:        // (blue steel)
5160       element = EL_STEEL_CHAR_PLUS;
5161       break;
5162
5163     case 0x1647:        // (blue steel)
5164       element = EL_STEEL_CHAR_MINUS;
5165       break;
5166
5167     case 0x1648:        // (blue steel)
5168       element = EL_STEEL_CHAR_APOSTROPHE;
5169       break;
5170
5171     case 0x1649:        // (blue steel)
5172       element = EL_STEEL_CHAR_PARENLEFT;
5173       break;
5174
5175     case 0x164a:        // (blue steel)
5176       element = EL_STEEL_CHAR_PARENRIGHT;
5177       break;
5178
5179     case 0x164b:        // (green steel)
5180       element = EL_STEEL_CHAR_A;
5181       break;
5182
5183     case 0x164c:        // (green steel)
5184       element = EL_STEEL_CHAR_B;
5185       break;
5186
5187     case 0x164d:        // (green steel)
5188       element = EL_STEEL_CHAR_C;
5189       break;
5190
5191     case 0x164e:        // (green steel)
5192       element = EL_STEEL_CHAR_D;
5193       break;
5194
5195     case 0x164f:        // (green steel)
5196       element = EL_STEEL_CHAR_E;
5197       break;
5198
5199     case 0x1650:        // (green steel)
5200       element = EL_STEEL_CHAR_F;
5201       break;
5202
5203     case 0x1651:        // (green steel)
5204       element = EL_STEEL_CHAR_G;
5205       break;
5206
5207     case 0x1652:        // (green steel)
5208       element = EL_STEEL_CHAR_H;
5209       break;
5210
5211     case 0x1653:        // (green steel)
5212       element = EL_STEEL_CHAR_I;
5213       break;
5214
5215     case 0x1654:        // (green steel)
5216       element = EL_STEEL_CHAR_J;
5217       break;
5218
5219     case 0x1655:        // (green steel)
5220       element = EL_STEEL_CHAR_K;
5221       break;
5222
5223     case 0x1656:        // (green steel)
5224       element = EL_STEEL_CHAR_L;
5225       break;
5226
5227     case 0x1657:        // (green steel)
5228       element = EL_STEEL_CHAR_M;
5229       break;
5230
5231     case 0x1658:        // (green steel)
5232       element = EL_STEEL_CHAR_N;
5233       break;
5234
5235     case 0x1659:        // (green steel)
5236       element = EL_STEEL_CHAR_O;
5237       break;
5238
5239     case 0x165a:        // (green steel)
5240       element = EL_STEEL_CHAR_P;
5241       break;
5242
5243     case 0x165b:        // (green steel)
5244       element = EL_STEEL_CHAR_Q;
5245       break;
5246
5247     case 0x165c:        // (green steel)
5248       element = EL_STEEL_CHAR_R;
5249       break;
5250
5251     case 0x165d:        // (green steel)
5252       element = EL_STEEL_CHAR_S;
5253       break;
5254
5255     case 0x165e:        // (green steel)
5256       element = EL_STEEL_CHAR_T;
5257       break;
5258
5259     case 0x165f:        // (green steel)
5260       element = EL_STEEL_CHAR_U;
5261       break;
5262
5263     case 0x1660:        // (green steel)
5264       element = EL_STEEL_CHAR_V;
5265       break;
5266
5267     case 0x1661:        // (green steel)
5268       element = EL_STEEL_CHAR_W;
5269       break;
5270
5271     case 0x1662:        // (green steel)
5272       element = EL_STEEL_CHAR_X;
5273       break;
5274
5275     case 0x1663:        // (green steel)
5276       element = EL_STEEL_CHAR_Y;
5277       break;
5278
5279     case 0x1664:        // (green steel)
5280       element = EL_STEEL_CHAR_Z;
5281       break;
5282
5283     case 0x1665:        // (green steel)
5284       element = EL_STEEL_CHAR_AUMLAUT;
5285       break;
5286
5287     case 0x1666:        // (green steel)
5288       element = EL_STEEL_CHAR_OUMLAUT;
5289       break;
5290
5291     case 0x1667:        // (green steel)
5292       element = EL_STEEL_CHAR_UUMLAUT;
5293       break;
5294
5295     case 0x1668:        // (green steel)
5296       element = EL_STEEL_CHAR_0;
5297       break;
5298
5299     case 0x1669:        // (green steel)
5300       element = EL_STEEL_CHAR_1;
5301       break;
5302
5303     case 0x166a:        // (green steel)
5304       element = EL_STEEL_CHAR_2;
5305       break;
5306
5307     case 0x166b:        // (green steel)
5308       element = EL_STEEL_CHAR_3;
5309       break;
5310
5311     case 0x166c:        // (green steel)
5312       element = EL_STEEL_CHAR_4;
5313       break;
5314
5315     case 0x166d:        // (green steel)
5316       element = EL_STEEL_CHAR_5;
5317       break;
5318
5319     case 0x166e:        // (green steel)
5320       element = EL_STEEL_CHAR_6;
5321       break;
5322
5323     case 0x166f:        // (green steel)
5324       element = EL_STEEL_CHAR_7;
5325       break;
5326
5327     case 0x1670:        // (green steel)
5328       element = EL_STEEL_CHAR_8;
5329       break;
5330
5331     case 0x1671:        // (green steel)
5332       element = EL_STEEL_CHAR_9;
5333       break;
5334
5335     case 0x1672:        // (green steel)
5336       element = EL_STEEL_CHAR_PERIOD;
5337       break;
5338
5339     case 0x1673:        // (green steel)
5340       element = EL_STEEL_CHAR_EXCLAM;
5341       break;
5342
5343     case 0x1674:        // (green steel)
5344       element = EL_STEEL_CHAR_COLON;
5345       break;
5346
5347     case 0x1675:        // (green steel)
5348       element = EL_STEEL_CHAR_LESS;
5349       break;
5350
5351     case 0x1676:        // (green steel)
5352       element = EL_STEEL_CHAR_GREATER;
5353       break;
5354
5355     case 0x1677:        // (green steel)
5356       element = EL_STEEL_CHAR_QUESTION;
5357       break;
5358
5359     case 0x1678:        // (green steel)
5360       element = EL_STEEL_CHAR_COPYRIGHT;
5361       break;
5362
5363     case 0x1679:        // (green steel)
5364       element = EL_STEEL_CHAR_UP;
5365       break;
5366
5367     case 0x167a:        // (green steel)
5368       element = EL_STEEL_CHAR_DOWN;
5369       break;
5370
5371     case 0x167b:        // (green steel)
5372       element = EL_STEEL_CHAR_BUTTON;
5373       break;
5374
5375     case 0x167c:        // (green steel)
5376       element = EL_STEEL_CHAR_PLUS;
5377       break;
5378
5379     case 0x167d:        // (green steel)
5380       element = EL_STEEL_CHAR_MINUS;
5381       break;
5382
5383     case 0x167e:        // (green steel)
5384       element = EL_STEEL_CHAR_APOSTROPHE;
5385       break;
5386
5387     case 0x167f:        // (green steel)
5388       element = EL_STEEL_CHAR_PARENLEFT;
5389       break;
5390
5391     case 0x1680:        // (green steel)
5392       element = EL_STEEL_CHAR_PARENRIGHT;
5393       break;
5394
5395     case 0x1681:        // gate (red)
5396       element = EL_EM_GATE_1;
5397       break;
5398
5399     case 0x1682:        // secret gate (red)
5400       element = EL_EM_GATE_1_GRAY;
5401       break;
5402
5403     case 0x1683:        // gate (yellow)
5404       element = EL_EM_GATE_2;
5405       break;
5406
5407     case 0x1684:        // secret gate (yellow)
5408       element = EL_EM_GATE_2_GRAY;
5409       break;
5410
5411     case 0x1685:        // gate (blue)
5412       element = EL_EM_GATE_4;
5413       break;
5414
5415     case 0x1686:        // secret gate (blue)
5416       element = EL_EM_GATE_4_GRAY;
5417       break;
5418
5419     case 0x1687:        // gate (green)
5420       element = EL_EM_GATE_3;
5421       break;
5422
5423     case 0x1688:        // secret gate (green)
5424       element = EL_EM_GATE_3_GRAY;
5425       break;
5426
5427     case 0x1689:        // gate (white)
5428       element = EL_DC_GATE_WHITE;
5429       break;
5430
5431     case 0x168a:        // secret gate (white)
5432       element = EL_DC_GATE_WHITE_GRAY;
5433       break;
5434
5435     case 0x168b:        // secret gate (no key)
5436       element = EL_DC_GATE_FAKE_GRAY;
5437       break;
5438
5439     case 0x168c:
5440       element = EL_ROBOT_WHEEL;
5441       break;
5442
5443     case 0x168d:
5444       element = EL_DC_TIMEGATE_SWITCH;
5445       break;
5446
5447     case 0x168e:
5448       element = EL_ACID_POOL_BOTTOM;
5449       break;
5450
5451     case 0x168f:
5452       element = EL_ACID_POOL_TOPLEFT;
5453       break;
5454
5455     case 0x1690:
5456       element = EL_ACID_POOL_TOPRIGHT;
5457       break;
5458
5459     case 0x1691:
5460       element = EL_ACID_POOL_BOTTOMLEFT;
5461       break;
5462
5463     case 0x1692:
5464       element = EL_ACID_POOL_BOTTOMRIGHT;
5465       break;
5466
5467     case 0x1693:
5468       element = EL_STEELWALL;
5469       break;
5470
5471     case 0x1694:
5472       element = EL_STEELWALL_SLIPPERY;
5473       break;
5474
5475     case 0x1695:        // steel wall (not round)
5476       element = EL_STEELWALL;
5477       break;
5478
5479     case 0x1696:        // steel wall (left)
5480       element = EL_DC_STEELWALL_1_LEFT;
5481       break;
5482
5483     case 0x1697:        // steel wall (bottom)
5484       element = EL_DC_STEELWALL_1_BOTTOM;
5485       break;
5486
5487     case 0x1698:        // steel wall (right)
5488       element = EL_DC_STEELWALL_1_RIGHT;
5489       break;
5490
5491     case 0x1699:        // steel wall (top)
5492       element = EL_DC_STEELWALL_1_TOP;
5493       break;
5494
5495     case 0x169a:        // steel wall (left/bottom)
5496       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5497       break;
5498
5499     case 0x169b:        // steel wall (right/bottom)
5500       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5501       break;
5502
5503     case 0x169c:        // steel wall (right/top)
5504       element = EL_DC_STEELWALL_1_TOPRIGHT;
5505       break;
5506
5507     case 0x169d:        // steel wall (left/top)
5508       element = EL_DC_STEELWALL_1_TOPLEFT;
5509       break;
5510
5511     case 0x169e:        // steel wall (right/bottom small)
5512       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5513       break;
5514
5515     case 0x169f:        // steel wall (left/bottom small)
5516       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5517       break;
5518
5519     case 0x16a0:        // steel wall (right/top small)
5520       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5521       break;
5522
5523     case 0x16a1:        // steel wall (left/top small)
5524       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5525       break;
5526
5527     case 0x16a2:        // steel wall (left/right)
5528       element = EL_DC_STEELWALL_1_VERTICAL;
5529       break;
5530
5531     case 0x16a3:        // steel wall (top/bottom)
5532       element = EL_DC_STEELWALL_1_HORIZONTAL;
5533       break;
5534
5535     case 0x16a4:        // steel wall 2 (left end)
5536       element = EL_DC_STEELWALL_2_LEFT;
5537       break;
5538
5539     case 0x16a5:        // steel wall 2 (right end)
5540       element = EL_DC_STEELWALL_2_RIGHT;
5541       break;
5542
5543     case 0x16a6:        // steel wall 2 (top end)
5544       element = EL_DC_STEELWALL_2_TOP;
5545       break;
5546
5547     case 0x16a7:        // steel wall 2 (bottom end)
5548       element = EL_DC_STEELWALL_2_BOTTOM;
5549       break;
5550
5551     case 0x16a8:        // steel wall 2 (left/right)
5552       element = EL_DC_STEELWALL_2_HORIZONTAL;
5553       break;
5554
5555     case 0x16a9:        // steel wall 2 (up/down)
5556       element = EL_DC_STEELWALL_2_VERTICAL;
5557       break;
5558
5559     case 0x16aa:        // steel wall 2 (mid)
5560       element = EL_DC_STEELWALL_2_MIDDLE;
5561       break;
5562
5563     case 0x16ab:
5564       element = EL_SIGN_EXCLAMATION;
5565       break;
5566
5567     case 0x16ac:
5568       element = EL_SIGN_RADIOACTIVITY;
5569       break;
5570
5571     case 0x16ad:
5572       element = EL_SIGN_STOP;
5573       break;
5574
5575     case 0x16ae:
5576       element = EL_SIGN_WHEELCHAIR;
5577       break;
5578
5579     case 0x16af:
5580       element = EL_SIGN_PARKING;
5581       break;
5582
5583     case 0x16b0:
5584       element = EL_SIGN_NO_ENTRY;
5585       break;
5586
5587     case 0x16b1:
5588       element = EL_SIGN_HEART;
5589       break;
5590
5591     case 0x16b2:
5592       element = EL_SIGN_GIVE_WAY;
5593       break;
5594
5595     case 0x16b3:
5596       element = EL_SIGN_ENTRY_FORBIDDEN;
5597       break;
5598
5599     case 0x16b4:
5600       element = EL_SIGN_EMERGENCY_EXIT;
5601       break;
5602
5603     case 0x16b5:
5604       element = EL_SIGN_YIN_YANG;
5605       break;
5606
5607     case 0x16b6:
5608       element = EL_WALL_EMERALD;
5609       break;
5610
5611     case 0x16b7:
5612       element = EL_WALL_DIAMOND;
5613       break;
5614
5615     case 0x16b8:
5616       element = EL_WALL_PEARL;
5617       break;
5618
5619     case 0x16b9:
5620       element = EL_WALL_CRYSTAL;
5621       break;
5622
5623     case 0x16ba:
5624       element = EL_INVISIBLE_WALL;
5625       break;
5626
5627     case 0x16bb:
5628       element = EL_INVISIBLE_STEELWALL;
5629       break;
5630
5631       // 0x16bc - 0x16cb:
5632       // EL_INVISIBLE_SAND
5633
5634     case 0x16cc:
5635       element = EL_LIGHT_SWITCH;
5636       break;
5637
5638     case 0x16cd:
5639       element = EL_ENVELOPE_1;
5640       break;
5641
5642     default:
5643       if (element >= 0x0117 && element <= 0x036e)       // (?)
5644         element = EL_DIAMOND;
5645       else if (element >= 0x042d && element <= 0x0684)  // (?)
5646         element = EL_EMERALD;
5647       else if (element >= 0x157c && element <= 0x158b)
5648         element = EL_SAND;
5649       else if (element >= 0x1590 && element <= 0x159f)
5650         element = EL_DC_LANDMINE;
5651       else if (element >= 0x16bc && element <= 0x16cb)
5652         element = EL_INVISIBLE_SAND;
5653       else
5654       {
5655         Warn("unknown Diamond Caves element 0x%04x", element);
5656
5657         element = EL_UNKNOWN;
5658       }
5659       break;
5660   }
5661
5662   return getMappedElement(element);
5663 }
5664
5665 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5666                                        int nr)
5667 {
5668   byte header[DC_LEVEL_HEADER_SIZE];
5669   int envelope_size;
5670   int envelope_header_pos = 62;
5671   int envelope_content_pos = 94;
5672   int level_name_pos = 251;
5673   int level_author_pos = 292;
5674   int envelope_header_len;
5675   int envelope_content_len;
5676   int level_name_len;
5677   int level_author_len;
5678   int fieldx, fieldy;
5679   int num_yamyam_contents;
5680   int i, x, y;
5681
5682   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5683
5684   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5685   {
5686     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5687
5688     header[i * 2 + 0] = header_word >> 8;
5689     header[i * 2 + 1] = header_word & 0xff;
5690   }
5691
5692   // read some values from level header to check level decoding integrity
5693   fieldx = header[6] | (header[7] << 8);
5694   fieldy = header[8] | (header[9] << 8);
5695   num_yamyam_contents = header[60] | (header[61] << 8);
5696
5697   // do some simple sanity checks to ensure that level was correctly decoded
5698   if (fieldx < 1 || fieldx > 256 ||
5699       fieldy < 1 || fieldy > 256 ||
5700       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5701   {
5702     level->no_valid_file = TRUE;
5703
5704     Warn("cannot decode level from stream -- using empty level");
5705
5706     return;
5707   }
5708
5709   // maximum envelope header size is 31 bytes
5710   envelope_header_len   = header[envelope_header_pos];
5711   // maximum envelope content size is 110 (156?) bytes
5712   envelope_content_len  = header[envelope_content_pos];
5713
5714   // maximum level title size is 40 bytes
5715   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5716   // maximum level author size is 30 (51?) bytes
5717   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5718
5719   envelope_size = 0;
5720
5721   for (i = 0; i < envelope_header_len; i++)
5722     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5723       level->envelope[0].text[envelope_size++] =
5724         header[envelope_header_pos + 1 + i];
5725
5726   if (envelope_header_len > 0 && envelope_content_len > 0)
5727   {
5728     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5729       level->envelope[0].text[envelope_size++] = '\n';
5730     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5731       level->envelope[0].text[envelope_size++] = '\n';
5732   }
5733
5734   for (i = 0; i < envelope_content_len; i++)
5735     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5736       level->envelope[0].text[envelope_size++] =
5737         header[envelope_content_pos + 1 + i];
5738
5739   level->envelope[0].text[envelope_size] = '\0';
5740
5741   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5742   level->envelope[0].ysize = 10;
5743   level->envelope[0].autowrap = TRUE;
5744   level->envelope[0].centered = TRUE;
5745
5746   for (i = 0; i < level_name_len; i++)
5747     level->name[i] = header[level_name_pos + 1 + i];
5748   level->name[level_name_len] = '\0';
5749
5750   for (i = 0; i < level_author_len; i++)
5751     level->author[i] = header[level_author_pos + 1 + i];
5752   level->author[level_author_len] = '\0';
5753
5754   num_yamyam_contents = header[60] | (header[61] << 8);
5755   level->num_yamyam_contents =
5756     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5757
5758   for (i = 0; i < num_yamyam_contents; i++)
5759   {
5760     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5761     {
5762       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5763       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5764
5765       if (i < MAX_ELEMENT_CONTENTS)
5766         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5767     }
5768   }
5769
5770   fieldx = header[6] | (header[7] << 8);
5771   fieldy = header[8] | (header[9] << 8);
5772   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5773   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5774
5775   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5776   {
5777     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5778     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5779
5780     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5781       level->field[x][y] = getMappedElement_DC(element_dc);
5782   }
5783
5784   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5785   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5786   level->field[x][y] = EL_PLAYER_1;
5787
5788   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5789   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5790   level->field[x][y] = EL_PLAYER_2;
5791
5792   level->gems_needed            = header[18] | (header[19] << 8);
5793
5794   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5795   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5796   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5797   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5798   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5799   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5800   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5801   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5802   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5803   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5804   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5805   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5806
5807   level->time                   = header[44] | (header[45] << 8);
5808
5809   level->amoeba_speed           = header[46] | (header[47] << 8);
5810   level->time_light             = header[48] | (header[49] << 8);
5811   level->time_timegate          = header[50] | (header[51] << 8);
5812   level->time_wheel             = header[52] | (header[53] << 8);
5813   level->time_magic_wall        = header[54] | (header[55] << 8);
5814   level->extra_time             = header[56] | (header[57] << 8);
5815   level->shield_normal_time     = header[58] | (header[59] << 8);
5816
5817   // shield and extra time elements do not have a score
5818   level->score[SC_SHIELD]       = 0;
5819   level->extra_time_score       = 0;
5820
5821   // set time for normal and deadly shields to the same value
5822   level->shield_deadly_time     = level->shield_normal_time;
5823
5824   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5825   // can slip down from flat walls, like normal walls and steel walls
5826   level->em_slippery_gems = TRUE;
5827
5828   // time score is counted for each 10 seconds left in Diamond Caves levels
5829   level->time_score_base = 10;
5830 }
5831
5832 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5833                                      struct LevelFileInfo *level_file_info,
5834                                      boolean level_info_only)
5835 {
5836   char *filename = level_file_info->filename;
5837   File *file;
5838   int num_magic_bytes = 8;
5839   char magic_bytes[num_magic_bytes + 1];
5840   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5841
5842   if (!(file = openFile(filename, MODE_READ)))
5843   {
5844     level->no_valid_file = TRUE;
5845
5846     if (!level_info_only)
5847       Warn("cannot read level '%s' -- using empty level", filename);
5848
5849     return;
5850   }
5851
5852   // fseek(file, 0x0000, SEEK_SET);
5853
5854   if (level_file_info->packed)
5855   {
5856     // read "magic bytes" from start of file
5857     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5858       magic_bytes[0] = '\0';
5859
5860     // check "magic bytes" for correct file format
5861     if (!strPrefix(magic_bytes, "DC2"))
5862     {
5863       level->no_valid_file = TRUE;
5864
5865       Warn("unknown DC level file '%s' -- using empty level", filename);
5866
5867       return;
5868     }
5869
5870     if (strPrefix(magic_bytes, "DC2Win95") ||
5871         strPrefix(magic_bytes, "DC2Win98"))
5872     {
5873       int position_first_level = 0x00fa;
5874       int extra_bytes = 4;
5875       int skip_bytes;
5876
5877       // advance file stream to first level inside the level package
5878       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5879
5880       // each block of level data is followed by block of non-level data
5881       num_levels_to_skip *= 2;
5882
5883       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5884       while (num_levels_to_skip >= 0)
5885       {
5886         // advance file stream to next level inside the level package
5887         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5888         {
5889           level->no_valid_file = TRUE;
5890
5891           Warn("cannot fseek in file '%s' -- using empty level", filename);
5892
5893           return;
5894         }
5895
5896         // skip apparently unused extra bytes following each level
5897         ReadUnusedBytesFromFile(file, extra_bytes);
5898
5899         // read size of next level in level package
5900         skip_bytes = getFile32BitLE(file);
5901
5902         num_levels_to_skip--;
5903       }
5904     }
5905     else
5906     {
5907       level->no_valid_file = TRUE;
5908
5909       Warn("unknown DC2 level file '%s' -- using empty level", filename);
5910
5911       return;
5912     }
5913   }
5914
5915   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5916
5917   closeFile(file);
5918 }
5919
5920
5921 // ----------------------------------------------------------------------------
5922 // functions for loading SB level
5923 // ----------------------------------------------------------------------------
5924
5925 int getMappedElement_SB(int element_ascii, boolean use_ces)
5926 {
5927   static struct
5928   {
5929     int ascii;
5930     int sb;
5931     int ce;
5932   }
5933   sb_element_mapping[] =
5934   {
5935     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
5936     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
5937     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
5938     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
5939     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
5940     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
5941     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
5942     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
5943
5944     { 0,   -1,                      -1          },
5945   };
5946
5947   int i;
5948
5949   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5950     if (element_ascii == sb_element_mapping[i].ascii)
5951       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5952
5953   return EL_UNDEFINED;
5954 }
5955
5956 static void SetLevelSettings_SB(struct LevelInfo *level)
5957 {
5958   // time settings
5959   level->time = 0;
5960   level->use_step_counter = TRUE;
5961
5962   // score settings
5963   level->score[SC_TIME_BONUS] = 0;
5964   level->time_score_base = 1;
5965   level->rate_time_over_score = TRUE;
5966
5967   // game settings
5968   level->auto_exit_sokoban = TRUE;
5969 }
5970
5971 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5972                                      struct LevelFileInfo *level_file_info,
5973                                      boolean level_info_only)
5974 {
5975   char *filename = level_file_info->filename;
5976   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5977   char last_comment[MAX_LINE_LEN];
5978   char level_name[MAX_LINE_LEN];
5979   char *line_ptr;
5980   File *file;
5981   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5982   boolean read_continued_line = FALSE;
5983   boolean reading_playfield = FALSE;
5984   boolean got_valid_playfield_line = FALSE;
5985   boolean invalid_playfield_char = FALSE;
5986   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5987   int file_level_nr = 0;
5988   int line_nr = 0;
5989   int x = 0, y = 0;             // initialized to make compilers happy
5990
5991   last_comment[0] = '\0';
5992   level_name[0] = '\0';
5993
5994   if (!(file = openFile(filename, MODE_READ)))
5995   {
5996     level->no_valid_file = TRUE;
5997
5998     if (!level_info_only)
5999       Warn("cannot read level '%s' -- using empty level", filename);
6000
6001     return;
6002   }
6003
6004   while (!checkEndOfFile(file))
6005   {
6006     // level successfully read, but next level may follow here
6007     if (!got_valid_playfield_line && reading_playfield)
6008     {
6009       // read playfield from single level file -- skip remaining file
6010       if (!level_file_info->packed)
6011         break;
6012
6013       if (file_level_nr >= num_levels_to_skip)
6014         break;
6015
6016       file_level_nr++;
6017
6018       last_comment[0] = '\0';
6019       level_name[0] = '\0';
6020
6021       reading_playfield = FALSE;
6022     }
6023
6024     got_valid_playfield_line = FALSE;
6025
6026     // read next line of input file
6027     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6028       break;
6029
6030     // check if line was completely read and is terminated by line break
6031     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6032       line_nr++;
6033
6034     // cut trailing line break (this can be newline and/or carriage return)
6035     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6036       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6037         *line_ptr = '\0';
6038
6039     // copy raw input line for later use (mainly debugging output)
6040     strcpy(line_raw, line);
6041
6042     if (read_continued_line)
6043     {
6044       // append new line to existing line, if there is enough space
6045       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6046         strcat(previous_line, line_ptr);
6047
6048       strcpy(line, previous_line);      // copy storage buffer to line
6049
6050       read_continued_line = FALSE;
6051     }
6052
6053     // if the last character is '\', continue at next line
6054     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6055     {
6056       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6057       strcpy(previous_line, line);      // copy line to storage buffer
6058
6059       read_continued_line = TRUE;
6060
6061       continue;
6062     }
6063
6064     // skip empty lines
6065     if (line[0] == '\0')
6066       continue;
6067
6068     // extract comment text from comment line
6069     if (line[0] == ';')
6070     {
6071       for (line_ptr = line; *line_ptr; line_ptr++)
6072         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6073           break;
6074
6075       strcpy(last_comment, line_ptr);
6076
6077       continue;
6078     }
6079
6080     // extract level title text from line containing level title
6081     if (line[0] == '\'')
6082     {
6083       strcpy(level_name, &line[1]);
6084
6085       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6086         level_name[strlen(level_name) - 1] = '\0';
6087
6088       continue;
6089     }
6090
6091     // skip lines containing only spaces (or empty lines)
6092     for (line_ptr = line; *line_ptr; line_ptr++)
6093       if (*line_ptr != ' ')
6094         break;
6095     if (*line_ptr == '\0')
6096       continue;
6097
6098     // at this point, we have found a line containing part of a playfield
6099
6100     got_valid_playfield_line = TRUE;
6101
6102     if (!reading_playfield)
6103     {
6104       reading_playfield = TRUE;
6105       invalid_playfield_char = FALSE;
6106
6107       for (x = 0; x < MAX_LEV_FIELDX; x++)
6108         for (y = 0; y < MAX_LEV_FIELDY; y++)
6109           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6110
6111       level->fieldx = 0;
6112       level->fieldy = 0;
6113
6114       // start with topmost tile row
6115       y = 0;
6116     }
6117
6118     // skip playfield line if larger row than allowed
6119     if (y >= MAX_LEV_FIELDY)
6120       continue;
6121
6122     // start with leftmost tile column
6123     x = 0;
6124
6125     // read playfield elements from line
6126     for (line_ptr = line; *line_ptr; line_ptr++)
6127     {
6128       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6129
6130       // stop parsing playfield line if larger column than allowed
6131       if (x >= MAX_LEV_FIELDX)
6132         break;
6133
6134       if (mapped_sb_element == EL_UNDEFINED)
6135       {
6136         invalid_playfield_char = TRUE;
6137
6138         break;
6139       }
6140
6141       level->field[x][y] = mapped_sb_element;
6142
6143       // continue with next tile column
6144       x++;
6145
6146       level->fieldx = MAX(x, level->fieldx);
6147     }
6148
6149     if (invalid_playfield_char)
6150     {
6151       // if first playfield line, treat invalid lines as comment lines
6152       if (y == 0)
6153         reading_playfield = FALSE;
6154
6155       continue;
6156     }
6157
6158     // continue with next tile row
6159     y++;
6160   }
6161
6162   closeFile(file);
6163
6164   level->fieldy = y;
6165
6166   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6167   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6168
6169   if (!reading_playfield)
6170   {
6171     level->no_valid_file = TRUE;
6172
6173     Warn("cannot read level '%s' -- using empty level", filename);
6174
6175     return;
6176   }
6177
6178   if (*level_name != '\0')
6179   {
6180     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6181     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6182   }
6183   else if (*last_comment != '\0')
6184   {
6185     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6186     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6187   }
6188   else
6189   {
6190     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6191   }
6192
6193   // set all empty fields beyond the border walls to invisible steel wall
6194   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6195   {
6196     if ((x == 0 || x == level->fieldx - 1 ||
6197          y == 0 || y == level->fieldy - 1) &&
6198         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6199       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6200                      level->field, level->fieldx, level->fieldy);
6201   }
6202
6203   // set special level settings for Sokoban levels
6204   SetLevelSettings_SB(level);
6205
6206   if (load_xsb_to_ces)
6207   {
6208     // special global settings can now be set in level template
6209     level->use_custom_template = TRUE;
6210   }
6211 }
6212
6213
6214 // -------------------------------------------------------------------------
6215 // functions for handling native levels
6216 // -------------------------------------------------------------------------
6217
6218 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6219                                      struct LevelFileInfo *level_file_info,
6220                                      boolean level_info_only)
6221 {
6222   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6223     level->no_valid_file = TRUE;
6224 }
6225
6226 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6227                                      struct LevelFileInfo *level_file_info,
6228                                      boolean level_info_only)
6229 {
6230   int pos = 0;
6231
6232   // determine position of requested level inside level package
6233   if (level_file_info->packed)
6234     pos = level_file_info->nr - leveldir_current->first_level;
6235
6236   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6237     level->no_valid_file = TRUE;
6238 }
6239
6240 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6241                                      struct LevelFileInfo *level_file_info,
6242                                      boolean level_info_only)
6243 {
6244   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6245     level->no_valid_file = TRUE;
6246 }
6247
6248 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6249 {
6250   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6251     CopyNativeLevel_RND_to_EM(level);
6252   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6253     CopyNativeLevel_RND_to_SP(level);
6254   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6255     CopyNativeLevel_RND_to_MM(level);
6256 }
6257
6258 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6259 {
6260   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6261     CopyNativeLevel_EM_to_RND(level);
6262   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6263     CopyNativeLevel_SP_to_RND(level);
6264   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6265     CopyNativeLevel_MM_to_RND(level);
6266 }
6267
6268 void SaveNativeLevel(struct LevelInfo *level)
6269 {
6270   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6271   {
6272     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6273     char *filename = getLevelFilenameFromBasename(basename);
6274
6275     CopyNativeLevel_RND_to_SP(level);
6276     CopyNativeTape_RND_to_SP(level);
6277
6278     SaveNativeLevel_SP(filename);
6279   }
6280 }
6281
6282
6283 // ----------------------------------------------------------------------------
6284 // functions for loading generic level
6285 // ----------------------------------------------------------------------------
6286
6287 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6288                                   struct LevelFileInfo *level_file_info,
6289                                   boolean level_info_only)
6290 {
6291   // always start with reliable default values
6292   setLevelInfoToDefaults(level, level_info_only, TRUE);
6293
6294   switch (level_file_info->type)
6295   {
6296     case LEVEL_FILE_TYPE_RND:
6297       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6298       break;
6299
6300     case LEVEL_FILE_TYPE_EM:
6301       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6302       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6303       break;
6304
6305     case LEVEL_FILE_TYPE_SP:
6306       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6307       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6308       break;
6309
6310     case LEVEL_FILE_TYPE_MM:
6311       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6312       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6313       break;
6314
6315     case LEVEL_FILE_TYPE_DC:
6316       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6317       break;
6318
6319     case LEVEL_FILE_TYPE_SB:
6320       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6321       break;
6322
6323     default:
6324       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6325       break;
6326   }
6327
6328   // if level file is invalid, restore level structure to default values
6329   if (level->no_valid_file)
6330     setLevelInfoToDefaults(level, level_info_only, FALSE);
6331
6332   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6333     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6334
6335   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6336     CopyNativeLevel_Native_to_RND(level);
6337 }
6338
6339 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6340 {
6341   static struct LevelFileInfo level_file_info;
6342
6343   // always start with reliable default values
6344   setFileInfoToDefaults(&level_file_info);
6345
6346   level_file_info.nr = 0;                       // unknown level number
6347   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6348
6349   setString(&level_file_info.filename, filename);
6350
6351   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6352 }
6353
6354 static void LoadLevel_InitVersion(struct LevelInfo *level)
6355 {
6356   int i, j;
6357
6358   if (leveldir_current == NULL)         // only when dumping level
6359     return;
6360
6361   // all engine modifications also valid for levels which use latest engine
6362   if (level->game_version < VERSION_IDENT(3,2,0,5))
6363   {
6364     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6365     level->time_score_base = 10;
6366   }
6367
6368   if (leveldir_current->latest_engine)
6369   {
6370     // ---------- use latest game engine --------------------------------------
6371
6372     /* For all levels which are forced to use the latest game engine version
6373        (normally all but user contributed, private and undefined levels), set
6374        the game engine version to the actual version; this allows for actual
6375        corrections in the game engine to take effect for existing, converted
6376        levels (from "classic" or other existing games) to make the emulation
6377        of the corresponding game more accurate, while (hopefully) not breaking
6378        existing levels created from other players. */
6379
6380     level->game_version = GAME_VERSION_ACTUAL;
6381
6382     /* Set special EM style gems behaviour: EM style gems slip down from
6383        normal, steel and growing wall. As this is a more fundamental change,
6384        it seems better to set the default behaviour to "off" (as it is more
6385        natural) and make it configurable in the level editor (as a property
6386        of gem style elements). Already existing converted levels (neither
6387        private nor contributed levels) are changed to the new behaviour. */
6388
6389     if (level->file_version < FILE_VERSION_2_0)
6390       level->em_slippery_gems = TRUE;
6391
6392     return;
6393   }
6394
6395   // ---------- use game engine the level was created with --------------------
6396
6397   /* For all levels which are not forced to use the latest game engine
6398      version (normally user contributed, private and undefined levels),
6399      use the version of the game engine the levels were created for.
6400
6401      Since 2.0.1, the game engine version is now directly stored
6402      in the level file (chunk "VERS"), so there is no need anymore
6403      to set the game version from the file version (except for old,
6404      pre-2.0 levels, where the game version is still taken from the
6405      file format version used to store the level -- see above). */
6406
6407   // player was faster than enemies in 1.0.0 and before
6408   if (level->file_version == FILE_VERSION_1_0)
6409     for (i = 0; i < MAX_PLAYERS; i++)
6410       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6411
6412   // default behaviour for EM style gems was "slippery" only in 2.0.1
6413   if (level->game_version == VERSION_IDENT(2,0,1,0))
6414     level->em_slippery_gems = TRUE;
6415
6416   // springs could be pushed over pits before (pre-release version) 2.2.0
6417   if (level->game_version < VERSION_IDENT(2,2,0,0))
6418     level->use_spring_bug = TRUE;
6419
6420   if (level->game_version < VERSION_IDENT(3,2,0,5))
6421   {
6422     // time orb caused limited time in endless time levels before 3.2.0-5
6423     level->use_time_orb_bug = TRUE;
6424
6425     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6426     level->block_snap_field = FALSE;
6427
6428     // extra time score was same value as time left score before 3.2.0-5
6429     level->extra_time_score = level->score[SC_TIME_BONUS];
6430   }
6431
6432   if (level->game_version < VERSION_IDENT(3,2,0,7))
6433   {
6434     // default behaviour for snapping was "not continuous" before 3.2.0-7
6435     level->continuous_snapping = FALSE;
6436   }
6437
6438   // only few elements were able to actively move into acid before 3.1.0
6439   // trigger settings did not exist before 3.1.0; set to default "any"
6440   if (level->game_version < VERSION_IDENT(3,1,0,0))
6441   {
6442     // correct "can move into acid" settings (all zero in old levels)
6443
6444     level->can_move_into_acid_bits = 0; // nothing can move into acid
6445     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6446
6447     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6448     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6449     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6450     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6451
6452     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6453       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6454
6455     // correct trigger settings (stored as zero == "none" in old levels)
6456
6457     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6458     {
6459       int element = EL_CUSTOM_START + i;
6460       struct ElementInfo *ei = &element_info[element];
6461
6462       for (j = 0; j < ei->num_change_pages; j++)
6463       {
6464         struct ElementChangeInfo *change = &ei->change_page[j];
6465
6466         change->trigger_player = CH_PLAYER_ANY;
6467         change->trigger_page = CH_PAGE_ANY;
6468       }
6469     }
6470   }
6471
6472   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6473   {
6474     int element = EL_CUSTOM_256;
6475     struct ElementInfo *ei = &element_info[element];
6476     struct ElementChangeInfo *change = &ei->change_page[0];
6477
6478     /* This is needed to fix a problem that was caused by a bugfix in function
6479        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6480        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6481        not replace walkable elements, but instead just placed the player on it,
6482        without placing the Sokoban field under the player). Unfortunately, this
6483        breaks "Snake Bite" style levels when the snake is halfway through a door
6484        that just closes (the snake head is still alive and can be moved in this
6485        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6486        player (without Sokoban element) which then gets killed as designed). */
6487
6488     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6489          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6490         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6491       change->target_element = EL_PLAYER_1;
6492   }
6493
6494   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6495   if (level->game_version < VERSION_IDENT(3,2,5,0))
6496   {
6497     /* This is needed to fix a problem that was caused by a bugfix in function
6498        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6499        corrects the behaviour when a custom element changes to another custom
6500        element with a higher element number that has change actions defined.
6501        Normally, only one change per frame is allowed for custom elements.
6502        Therefore, it is checked if a custom element already changed in the
6503        current frame; if it did, subsequent changes are suppressed.
6504        Unfortunately, this is only checked for element changes, but not for
6505        change actions, which are still executed. As the function above loops
6506        through all custom elements from lower to higher, an element change
6507        resulting in a lower CE number won't be checked again, while a target
6508        element with a higher number will also be checked, and potential change
6509        actions will get executed for this CE, too (which is wrong), while
6510        further changes are ignored (which is correct). As this bugfix breaks
6511        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6512        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6513        behaviour for existing levels and tapes that make use of this bug */
6514
6515     level->use_action_after_change_bug = TRUE;
6516   }
6517
6518   // not centering level after relocating player was default only in 3.2.3
6519   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6520     level->shifted_relocation = TRUE;
6521
6522   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6523   if (level->game_version < VERSION_IDENT(3,2,6,0))
6524     level->em_explodes_by_fire = TRUE;
6525
6526   // levels were solved by the first player entering an exit up to 4.1.0.0
6527   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6528     level->solved_by_one_player = TRUE;
6529
6530   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6531   if (level->game_version < VERSION_IDENT(4,1,1,1))
6532     level->use_life_bugs = TRUE;
6533
6534   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6535   if (level->game_version < VERSION_IDENT(4,1,1,1))
6536     level->sb_objects_needed = FALSE;
6537
6538   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6539   if (level->game_version <= VERSION_IDENT(4,2,2,0))
6540     level->finish_dig_collect = FALSE;
6541
6542   // CE changing to player was kept under the player if walkable up to 4.2.3.1
6543   if (level->game_version <= VERSION_IDENT(4,2,3,1))
6544     level->keep_walkable_ce = TRUE;
6545 }
6546
6547 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6548 {
6549   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
6550   int x, y;
6551
6552   // check if this level is (not) a Sokoban level
6553   for (y = 0; y < level->fieldy; y++)
6554     for (x = 0; x < level->fieldx; x++)
6555       if (!IS_SB_ELEMENT(Tile[x][y]))
6556         is_sokoban_level = FALSE;
6557
6558   if (is_sokoban_level)
6559   {
6560     // set special level settings for Sokoban levels
6561     SetLevelSettings_SB(level);
6562   }
6563 }
6564
6565 static void LoadLevel_InitSettings(struct LevelInfo *level)
6566 {
6567   // adjust level settings for (non-native) Sokoban-style levels
6568   LoadLevel_InitSettings_SB(level);
6569
6570   // rename levels with title "nameless level" or if renaming is forced
6571   if (leveldir_current->empty_level_name != NULL &&
6572       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6573        leveldir_current->force_level_name))
6574     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6575              leveldir_current->empty_level_name, level_nr);
6576 }
6577
6578 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6579 {
6580   int i, x, y;
6581
6582   // map elements that have changed in newer versions
6583   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6584                                                     level->game_version);
6585   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6586     for (x = 0; x < 3; x++)
6587       for (y = 0; y < 3; y++)
6588         level->yamyam_content[i].e[x][y] =
6589           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6590                                     level->game_version);
6591
6592 }
6593
6594 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6595 {
6596   int i, j;
6597
6598   // map custom element change events that have changed in newer versions
6599   // (these following values were accidentally changed in version 3.0.1)
6600   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6601   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6602   {
6603     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6604     {
6605       int element = EL_CUSTOM_START + i;
6606
6607       // order of checking and copying events to be mapped is important
6608       // (do not change the start and end value -- they are constant)
6609       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6610       {
6611         if (HAS_CHANGE_EVENT(element, j - 2))
6612         {
6613           SET_CHANGE_EVENT(element, j - 2, FALSE);
6614           SET_CHANGE_EVENT(element, j, TRUE);
6615         }
6616       }
6617
6618       // order of checking and copying events to be mapped is important
6619       // (do not change the start and end value -- they are constant)
6620       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6621       {
6622         if (HAS_CHANGE_EVENT(element, j - 1))
6623         {
6624           SET_CHANGE_EVENT(element, j - 1, FALSE);
6625           SET_CHANGE_EVENT(element, j, TRUE);
6626         }
6627       }
6628     }
6629   }
6630
6631   // initialize "can_change" field for old levels with only one change page
6632   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6633   {
6634     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6635     {
6636       int element = EL_CUSTOM_START + i;
6637
6638       if (CAN_CHANGE(element))
6639         element_info[element].change->can_change = TRUE;
6640     }
6641   }
6642
6643   // correct custom element values (for old levels without these options)
6644   if (level->game_version < VERSION_IDENT(3,1,1,0))
6645   {
6646     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6647     {
6648       int element = EL_CUSTOM_START + i;
6649       struct ElementInfo *ei = &element_info[element];
6650
6651       if (ei->access_direction == MV_NO_DIRECTION)
6652         ei->access_direction = MV_ALL_DIRECTIONS;
6653     }
6654   }
6655
6656   // correct custom element values (fix invalid values for all versions)
6657   if (1)
6658   {
6659     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6660     {
6661       int element = EL_CUSTOM_START + i;
6662       struct ElementInfo *ei = &element_info[element];
6663
6664       for (j = 0; j < ei->num_change_pages; j++)
6665       {
6666         struct ElementChangeInfo *change = &ei->change_page[j];
6667
6668         if (change->trigger_player == CH_PLAYER_NONE)
6669           change->trigger_player = CH_PLAYER_ANY;
6670
6671         if (change->trigger_side == CH_SIDE_NONE)
6672           change->trigger_side = CH_SIDE_ANY;
6673       }
6674     }
6675   }
6676
6677   // initialize "can_explode" field for old levels which did not store this
6678   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6679   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6680   {
6681     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6682     {
6683       int element = EL_CUSTOM_START + i;
6684
6685       if (EXPLODES_1X1_OLD(element))
6686         element_info[element].explosion_type = EXPLODES_1X1;
6687
6688       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6689                                              EXPLODES_SMASHED(element) ||
6690                                              EXPLODES_IMPACT(element)));
6691     }
6692   }
6693
6694   // correct previously hard-coded move delay values for maze runner style
6695   if (level->game_version < VERSION_IDENT(3,1,1,0))
6696   {
6697     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6698     {
6699       int element = EL_CUSTOM_START + i;
6700
6701       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6702       {
6703         // previously hard-coded and therefore ignored
6704         element_info[element].move_delay_fixed = 9;
6705         element_info[element].move_delay_random = 0;
6706       }
6707     }
6708   }
6709
6710   // set some other uninitialized values of custom elements in older levels
6711   if (level->game_version < VERSION_IDENT(3,1,0,0))
6712   {
6713     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6714     {
6715       int element = EL_CUSTOM_START + i;
6716
6717       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6718
6719       element_info[element].explosion_delay = 17;
6720       element_info[element].ignition_delay = 8;
6721     }
6722   }
6723
6724   // set mouse click change events to work for left/middle/right mouse button
6725   if (level->game_version < VERSION_IDENT(4,2,3,0))
6726   {
6727     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6728     {
6729       int element = EL_CUSTOM_START + i;
6730       struct ElementInfo *ei = &element_info[element];
6731
6732       for (j = 0; j < ei->num_change_pages; j++)
6733       {
6734         struct ElementChangeInfo *change = &ei->change_page[j];
6735
6736         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6737             change->has_event[CE_PRESSED_BY_MOUSE] ||
6738             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6739             change->has_event[CE_MOUSE_PRESSED_ON_X])
6740           change->trigger_side = CH_SIDE_ANY;
6741       }
6742     }
6743   }
6744 }
6745
6746 static void LoadLevel_InitElements(struct LevelInfo *level)
6747 {
6748   LoadLevel_InitStandardElements(level);
6749
6750   if (level->file_has_custom_elements)
6751     LoadLevel_InitCustomElements(level);
6752
6753   // initialize element properties for level editor etc.
6754   InitElementPropertiesEngine(level->game_version);
6755   InitElementPropertiesGfxElement();
6756 }
6757
6758 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6759 {
6760   int x, y;
6761
6762   // map elements that have changed in newer versions
6763   for (y = 0; y < level->fieldy; y++)
6764     for (x = 0; x < level->fieldx; x++)
6765       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6766                                                      level->game_version);
6767
6768   // clear unused playfield data (nicer if level gets resized in editor)
6769   for (x = 0; x < MAX_LEV_FIELDX; x++)
6770     for (y = 0; y < MAX_LEV_FIELDY; y++)
6771       if (x >= level->fieldx || y >= level->fieldy)
6772         level->field[x][y] = EL_EMPTY;
6773
6774   // copy elements to runtime playfield array
6775   for (x = 0; x < MAX_LEV_FIELDX; x++)
6776     for (y = 0; y < MAX_LEV_FIELDY; y++)
6777       Tile[x][y] = level->field[x][y];
6778
6779   // initialize level size variables for faster access
6780   lev_fieldx = level->fieldx;
6781   lev_fieldy = level->fieldy;
6782
6783   // determine border element for this level
6784   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6785     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
6786   else
6787     SetBorderElement();
6788 }
6789
6790 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6791 {
6792   struct LevelFileInfo *level_file_info = &level->file_info;
6793
6794   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6795     CopyNativeLevel_RND_to_Native(level);
6796 }
6797
6798 static void LoadLevelTemplate_LoadAndInit(void)
6799 {
6800   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6801
6802   LoadLevel_InitVersion(&level_template);
6803   LoadLevel_InitElements(&level_template);
6804   LoadLevel_InitSettings(&level_template);
6805
6806   ActivateLevelTemplate();
6807 }
6808
6809 void LoadLevelTemplate(int nr)
6810 {
6811   if (!fileExists(getGlobalLevelTemplateFilename()))
6812   {
6813     Warn("no level template found for this level");
6814
6815     return;
6816   }
6817
6818   setLevelFileInfo(&level_template.file_info, nr);
6819
6820   LoadLevelTemplate_LoadAndInit();
6821 }
6822
6823 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6824 {
6825   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6826
6827   LoadLevelTemplate_LoadAndInit();
6828 }
6829
6830 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6831 {
6832   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6833
6834   if (level.use_custom_template)
6835   {
6836     if (network_level != NULL)
6837       LoadNetworkLevelTemplate(network_level);
6838     else
6839       LoadLevelTemplate(-1);
6840   }
6841
6842   LoadLevel_InitVersion(&level);
6843   LoadLevel_InitElements(&level);
6844   LoadLevel_InitPlayfield(&level);
6845   LoadLevel_InitSettings(&level);
6846
6847   LoadLevel_InitNativeEngines(&level);
6848 }
6849
6850 void LoadLevel(int nr)
6851 {
6852   SetLevelSetInfo(leveldir_current->identifier, nr);
6853
6854   setLevelFileInfo(&level.file_info, nr);
6855
6856   LoadLevel_LoadAndInit(NULL);
6857 }
6858
6859 void LoadLevelInfoOnly(int nr)
6860 {
6861   setLevelFileInfo(&level.file_info, nr);
6862
6863   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6864 }
6865
6866 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6867 {
6868   SetLevelSetInfo(network_level->leveldir_identifier,
6869                   network_level->file_info.nr);
6870
6871   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6872
6873   LoadLevel_LoadAndInit(network_level);
6874 }
6875
6876 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6877 {
6878   int chunk_size = 0;
6879
6880   chunk_size += putFileVersion(file, level->file_version);
6881   chunk_size += putFileVersion(file, level->game_version);
6882
6883   return chunk_size;
6884 }
6885
6886 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6887 {
6888   int chunk_size = 0;
6889
6890   chunk_size += putFile16BitBE(file, level->creation_date.year);
6891   chunk_size += putFile8Bit(file,    level->creation_date.month);
6892   chunk_size += putFile8Bit(file,    level->creation_date.day);
6893
6894   return chunk_size;
6895 }
6896
6897 #if ENABLE_HISTORIC_CHUNKS
6898 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6899 {
6900   int i, x, y;
6901
6902   putFile8Bit(file, level->fieldx);
6903   putFile8Bit(file, level->fieldy);
6904
6905   putFile16BitBE(file, level->time);
6906   putFile16BitBE(file, level->gems_needed);
6907
6908   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6909     putFile8Bit(file, level->name[i]);
6910
6911   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6912     putFile8Bit(file, level->score[i]);
6913
6914   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6915     for (y = 0; y < 3; y++)
6916       for (x = 0; x < 3; x++)
6917         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6918                            level->yamyam_content[i].e[x][y]));
6919   putFile8Bit(file, level->amoeba_speed);
6920   putFile8Bit(file, level->time_magic_wall);
6921   putFile8Bit(file, level->time_wheel);
6922   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6923                      level->amoeba_content));
6924   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6925   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6926   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6927   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6928
6929   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6930
6931   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6932   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6933   putFile32BitBE(file, level->can_move_into_acid_bits);
6934   putFile8Bit(file, level->dont_collide_with_bits);
6935
6936   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6937   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6938
6939   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6940   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6941   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6942
6943   putFile8Bit(file, level->game_engine_type);
6944
6945   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6946 }
6947 #endif
6948
6949 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6950 {
6951   int chunk_size = 0;
6952   int i;
6953
6954   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6955     chunk_size += putFile8Bit(file, level->name[i]);
6956
6957   return chunk_size;
6958 }
6959
6960 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6961 {
6962   int chunk_size = 0;
6963   int i;
6964
6965   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6966     chunk_size += putFile8Bit(file, level->author[i]);
6967
6968   return chunk_size;
6969 }
6970
6971 #if ENABLE_HISTORIC_CHUNKS
6972 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6973 {
6974   int chunk_size = 0;
6975   int x, y;
6976
6977   for (y = 0; y < level->fieldy; y++)
6978     for (x = 0; x < level->fieldx; x++)
6979       if (level->encoding_16bit_field)
6980         chunk_size += putFile16BitBE(file, level->field[x][y]);
6981       else
6982         chunk_size += putFile8Bit(file, level->field[x][y]);
6983
6984   return chunk_size;
6985 }
6986 #endif
6987
6988 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6989 {
6990   int chunk_size = 0;
6991   int x, y;
6992
6993   for (y = 0; y < level->fieldy; y++) 
6994     for (x = 0; x < level->fieldx; x++) 
6995       chunk_size += putFile16BitBE(file, level->field[x][y]);
6996
6997   return chunk_size;
6998 }
6999
7000 #if ENABLE_HISTORIC_CHUNKS
7001 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7002 {
7003   int i, x, y;
7004
7005   putFile8Bit(file, EL_YAMYAM);
7006   putFile8Bit(file, level->num_yamyam_contents);
7007   putFile8Bit(file, 0);
7008   putFile8Bit(file, 0);
7009
7010   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7011     for (y = 0; y < 3; y++)
7012       for (x = 0; x < 3; x++)
7013         if (level->encoding_16bit_field)
7014           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7015         else
7016           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7017 }
7018 #endif
7019
7020 #if ENABLE_HISTORIC_CHUNKS
7021 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7022 {
7023   int i, x, y;
7024   int num_contents, content_xsize, content_ysize;
7025   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7026
7027   if (element == EL_YAMYAM)
7028   {
7029     num_contents = level->num_yamyam_contents;
7030     content_xsize = 3;
7031     content_ysize = 3;
7032
7033     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7034       for (y = 0; y < 3; y++)
7035         for (x = 0; x < 3; x++)
7036           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7037   }
7038   else if (element == EL_BD_AMOEBA)
7039   {
7040     num_contents = 1;
7041     content_xsize = 1;
7042     content_ysize = 1;
7043
7044     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7045       for (y = 0; y < 3; y++)
7046         for (x = 0; x < 3; x++)
7047           content_array[i][x][y] = EL_EMPTY;
7048     content_array[0][0][0] = level->amoeba_content;
7049   }
7050   else
7051   {
7052     // chunk header already written -- write empty chunk data
7053     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7054
7055     Warn("cannot save content for element '%d'", element);
7056
7057     return;
7058   }
7059
7060   putFile16BitBE(file, element);
7061   putFile8Bit(file, num_contents);
7062   putFile8Bit(file, content_xsize);
7063   putFile8Bit(file, content_ysize);
7064
7065   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7066
7067   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7068     for (y = 0; y < 3; y++)
7069       for (x = 0; x < 3; x++)
7070         putFile16BitBE(file, content_array[i][x][y]);
7071 }
7072 #endif
7073
7074 #if ENABLE_HISTORIC_CHUNKS
7075 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7076 {
7077   int envelope_nr = element - EL_ENVELOPE_1;
7078   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7079   int chunk_size = 0;
7080   int i;
7081
7082   chunk_size += putFile16BitBE(file, element);
7083   chunk_size += putFile16BitBE(file, envelope_len);
7084   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7085   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7086
7087   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7088   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7089
7090   for (i = 0; i < envelope_len; i++)
7091     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7092
7093   return chunk_size;
7094 }
7095 #endif
7096
7097 #if ENABLE_HISTORIC_CHUNKS
7098 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7099                            int num_changed_custom_elements)
7100 {
7101   int i, check = 0;
7102
7103   putFile16BitBE(file, num_changed_custom_elements);
7104
7105   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7106   {
7107     int element = EL_CUSTOM_START + i;
7108
7109     struct ElementInfo *ei = &element_info[element];
7110
7111     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7112     {
7113       if (check < num_changed_custom_elements)
7114       {
7115         putFile16BitBE(file, element);
7116         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7117       }
7118
7119       check++;
7120     }
7121   }
7122
7123   if (check != num_changed_custom_elements)     // should not happen
7124     Warn("inconsistent number of custom element properties");
7125 }
7126 #endif
7127
7128 #if ENABLE_HISTORIC_CHUNKS
7129 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7130                            int num_changed_custom_elements)
7131 {
7132   int i, check = 0;
7133
7134   putFile16BitBE(file, num_changed_custom_elements);
7135
7136   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7137   {
7138     int element = EL_CUSTOM_START + i;
7139
7140     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7141     {
7142       if (check < num_changed_custom_elements)
7143       {
7144         putFile16BitBE(file, element);
7145         putFile16BitBE(file, element_info[element].change->target_element);
7146       }
7147
7148       check++;
7149     }
7150   }
7151
7152   if (check != num_changed_custom_elements)     // should not happen
7153     Warn("inconsistent number of custom target elements");
7154 }
7155 #endif
7156
7157 #if ENABLE_HISTORIC_CHUNKS
7158 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7159                            int num_changed_custom_elements)
7160 {
7161   int i, j, x, y, check = 0;
7162
7163   putFile16BitBE(file, num_changed_custom_elements);
7164
7165   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7166   {
7167     int element = EL_CUSTOM_START + i;
7168     struct ElementInfo *ei = &element_info[element];
7169
7170     if (ei->modified_settings)
7171     {
7172       if (check < num_changed_custom_elements)
7173       {
7174         putFile16BitBE(file, element);
7175
7176         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7177           putFile8Bit(file, ei->description[j]);
7178
7179         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7180
7181         // some free bytes for future properties and padding
7182         WriteUnusedBytesToFile(file, 7);
7183
7184         putFile8Bit(file, ei->use_gfx_element);
7185         putFile16BitBE(file, ei->gfx_element_initial);
7186
7187         putFile8Bit(file, ei->collect_score_initial);
7188         putFile8Bit(file, ei->collect_count_initial);
7189
7190         putFile16BitBE(file, ei->push_delay_fixed);
7191         putFile16BitBE(file, ei->push_delay_random);
7192         putFile16BitBE(file, ei->move_delay_fixed);
7193         putFile16BitBE(file, ei->move_delay_random);
7194
7195         putFile16BitBE(file, ei->move_pattern);
7196         putFile8Bit(file, ei->move_direction_initial);
7197         putFile8Bit(file, ei->move_stepsize);
7198
7199         for (y = 0; y < 3; y++)
7200           for (x = 0; x < 3; x++)
7201             putFile16BitBE(file, ei->content.e[x][y]);
7202
7203         putFile32BitBE(file, ei->change->events);
7204
7205         putFile16BitBE(file, ei->change->target_element);
7206
7207         putFile16BitBE(file, ei->change->delay_fixed);
7208         putFile16BitBE(file, ei->change->delay_random);
7209         putFile16BitBE(file, ei->change->delay_frames);
7210
7211         putFile16BitBE(file, ei->change->initial_trigger_element);
7212
7213         putFile8Bit(file, ei->change->explode);
7214         putFile8Bit(file, ei->change->use_target_content);
7215         putFile8Bit(file, ei->change->only_if_complete);
7216         putFile8Bit(file, ei->change->use_random_replace);
7217
7218         putFile8Bit(file, ei->change->random_percentage);
7219         putFile8Bit(file, ei->change->replace_when);
7220
7221         for (y = 0; y < 3; y++)
7222           for (x = 0; x < 3; x++)
7223             putFile16BitBE(file, ei->change->content.e[x][y]);
7224
7225         putFile8Bit(file, ei->slippery_type);
7226
7227         // some free bytes for future properties and padding
7228         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7229       }
7230
7231       check++;
7232     }
7233   }
7234
7235   if (check != num_changed_custom_elements)     // should not happen
7236     Warn("inconsistent number of custom element properties");
7237 }
7238 #endif
7239
7240 #if ENABLE_HISTORIC_CHUNKS
7241 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7242 {
7243   struct ElementInfo *ei = &element_info[element];
7244   int i, j, x, y;
7245
7246   // ---------- custom element base property values (96 bytes) ----------------
7247
7248   putFile16BitBE(file, element);
7249
7250   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7251     putFile8Bit(file, ei->description[i]);
7252
7253   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7254
7255   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7256
7257   putFile8Bit(file, ei->num_change_pages);
7258
7259   putFile16BitBE(file, ei->ce_value_fixed_initial);
7260   putFile16BitBE(file, ei->ce_value_random_initial);
7261   putFile8Bit(file, ei->use_last_ce_value);
7262
7263   putFile8Bit(file, ei->use_gfx_element);
7264   putFile16BitBE(file, ei->gfx_element_initial);
7265
7266   putFile8Bit(file, ei->collect_score_initial);
7267   putFile8Bit(file, ei->collect_count_initial);
7268
7269   putFile8Bit(file, ei->drop_delay_fixed);
7270   putFile8Bit(file, ei->push_delay_fixed);
7271   putFile8Bit(file, ei->drop_delay_random);
7272   putFile8Bit(file, ei->push_delay_random);
7273   putFile16BitBE(file, ei->move_delay_fixed);
7274   putFile16BitBE(file, ei->move_delay_random);
7275
7276   // bits 0 - 15 of "move_pattern" ...
7277   putFile16BitBE(file, ei->move_pattern & 0xffff);
7278   putFile8Bit(file, ei->move_direction_initial);
7279   putFile8Bit(file, ei->move_stepsize);
7280
7281   putFile8Bit(file, ei->slippery_type);
7282
7283   for (y = 0; y < 3; y++)
7284     for (x = 0; x < 3; x++)
7285       putFile16BitBE(file, ei->content.e[x][y]);
7286
7287   putFile16BitBE(file, ei->move_enter_element);
7288   putFile16BitBE(file, ei->move_leave_element);
7289   putFile8Bit(file, ei->move_leave_type);
7290
7291   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7292   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7293
7294   putFile8Bit(file, ei->access_direction);
7295
7296   putFile8Bit(file, ei->explosion_delay);
7297   putFile8Bit(file, ei->ignition_delay);
7298   putFile8Bit(file, ei->explosion_type);
7299
7300   // some free bytes for future custom property values and padding
7301   WriteUnusedBytesToFile(file, 1);
7302
7303   // ---------- change page property values (48 bytes) ------------------------
7304
7305   for (i = 0; i < ei->num_change_pages; i++)
7306   {
7307     struct ElementChangeInfo *change = &ei->change_page[i];
7308     unsigned int event_bits;
7309
7310     // bits 0 - 31 of "has_event[]" ...
7311     event_bits = 0;
7312     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7313       if (change->has_event[j])
7314         event_bits |= (1 << j);
7315     putFile32BitBE(file, event_bits);
7316
7317     putFile16BitBE(file, change->target_element);
7318
7319     putFile16BitBE(file, change->delay_fixed);
7320     putFile16BitBE(file, change->delay_random);
7321     putFile16BitBE(file, change->delay_frames);
7322
7323     putFile16BitBE(file, change->initial_trigger_element);
7324
7325     putFile8Bit(file, change->explode);
7326     putFile8Bit(file, change->use_target_content);
7327     putFile8Bit(file, change->only_if_complete);
7328     putFile8Bit(file, change->use_random_replace);
7329
7330     putFile8Bit(file, change->random_percentage);
7331     putFile8Bit(file, change->replace_when);
7332
7333     for (y = 0; y < 3; y++)
7334       for (x = 0; x < 3; x++)
7335         putFile16BitBE(file, change->target_content.e[x][y]);
7336
7337     putFile8Bit(file, change->can_change);
7338
7339     putFile8Bit(file, change->trigger_side);
7340
7341     putFile8Bit(file, change->trigger_player);
7342     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7343                        log_2(change->trigger_page)));
7344
7345     putFile8Bit(file, change->has_action);
7346     putFile8Bit(file, change->action_type);
7347     putFile8Bit(file, change->action_mode);
7348     putFile16BitBE(file, change->action_arg);
7349
7350     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7351     event_bits = 0;
7352     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7353       if (change->has_event[j])
7354         event_bits |= (1 << (j - 32));
7355     putFile8Bit(file, event_bits);
7356   }
7357 }
7358 #endif
7359
7360 #if ENABLE_HISTORIC_CHUNKS
7361 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7362 {
7363   struct ElementInfo *ei = &element_info[element];
7364   struct ElementGroupInfo *group = ei->group;
7365   int i;
7366
7367   putFile16BitBE(file, element);
7368
7369   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7370     putFile8Bit(file, ei->description[i]);
7371
7372   putFile8Bit(file, group->num_elements);
7373
7374   putFile8Bit(file, ei->use_gfx_element);
7375   putFile16BitBE(file, ei->gfx_element_initial);
7376
7377   putFile8Bit(file, group->choice_mode);
7378
7379   // some free bytes for future values and padding
7380   WriteUnusedBytesToFile(file, 3);
7381
7382   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7383     putFile16BitBE(file, group->element[i]);
7384 }
7385 #endif
7386
7387 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7388                                 boolean write_element)
7389 {
7390   int save_type = entry->save_type;
7391   int data_type = entry->data_type;
7392   int conf_type = entry->conf_type;
7393   int byte_mask = conf_type & CONF_MASK_BYTES;
7394   int element = entry->element;
7395   int default_value = entry->default_value;
7396   int num_bytes = 0;
7397   boolean modified = FALSE;
7398
7399   if (byte_mask != CONF_MASK_MULTI_BYTES)
7400   {
7401     void *value_ptr = entry->value;
7402     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7403                  *(int *)value_ptr);
7404
7405     // check if any settings have been modified before saving them
7406     if (value != default_value)
7407       modified = TRUE;
7408
7409     // do not save if explicitly told or if unmodified default settings
7410     if ((save_type == SAVE_CONF_NEVER) ||
7411         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7412       return 0;
7413
7414     if (write_element)
7415       num_bytes += putFile16BitBE(file, element);
7416
7417     num_bytes += putFile8Bit(file, conf_type);
7418     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7419                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7420                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7421                   0);
7422   }
7423   else if (data_type == TYPE_STRING)
7424   {
7425     char *default_string = entry->default_string;
7426     char *string = (char *)(entry->value);
7427     int string_length = strlen(string);
7428     int i;
7429
7430     // check if any settings have been modified before saving them
7431     if (!strEqual(string, default_string))
7432       modified = TRUE;
7433
7434     // do not save if explicitly told or if unmodified default settings
7435     if ((save_type == SAVE_CONF_NEVER) ||
7436         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7437       return 0;
7438
7439     if (write_element)
7440       num_bytes += putFile16BitBE(file, element);
7441
7442     num_bytes += putFile8Bit(file, conf_type);
7443     num_bytes += putFile16BitBE(file, string_length);
7444
7445     for (i = 0; i < string_length; i++)
7446       num_bytes += putFile8Bit(file, string[i]);
7447   }
7448   else if (data_type == TYPE_ELEMENT_LIST)
7449   {
7450     int *element_array = (int *)(entry->value);
7451     int num_elements = *(int *)(entry->num_entities);
7452     int i;
7453
7454     // check if any settings have been modified before saving them
7455     for (i = 0; i < num_elements; i++)
7456       if (element_array[i] != default_value)
7457         modified = TRUE;
7458
7459     // do not save if explicitly told or if unmodified default settings
7460     if ((save_type == SAVE_CONF_NEVER) ||
7461         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7462       return 0;
7463
7464     if (write_element)
7465       num_bytes += putFile16BitBE(file, element);
7466
7467     num_bytes += putFile8Bit(file, conf_type);
7468     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7469
7470     for (i = 0; i < num_elements; i++)
7471       num_bytes += putFile16BitBE(file, element_array[i]);
7472   }
7473   else if (data_type == TYPE_CONTENT_LIST)
7474   {
7475     struct Content *content = (struct Content *)(entry->value);
7476     int num_contents = *(int *)(entry->num_entities);
7477     int i, x, y;
7478
7479     // check if any settings have been modified before saving them
7480     for (i = 0; i < num_contents; i++)
7481       for (y = 0; y < 3; y++)
7482         for (x = 0; x < 3; x++)
7483           if (content[i].e[x][y] != default_value)
7484             modified = TRUE;
7485
7486     // do not save if explicitly told or if unmodified default settings
7487     if ((save_type == SAVE_CONF_NEVER) ||
7488         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7489       return 0;
7490
7491     if (write_element)
7492       num_bytes += putFile16BitBE(file, element);
7493
7494     num_bytes += putFile8Bit(file, conf_type);
7495     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7496
7497     for (i = 0; i < num_contents; i++)
7498       for (y = 0; y < 3; y++)
7499         for (x = 0; x < 3; x++)
7500           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7501   }
7502
7503   return num_bytes;
7504 }
7505
7506 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7507 {
7508   int chunk_size = 0;
7509   int i;
7510
7511   li = *level;          // copy level data into temporary buffer
7512
7513   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7514     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7515
7516   return chunk_size;
7517 }
7518
7519 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7520 {
7521   int chunk_size = 0;
7522   int i;
7523
7524   li = *level;          // copy level data into temporary buffer
7525
7526   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7527     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7528
7529   return chunk_size;
7530 }
7531
7532 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7533 {
7534   int envelope_nr = element - EL_ENVELOPE_1;
7535   int chunk_size = 0;
7536   int i;
7537
7538   chunk_size += putFile16BitBE(file, element);
7539
7540   // copy envelope data into temporary buffer
7541   xx_envelope = level->envelope[envelope_nr];
7542
7543   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7544     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7545
7546   return chunk_size;
7547 }
7548
7549 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7550 {
7551   struct ElementInfo *ei = &element_info[element];
7552   int chunk_size = 0;
7553   int i, j;
7554
7555   chunk_size += putFile16BitBE(file, element);
7556
7557   xx_ei = *ei;          // copy element data into temporary buffer
7558
7559   // set default description string for this specific element
7560   strcpy(xx_default_description, getDefaultElementDescription(ei));
7561
7562   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7563     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7564
7565   for (i = 0; i < ei->num_change_pages; i++)
7566   {
7567     struct ElementChangeInfo *change = &ei->change_page[i];
7568
7569     xx_current_change_page = i;
7570
7571     xx_change = *change;        // copy change data into temporary buffer
7572
7573     resetEventBits();
7574     setEventBitsFromEventFlags(change);
7575
7576     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7577       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7578                                          FALSE);
7579   }
7580
7581   return chunk_size;
7582 }
7583
7584 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7585 {
7586   struct ElementInfo *ei = &element_info[element];
7587   struct ElementGroupInfo *group = ei->group;
7588   int chunk_size = 0;
7589   int i;
7590
7591   chunk_size += putFile16BitBE(file, element);
7592
7593   xx_ei = *ei;          // copy element data into temporary buffer
7594   xx_group = *group;    // copy group data into temporary buffer
7595
7596   // set default description string for this specific element
7597   strcpy(xx_default_description, getDefaultElementDescription(ei));
7598
7599   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7600     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7601
7602   return chunk_size;
7603 }
7604
7605 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7606 {
7607   struct ElementInfo *ei = &element_info[element];
7608   int chunk_size = 0;
7609   int i;
7610
7611   chunk_size += putFile16BitBE(file, element);
7612
7613   xx_ei = *ei;          // copy element data into temporary buffer
7614
7615   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7616     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7617
7618   return chunk_size;
7619 }
7620
7621 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7622                                   boolean save_as_template)
7623 {
7624   int chunk_size;
7625   int i;
7626   FILE *file;
7627
7628   if (!(file = fopen(filename, MODE_WRITE)))
7629   {
7630     Warn("cannot save level file '%s'", filename);
7631
7632     return;
7633   }
7634
7635   level->file_version = FILE_VERSION_ACTUAL;
7636   level->game_version = GAME_VERSION_ACTUAL;
7637
7638   level->creation_date = getCurrentDate();
7639
7640   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7641   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7642
7643   chunk_size = SaveLevel_VERS(NULL, level);
7644   putFileChunkBE(file, "VERS", chunk_size);
7645   SaveLevel_VERS(file, level);
7646
7647   chunk_size = SaveLevel_DATE(NULL, level);
7648   putFileChunkBE(file, "DATE", chunk_size);
7649   SaveLevel_DATE(file, level);
7650
7651   chunk_size = SaveLevel_NAME(NULL, level);
7652   putFileChunkBE(file, "NAME", chunk_size);
7653   SaveLevel_NAME(file, level);
7654
7655   chunk_size = SaveLevel_AUTH(NULL, level);
7656   putFileChunkBE(file, "AUTH", chunk_size);
7657   SaveLevel_AUTH(file, level);
7658
7659   chunk_size = SaveLevel_INFO(NULL, level);
7660   putFileChunkBE(file, "INFO", chunk_size);
7661   SaveLevel_INFO(file, level);
7662
7663   chunk_size = SaveLevel_BODY(NULL, level);
7664   putFileChunkBE(file, "BODY", chunk_size);
7665   SaveLevel_BODY(file, level);
7666
7667   chunk_size = SaveLevel_ELEM(NULL, level);
7668   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7669   {
7670     putFileChunkBE(file, "ELEM", chunk_size);
7671     SaveLevel_ELEM(file, level);
7672   }
7673
7674   for (i = 0; i < NUM_ENVELOPES; i++)
7675   {
7676     int element = EL_ENVELOPE_1 + i;
7677
7678     chunk_size = SaveLevel_NOTE(NULL, level, element);
7679     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7680     {
7681       putFileChunkBE(file, "NOTE", chunk_size);
7682       SaveLevel_NOTE(file, level, element);
7683     }
7684   }
7685
7686   // if not using template level, check for non-default custom/group elements
7687   if (!level->use_custom_template || save_as_template)
7688   {
7689     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7690     {
7691       int element = EL_CUSTOM_START + i;
7692
7693       chunk_size = SaveLevel_CUSX(NULL, level, element);
7694       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
7695       {
7696         putFileChunkBE(file, "CUSX", chunk_size);
7697         SaveLevel_CUSX(file, level, element);
7698       }
7699     }
7700
7701     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7702     {
7703       int element = EL_GROUP_START + i;
7704
7705       chunk_size = SaveLevel_GRPX(NULL, level, element);
7706       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
7707       {
7708         putFileChunkBE(file, "GRPX", chunk_size);
7709         SaveLevel_GRPX(file, level, element);
7710       }
7711     }
7712
7713     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7714     {
7715       int element = GET_EMPTY_ELEMENT(i);
7716
7717       chunk_size = SaveLevel_EMPX(NULL, level, element);
7718       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
7719       {
7720         putFileChunkBE(file, "EMPX", chunk_size);
7721         SaveLevel_EMPX(file, level, element);
7722       }
7723     }
7724   }
7725
7726   fclose(file);
7727
7728   SetFilePermissions(filename, PERMS_PRIVATE);
7729 }
7730
7731 void SaveLevel(int nr)
7732 {
7733   char *filename = getDefaultLevelFilename(nr);
7734
7735   SaveLevelFromFilename(&level, filename, FALSE);
7736 }
7737
7738 void SaveLevelTemplate(void)
7739 {
7740   char *filename = getLocalLevelTemplateFilename();
7741
7742   SaveLevelFromFilename(&level, filename, TRUE);
7743 }
7744
7745 boolean SaveLevelChecked(int nr)
7746 {
7747   char *filename = getDefaultLevelFilename(nr);
7748   boolean new_level = !fileExists(filename);
7749   boolean level_saved = FALSE;
7750
7751   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7752   {
7753     SaveLevel(nr);
7754
7755     if (new_level)
7756       Request("Level saved!", REQ_CONFIRM);
7757
7758     level_saved = TRUE;
7759   }
7760
7761   return level_saved;
7762 }
7763
7764 void DumpLevel(struct LevelInfo *level)
7765 {
7766   if (level->no_level_file || level->no_valid_file)
7767   {
7768     Warn("cannot dump -- no valid level file found");
7769
7770     return;
7771   }
7772
7773   PrintLine("-", 79);
7774   Print("Level xxx (file version %08d, game version %08d)\n",
7775         level->file_version, level->game_version);
7776   PrintLine("-", 79);
7777
7778   Print("Level author: '%s'\n", level->author);
7779   Print("Level title:  '%s'\n", level->name);
7780   Print("\n");
7781   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7782   Print("\n");
7783   Print("Level time:  %d seconds\n", level->time);
7784   Print("Gems needed: %d\n", level->gems_needed);
7785   Print("\n");
7786   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7787   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7788   Print("Time for light:      %d seconds\n", level->time_light);
7789   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7790   Print("\n");
7791   Print("Amoeba speed: %d\n", level->amoeba_speed);
7792   Print("\n");
7793
7794   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7795   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7796   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7797   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7798   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7799   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7800
7801   if (options.debug)
7802   {
7803     int i, j;
7804
7805     for (i = 0; i < NUM_ENVELOPES; i++)
7806     {
7807       char *text = level->envelope[i].text;
7808       int text_len = strlen(text);
7809       boolean has_text = FALSE;
7810
7811       for (j = 0; j < text_len; j++)
7812         if (text[j] != ' ' && text[j] != '\n')
7813           has_text = TRUE;
7814
7815       if (has_text)
7816       {
7817         Print("\n");
7818         Print("Envelope %d:\n'%s'\n", i + 1, text);
7819       }
7820     }
7821   }
7822
7823   PrintLine("-", 79);
7824 }
7825
7826 void DumpLevels(void)
7827 {
7828   static LevelDirTree *dumplevel_leveldir = NULL;
7829
7830   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7831                                                  global.dumplevel_leveldir);
7832
7833   if (dumplevel_leveldir == NULL)
7834     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7835
7836   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7837       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7838     Fail("no such level number: %d", global.dumplevel_level_nr);
7839
7840   leveldir_current = dumplevel_leveldir;
7841
7842   LoadLevel(global.dumplevel_level_nr);
7843   DumpLevel(&level);
7844
7845   CloseAllAndExit(0);
7846 }
7847
7848
7849 // ============================================================================
7850 // tape file functions
7851 // ============================================================================
7852
7853 static void setTapeInfoToDefaults(void)
7854 {
7855   int i;
7856
7857   // always start with reliable default values (empty tape)
7858   TapeErase();
7859
7860   // default values (also for pre-1.2 tapes) with only the first player
7861   tape.player_participates[0] = TRUE;
7862   for (i = 1; i < MAX_PLAYERS; i++)
7863     tape.player_participates[i] = FALSE;
7864
7865   // at least one (default: the first) player participates in every tape
7866   tape.num_participating_players = 1;
7867
7868   tape.property_bits = TAPE_PROPERTY_NONE;
7869
7870   tape.level_nr = level_nr;
7871   tape.counter = 0;
7872   tape.changed = FALSE;
7873
7874   tape.recording = FALSE;
7875   tape.playing = FALSE;
7876   tape.pausing = FALSE;
7877
7878   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7879   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7880
7881   tape.no_info_chunk = TRUE;
7882   tape.no_valid_file = FALSE;
7883 }
7884
7885 static int getTapePosSize(struct TapeInfo *tape)
7886 {
7887   int tape_pos_size = 0;
7888
7889   if (tape->use_key_actions)
7890     tape_pos_size += tape->num_participating_players;
7891
7892   if (tape->use_mouse_actions)
7893     tape_pos_size += 3;         // x and y position and mouse button mask
7894
7895   tape_pos_size += 1;           // tape action delay value
7896
7897   return tape_pos_size;
7898 }
7899
7900 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7901 {
7902   tape->use_key_actions = FALSE;
7903   tape->use_mouse_actions = FALSE;
7904
7905   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7906     tape->use_key_actions = TRUE;
7907
7908   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7909     tape->use_mouse_actions = TRUE;
7910 }
7911
7912 static int getTapeActionValue(struct TapeInfo *tape)
7913 {
7914   return (tape->use_key_actions &&
7915           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7916           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7917           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7918           TAPE_ACTIONS_DEFAULT);
7919 }
7920
7921 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7922 {
7923   tape->file_version = getFileVersion(file);
7924   tape->game_version = getFileVersion(file);
7925
7926   return chunk_size;
7927 }
7928
7929 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7930 {
7931   int i;
7932
7933   tape->random_seed = getFile32BitBE(file);
7934   tape->date        = getFile32BitBE(file);
7935   tape->length      = getFile32BitBE(file);
7936
7937   // read header fields that are new since version 1.2
7938   if (tape->file_version >= FILE_VERSION_1_2)
7939   {
7940     byte store_participating_players = getFile8Bit(file);
7941     int engine_version;
7942
7943     // since version 1.2, tapes store which players participate in the tape
7944     tape->num_participating_players = 0;
7945     for (i = 0; i < MAX_PLAYERS; i++)
7946     {
7947       tape->player_participates[i] = FALSE;
7948
7949       if (store_participating_players & (1 << i))
7950       {
7951         tape->player_participates[i] = TRUE;
7952         tape->num_participating_players++;
7953       }
7954     }
7955
7956     setTapeActionFlags(tape, getFile8Bit(file));
7957
7958     tape->property_bits = getFile8Bit(file);
7959
7960     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7961
7962     engine_version = getFileVersion(file);
7963     if (engine_version > 0)
7964       tape->engine_version = engine_version;
7965     else
7966       tape->engine_version = tape->game_version;
7967   }
7968
7969   return chunk_size;
7970 }
7971
7972 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7973 {
7974   tape->scr_fieldx = getFile8Bit(file);
7975   tape->scr_fieldy = getFile8Bit(file);
7976
7977   return chunk_size;
7978 }
7979
7980 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7981 {
7982   char *level_identifier = NULL;
7983   int level_identifier_size;
7984   int i;
7985
7986   tape->no_info_chunk = FALSE;
7987
7988   level_identifier_size = getFile16BitBE(file);
7989
7990   level_identifier = checked_malloc(level_identifier_size);
7991
7992   for (i = 0; i < level_identifier_size; i++)
7993     level_identifier[i] = getFile8Bit(file);
7994
7995   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7996   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7997
7998   checked_free(level_identifier);
7999
8000   tape->level_nr = getFile16BitBE(file);
8001
8002   chunk_size = 2 + level_identifier_size + 2;
8003
8004   return chunk_size;
8005 }
8006
8007 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8008 {
8009   int i, j;
8010   int tape_pos_size = getTapePosSize(tape);
8011   int chunk_size_expected = tape_pos_size * tape->length;
8012
8013   if (chunk_size_expected != chunk_size)
8014   {
8015     ReadUnusedBytesFromFile(file, chunk_size);
8016     return chunk_size_expected;
8017   }
8018
8019   for (i = 0; i < tape->length; i++)
8020   {
8021     if (i >= MAX_TAPE_LEN)
8022     {
8023       Warn("tape truncated -- size exceeds maximum tape size %d",
8024             MAX_TAPE_LEN);
8025
8026       // tape too large; read and ignore remaining tape data from this chunk
8027       for (;i < tape->length; i++)
8028         ReadUnusedBytesFromFile(file, tape_pos_size);
8029
8030       break;
8031     }
8032
8033     if (tape->use_key_actions)
8034     {
8035       for (j = 0; j < MAX_PLAYERS; j++)
8036       {
8037         tape->pos[i].action[j] = MV_NONE;
8038
8039         if (tape->player_participates[j])
8040           tape->pos[i].action[j] = getFile8Bit(file);
8041       }
8042     }
8043
8044     if (tape->use_mouse_actions)
8045     {
8046       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8047       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8048       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8049     }
8050
8051     tape->pos[i].delay = getFile8Bit(file);
8052
8053     if (tape->file_version == FILE_VERSION_1_0)
8054     {
8055       // eliminate possible diagonal moves in old tapes
8056       // this is only for backward compatibility
8057
8058       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8059       byte action = tape->pos[i].action[0];
8060       int k, num_moves = 0;
8061
8062       for (k = 0; k<4; k++)
8063       {
8064         if (action & joy_dir[k])
8065         {
8066           tape->pos[i + num_moves].action[0] = joy_dir[k];
8067           if (num_moves > 0)
8068             tape->pos[i + num_moves].delay = 0;
8069           num_moves++;
8070         }
8071       }
8072
8073       if (num_moves > 1)
8074       {
8075         num_moves--;
8076         i += num_moves;
8077         tape->length += num_moves;
8078       }
8079     }
8080     else if (tape->file_version < FILE_VERSION_2_0)
8081     {
8082       // convert pre-2.0 tapes to new tape format
8083
8084       if (tape->pos[i].delay > 1)
8085       {
8086         // action part
8087         tape->pos[i + 1] = tape->pos[i];
8088         tape->pos[i + 1].delay = 1;
8089
8090         // delay part
8091         for (j = 0; j < MAX_PLAYERS; j++)
8092           tape->pos[i].action[j] = MV_NONE;
8093         tape->pos[i].delay--;
8094
8095         i++;
8096         tape->length++;
8097       }
8098     }
8099
8100     if (checkEndOfFile(file))
8101       break;
8102   }
8103
8104   if (i != tape->length)
8105     chunk_size = tape_pos_size * i;
8106
8107   return chunk_size;
8108 }
8109
8110 static void LoadTape_SokobanSolution(char *filename)
8111 {
8112   File *file;
8113   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8114
8115   if (!(file = openFile(filename, MODE_READ)))
8116   {
8117     tape.no_valid_file = TRUE;
8118
8119     return;
8120   }
8121
8122   while (!checkEndOfFile(file))
8123   {
8124     unsigned char c = getByteFromFile(file);
8125
8126     if (checkEndOfFile(file))
8127       break;
8128
8129     switch (c)
8130     {
8131       case 'u':
8132       case 'U':
8133         tape.pos[tape.length].action[0] = MV_UP;
8134         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8135         tape.length++;
8136         break;
8137
8138       case 'd':
8139       case 'D':
8140         tape.pos[tape.length].action[0] = MV_DOWN;
8141         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8142         tape.length++;
8143         break;
8144
8145       case 'l':
8146       case 'L':
8147         tape.pos[tape.length].action[0] = MV_LEFT;
8148         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8149         tape.length++;
8150         break;
8151
8152       case 'r':
8153       case 'R':
8154         tape.pos[tape.length].action[0] = MV_RIGHT;
8155         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8156         tape.length++;
8157         break;
8158
8159       case '\n':
8160       case '\r':
8161       case '\t':
8162       case ' ':
8163         // ignore white-space characters
8164         break;
8165
8166       default:
8167         tape.no_valid_file = TRUE;
8168
8169         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8170
8171         break;
8172     }
8173   }
8174
8175   closeFile(file);
8176
8177   if (tape.no_valid_file)
8178     return;
8179
8180   tape.length_frames  = GetTapeLengthFrames();
8181   tape.length_seconds = GetTapeLengthSeconds();
8182 }
8183
8184 void LoadTapeFromFilename(char *filename)
8185 {
8186   char cookie[MAX_LINE_LEN];
8187   char chunk_name[CHUNK_ID_LEN + 1];
8188   File *file;
8189   int chunk_size;
8190
8191   // always start with reliable default values
8192   setTapeInfoToDefaults();
8193
8194   if (strSuffix(filename, ".sln"))
8195   {
8196     LoadTape_SokobanSolution(filename);
8197
8198     return;
8199   }
8200
8201   if (!(file = openFile(filename, MODE_READ)))
8202   {
8203     tape.no_valid_file = TRUE;
8204
8205     return;
8206   }
8207
8208   getFileChunkBE(file, chunk_name, NULL);
8209   if (strEqual(chunk_name, "RND1"))
8210   {
8211     getFile32BitBE(file);               // not used
8212
8213     getFileChunkBE(file, chunk_name, NULL);
8214     if (!strEqual(chunk_name, "TAPE"))
8215     {
8216       tape.no_valid_file = TRUE;
8217
8218       Warn("unknown format of tape file '%s'", filename);
8219
8220       closeFile(file);
8221
8222       return;
8223     }
8224   }
8225   else  // check for pre-2.0 file format with cookie string
8226   {
8227     strcpy(cookie, chunk_name);
8228     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8229       cookie[4] = '\0';
8230     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8231       cookie[strlen(cookie) - 1] = '\0';
8232
8233     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8234     {
8235       tape.no_valid_file = TRUE;
8236
8237       Warn("unknown format of tape file '%s'", filename);
8238
8239       closeFile(file);
8240
8241       return;
8242     }
8243
8244     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8245     {
8246       tape.no_valid_file = TRUE;
8247
8248       Warn("unsupported version of tape file '%s'", filename);
8249
8250       closeFile(file);
8251
8252       return;
8253     }
8254
8255     // pre-2.0 tape files have no game version, so use file version here
8256     tape.game_version = tape.file_version;
8257   }
8258
8259   if (tape.file_version < FILE_VERSION_1_2)
8260   {
8261     // tape files from versions before 1.2.0 without chunk structure
8262     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8263     LoadTape_BODY(file, 2 * tape.length,      &tape);
8264   }
8265   else
8266   {
8267     static struct
8268     {
8269       char *name;
8270       int size;
8271       int (*loader)(File *, int, struct TapeInfo *);
8272     }
8273     chunk_info[] =
8274     {
8275       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8276       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8277       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8278       { "INFO", -1,                     LoadTape_INFO },
8279       { "BODY", -1,                     LoadTape_BODY },
8280       {  NULL,  0,                      NULL }
8281     };
8282
8283     while (getFileChunkBE(file, chunk_name, &chunk_size))
8284     {
8285       int i = 0;
8286
8287       while (chunk_info[i].name != NULL &&
8288              !strEqual(chunk_name, chunk_info[i].name))
8289         i++;
8290
8291       if (chunk_info[i].name == NULL)
8292       {
8293         Warn("unknown chunk '%s' in tape file '%s'",
8294               chunk_name, filename);
8295
8296         ReadUnusedBytesFromFile(file, chunk_size);
8297       }
8298       else if (chunk_info[i].size != -1 &&
8299                chunk_info[i].size != chunk_size)
8300       {
8301         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8302               chunk_size, chunk_name, filename);
8303
8304         ReadUnusedBytesFromFile(file, chunk_size);
8305       }
8306       else
8307       {
8308         // call function to load this tape chunk
8309         int chunk_size_expected =
8310           (chunk_info[i].loader)(file, chunk_size, &tape);
8311
8312         // the size of some chunks cannot be checked before reading other
8313         // chunks first (like "HEAD" and "BODY") that contain some header
8314         // information, so check them here
8315         if (chunk_size_expected != chunk_size)
8316         {
8317           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8318                 chunk_size, chunk_name, filename);
8319         }
8320       }
8321     }
8322   }
8323
8324   closeFile(file);
8325
8326   tape.length_frames  = GetTapeLengthFrames();
8327   tape.length_seconds = GetTapeLengthSeconds();
8328
8329 #if 0
8330   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8331         tape.file_version);
8332   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8333         tape.game_version);
8334   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8335         tape.engine_version);
8336 #endif
8337 }
8338
8339 void LoadTape(int nr)
8340 {
8341   char *filename = getTapeFilename(nr);
8342
8343   LoadTapeFromFilename(filename);
8344 }
8345
8346 void LoadSolutionTape(int nr)
8347 {
8348   char *filename = getSolutionTapeFilename(nr);
8349
8350   LoadTapeFromFilename(filename);
8351
8352   if (TAPE_IS_EMPTY(tape) &&
8353       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8354       level.native_sp_level->demo.is_available)
8355     CopyNativeTape_SP_to_RND(&level);
8356 }
8357
8358 void LoadScoreTape(char *score_tape_basename, int nr)
8359 {
8360   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8361
8362   LoadTapeFromFilename(filename);
8363 }
8364
8365 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8366 {
8367   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8368
8369   LoadTapeFromFilename(filename);
8370 }
8371
8372 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8373 {
8374   // chunk required for team mode tapes with non-default screen size
8375   return (tape->num_participating_players > 1 &&
8376           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8377            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8378 }
8379
8380 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8381 {
8382   putFileVersion(file, tape->file_version);
8383   putFileVersion(file, tape->game_version);
8384 }
8385
8386 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8387 {
8388   int i;
8389   byte store_participating_players = 0;
8390
8391   // set bits for participating players for compact storage
8392   for (i = 0; i < MAX_PLAYERS; i++)
8393     if (tape->player_participates[i])
8394       store_participating_players |= (1 << i);
8395
8396   putFile32BitBE(file, tape->random_seed);
8397   putFile32BitBE(file, tape->date);
8398   putFile32BitBE(file, tape->length);
8399
8400   putFile8Bit(file, store_participating_players);
8401
8402   putFile8Bit(file, getTapeActionValue(tape));
8403
8404   putFile8Bit(file, tape->property_bits);
8405
8406   // unused bytes not at the end here for 4-byte alignment of engine_version
8407   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8408
8409   putFileVersion(file, tape->engine_version);
8410 }
8411
8412 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8413 {
8414   putFile8Bit(file, tape->scr_fieldx);
8415   putFile8Bit(file, tape->scr_fieldy);
8416 }
8417
8418 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8419 {
8420   int level_identifier_size = strlen(tape->level_identifier) + 1;
8421   int i;
8422
8423   putFile16BitBE(file, level_identifier_size);
8424
8425   for (i = 0; i < level_identifier_size; i++)
8426     putFile8Bit(file, tape->level_identifier[i]);
8427
8428   putFile16BitBE(file, tape->level_nr);
8429 }
8430
8431 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8432 {
8433   int i, j;
8434
8435   for (i = 0; i < tape->length; i++)
8436   {
8437     if (tape->use_key_actions)
8438     {
8439       for (j = 0; j < MAX_PLAYERS; j++)
8440         if (tape->player_participates[j])
8441           putFile8Bit(file, tape->pos[i].action[j]);
8442     }
8443
8444     if (tape->use_mouse_actions)
8445     {
8446       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8447       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8448       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8449     }
8450
8451     putFile8Bit(file, tape->pos[i].delay);
8452   }
8453 }
8454
8455 void SaveTapeToFilename(char *filename)
8456 {
8457   FILE *file;
8458   int tape_pos_size;
8459   int info_chunk_size;
8460   int body_chunk_size;
8461
8462   if (!(file = fopen(filename, MODE_WRITE)))
8463   {
8464     Warn("cannot save level recording file '%s'", filename);
8465
8466     return;
8467   }
8468
8469   tape_pos_size = getTapePosSize(&tape);
8470
8471   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8472   body_chunk_size = tape_pos_size * tape.length;
8473
8474   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8475   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8476
8477   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8478   SaveTape_VERS(file, &tape);
8479
8480   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8481   SaveTape_HEAD(file, &tape);
8482
8483   if (checkSaveTape_SCRN(&tape))
8484   {
8485     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8486     SaveTape_SCRN(file, &tape);
8487   }
8488
8489   putFileChunkBE(file, "INFO", info_chunk_size);
8490   SaveTape_INFO(file, &tape);
8491
8492   putFileChunkBE(file, "BODY", body_chunk_size);
8493   SaveTape_BODY(file, &tape);
8494
8495   fclose(file);
8496
8497   SetFilePermissions(filename, PERMS_PRIVATE);
8498 }
8499
8500 static void SaveTapeExt(char *filename)
8501 {
8502   int i;
8503
8504   tape.file_version = FILE_VERSION_ACTUAL;
8505   tape.game_version = GAME_VERSION_ACTUAL;
8506
8507   tape.num_participating_players = 0;
8508
8509   // count number of participating players
8510   for (i = 0; i < MAX_PLAYERS; i++)
8511     if (tape.player_participates[i])
8512       tape.num_participating_players++;
8513
8514   SaveTapeToFilename(filename);
8515
8516   tape.changed = FALSE;
8517 }
8518
8519 void SaveTape(int nr)
8520 {
8521   char *filename = getTapeFilename(nr);
8522
8523   InitTapeDirectory(leveldir_current->subdir);
8524
8525   SaveTapeExt(filename);
8526 }
8527
8528 void SaveScoreTape(int nr)
8529 {
8530   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8531
8532   // used instead of "leveldir_current->subdir" (for network games)
8533   InitScoreTapeDirectory(levelset.identifier, nr);
8534
8535   SaveTapeExt(filename);
8536 }
8537
8538 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8539                                   unsigned int req_state_added)
8540 {
8541   char *filename = getTapeFilename(nr);
8542   boolean new_tape = !fileExists(filename);
8543   boolean tape_saved = FALSE;
8544
8545   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8546   {
8547     SaveTape(nr);
8548
8549     if (new_tape)
8550       Request(msg_saved, REQ_CONFIRM | req_state_added);
8551
8552     tape_saved = TRUE;
8553   }
8554
8555   return tape_saved;
8556 }
8557
8558 boolean SaveTapeChecked(int nr)
8559 {
8560   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8561 }
8562
8563 boolean SaveTapeChecked_LevelSolved(int nr)
8564 {
8565   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8566                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8567 }
8568
8569 void DumpTape(struct TapeInfo *tape)
8570 {
8571   int tape_frame_counter;
8572   int i, j;
8573
8574   if (tape->no_valid_file)
8575   {
8576     Warn("cannot dump -- no valid tape file found");
8577
8578     return;
8579   }
8580
8581   PrintLine("-", 79);
8582
8583   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8584         tape->level_nr, tape->file_version, tape->game_version);
8585   Print("                  (effective engine version %08d)\n",
8586         tape->engine_version);
8587   Print("Level series identifier: '%s'\n", tape->level_identifier);
8588
8589   Print("Special tape properties: ");
8590   if (tape->property_bits == TAPE_PROPERTY_NONE)
8591     Print("[none]");
8592   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8593     Print("[em_random_bug]");
8594   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8595     Print("[game_speed]");
8596   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8597     Print("[pause]");
8598   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8599     Print("[single_step]");
8600   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8601     Print("[snapshot]");
8602   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8603     Print("[replayed]");
8604   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8605     Print("[tas_keys]");
8606   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8607     Print("[small_graphics]");
8608   Print("\n");
8609
8610   int year2 = tape->date / 10000;
8611   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8612   int month_index_raw = (tape->date / 100) % 100;
8613   int month_index = month_index_raw % 12;       // prevent invalid index
8614   int month = month_index + 1;
8615   int day = tape->date % 100;
8616
8617   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8618
8619   PrintLine("-", 79);
8620
8621   tape_frame_counter = 0;
8622
8623   for (i = 0; i < tape->length; i++)
8624   {
8625     if (i >= MAX_TAPE_LEN)
8626       break;
8627
8628     Print("%04d: ", i);
8629
8630     for (j = 0; j < MAX_PLAYERS; j++)
8631     {
8632       if (tape->player_participates[j])
8633       {
8634         int action = tape->pos[i].action[j];
8635
8636         Print("%d:%02x ", j, action);
8637         Print("[%c%c%c%c|%c%c] - ",
8638               (action & JOY_LEFT ? '<' : ' '),
8639               (action & JOY_RIGHT ? '>' : ' '),
8640               (action & JOY_UP ? '^' : ' '),
8641               (action & JOY_DOWN ? 'v' : ' '),
8642               (action & JOY_BUTTON_1 ? '1' : ' '),
8643               (action & JOY_BUTTON_2 ? '2' : ' '));
8644       }
8645     }
8646
8647     Print("(%03d) ", tape->pos[i].delay);
8648     Print("[%05d]\n", tape_frame_counter);
8649
8650     tape_frame_counter += tape->pos[i].delay;
8651   }
8652
8653   PrintLine("-", 79);
8654 }
8655
8656 void DumpTapes(void)
8657 {
8658   static LevelDirTree *dumptape_leveldir = NULL;
8659
8660   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8661                                                 global.dumptape_leveldir);
8662
8663   if (dumptape_leveldir == NULL)
8664     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8665
8666   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8667       global.dumptape_level_nr > dumptape_leveldir->last_level)
8668     Fail("no such level number: %d", global.dumptape_level_nr);
8669
8670   leveldir_current = dumptape_leveldir;
8671
8672   if (options.mytapes)
8673     LoadTape(global.dumptape_level_nr);
8674   else
8675     LoadSolutionTape(global.dumptape_level_nr);
8676
8677   DumpTape(&tape);
8678
8679   CloseAllAndExit(0);
8680 }
8681
8682
8683 // ============================================================================
8684 // score file functions
8685 // ============================================================================
8686
8687 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8688 {
8689   int i;
8690
8691   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8692   {
8693     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8694     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8695     scores->entry[i].score = 0;
8696     scores->entry[i].time = 0;
8697
8698     scores->entry[i].id = -1;
8699     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
8700     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
8701     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
8702     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8703     strcpy(scores->entry[i].country_code, "??");
8704   }
8705
8706   scores->num_entries = 0;
8707   scores->last_added = -1;
8708   scores->last_added_local = -1;
8709
8710   scores->updated = FALSE;
8711   scores->uploaded = FALSE;
8712   scores->tape_downloaded = FALSE;
8713   scores->force_last_added = FALSE;
8714
8715   // The following values are intentionally not reset here:
8716   // - last_level_nr
8717   // - last_entry_nr
8718   // - next_level_nr
8719   // - continue_playing
8720   // - continue_on_return
8721 }
8722
8723 static void setScoreInfoToDefaults(void)
8724 {
8725   setScoreInfoToDefaultsExt(&scores);
8726 }
8727
8728 static void setServerScoreInfoToDefaults(void)
8729 {
8730   setScoreInfoToDefaultsExt(&server_scores);
8731 }
8732
8733 static void LoadScore_OLD(int nr)
8734 {
8735   int i;
8736   char *filename = getScoreFilename(nr);
8737   char cookie[MAX_LINE_LEN];
8738   char line[MAX_LINE_LEN];
8739   char *line_ptr;
8740   FILE *file;
8741
8742   if (!(file = fopen(filename, MODE_READ)))
8743     return;
8744
8745   // check file identifier
8746   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8747     cookie[0] = '\0';
8748   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8749     cookie[strlen(cookie) - 1] = '\0';
8750
8751   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8752   {
8753     Warn("unknown format of score file '%s'", filename);
8754
8755     fclose(file);
8756
8757     return;
8758   }
8759
8760   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8761   {
8762     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8763       Warn("fscanf() failed; %s", strerror(errno));
8764
8765     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8766       line[0] = '\0';
8767
8768     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8769       line[strlen(line) - 1] = '\0';
8770
8771     for (line_ptr = line; *line_ptr; line_ptr++)
8772     {
8773       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8774       {
8775         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8776         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8777         break;
8778       }
8779     }
8780   }
8781
8782   fclose(file);
8783 }
8784
8785 static void ConvertScore_OLD(void)
8786 {
8787   // only convert score to time for levels that rate playing time over score
8788   if (!level.rate_time_over_score)
8789     return;
8790
8791   // convert old score to playing time for score-less levels (like Supaplex)
8792   int time_final_max = 999;
8793   int i;
8794
8795   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8796   {
8797     int score = scores.entry[i].score;
8798
8799     if (score > 0 && score < time_final_max)
8800       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8801   }
8802 }
8803
8804 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8805 {
8806   scores->file_version = getFileVersion(file);
8807   scores->game_version = getFileVersion(file);
8808
8809   return chunk_size;
8810 }
8811
8812 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8813 {
8814   char *level_identifier = NULL;
8815   int level_identifier_size;
8816   int i;
8817
8818   level_identifier_size = getFile16BitBE(file);
8819
8820   level_identifier = checked_malloc(level_identifier_size);
8821
8822   for (i = 0; i < level_identifier_size; i++)
8823     level_identifier[i] = getFile8Bit(file);
8824
8825   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8826   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8827
8828   checked_free(level_identifier);
8829
8830   scores->level_nr = getFile16BitBE(file);
8831   scores->num_entries = getFile16BitBE(file);
8832
8833   chunk_size = 2 + level_identifier_size + 2 + 2;
8834
8835   return chunk_size;
8836 }
8837
8838 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8839 {
8840   int i, j;
8841
8842   for (i = 0; i < scores->num_entries; i++)
8843   {
8844     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8845       scores->entry[i].name[j] = getFile8Bit(file);
8846
8847     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8848   }
8849
8850   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8851
8852   return chunk_size;
8853 }
8854
8855 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8856 {
8857   int i;
8858
8859   for (i = 0; i < scores->num_entries; i++)
8860     scores->entry[i].score = getFile16BitBE(file);
8861
8862   chunk_size = scores->num_entries * 2;
8863
8864   return chunk_size;
8865 }
8866
8867 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8868 {
8869   int i;
8870
8871   for (i = 0; i < scores->num_entries; i++)
8872     scores->entry[i].score = getFile32BitBE(file);
8873
8874   chunk_size = scores->num_entries * 4;
8875
8876   return chunk_size;
8877 }
8878
8879 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8880 {
8881   int i;
8882
8883   for (i = 0; i < scores->num_entries; i++)
8884     scores->entry[i].time = getFile32BitBE(file);
8885
8886   chunk_size = scores->num_entries * 4;
8887
8888   return chunk_size;
8889 }
8890
8891 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8892 {
8893   int i, j;
8894
8895   for (i = 0; i < scores->num_entries; i++)
8896   {
8897     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8898       scores->entry[i].tape_basename[j] = getFile8Bit(file);
8899
8900     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8901   }
8902
8903   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8904
8905   return chunk_size;
8906 }
8907
8908 void LoadScore(int nr)
8909 {
8910   char *filename = getScoreFilename(nr);
8911   char cookie[MAX_LINE_LEN];
8912   char chunk_name[CHUNK_ID_LEN + 1];
8913   int chunk_size;
8914   boolean old_score_file_format = FALSE;
8915   File *file;
8916
8917   // always start with reliable default values
8918   setScoreInfoToDefaults();
8919
8920   if (!(file = openFile(filename, MODE_READ)))
8921     return;
8922
8923   getFileChunkBE(file, chunk_name, NULL);
8924   if (strEqual(chunk_name, "RND1"))
8925   {
8926     getFile32BitBE(file);               // not used
8927
8928     getFileChunkBE(file, chunk_name, NULL);
8929     if (!strEqual(chunk_name, "SCOR"))
8930     {
8931       Warn("unknown format of score file '%s'", filename);
8932
8933       closeFile(file);
8934
8935       return;
8936     }
8937   }
8938   else  // check for old file format with cookie string
8939   {
8940     strcpy(cookie, chunk_name);
8941     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8942       cookie[4] = '\0';
8943     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8944       cookie[strlen(cookie) - 1] = '\0';
8945
8946     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8947     {
8948       Warn("unknown format of score file '%s'", filename);
8949
8950       closeFile(file);
8951
8952       return;
8953     }
8954
8955     old_score_file_format = TRUE;
8956   }
8957
8958   if (old_score_file_format)
8959   {
8960     // score files from versions before 4.2.4.0 without chunk structure
8961     LoadScore_OLD(nr);
8962
8963     // convert score to time, if possible (mainly for Supaplex levels)
8964     ConvertScore_OLD();
8965   }
8966   else
8967   {
8968     static struct
8969     {
8970       char *name;
8971       int size;
8972       int (*loader)(File *, int, struct ScoreInfo *);
8973     }
8974     chunk_info[] =
8975     {
8976       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
8977       { "INFO", -1,                     LoadScore_INFO },
8978       { "NAME", -1,                     LoadScore_NAME },
8979       { "SCOR", -1,                     LoadScore_SCOR },
8980       { "SC4R", -1,                     LoadScore_SC4R },
8981       { "TIME", -1,                     LoadScore_TIME },
8982       { "TAPE", -1,                     LoadScore_TAPE },
8983
8984       {  NULL,  0,                      NULL }
8985     };
8986
8987     while (getFileChunkBE(file, chunk_name, &chunk_size))
8988     {
8989       int i = 0;
8990
8991       while (chunk_info[i].name != NULL &&
8992              !strEqual(chunk_name, chunk_info[i].name))
8993         i++;
8994
8995       if (chunk_info[i].name == NULL)
8996       {
8997         Warn("unknown chunk '%s' in score file '%s'",
8998               chunk_name, filename);
8999
9000         ReadUnusedBytesFromFile(file, chunk_size);
9001       }
9002       else if (chunk_info[i].size != -1 &&
9003                chunk_info[i].size != chunk_size)
9004       {
9005         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9006               chunk_size, chunk_name, filename);
9007
9008         ReadUnusedBytesFromFile(file, chunk_size);
9009       }
9010       else
9011       {
9012         // call function to load this score chunk
9013         int chunk_size_expected =
9014           (chunk_info[i].loader)(file, chunk_size, &scores);
9015
9016         // the size of some chunks cannot be checked before reading other
9017         // chunks first (like "HEAD" and "BODY") that contain some header
9018         // information, so check them here
9019         if (chunk_size_expected != chunk_size)
9020         {
9021           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9022                 chunk_size, chunk_name, filename);
9023         }
9024       }
9025     }
9026   }
9027
9028   closeFile(file);
9029 }
9030
9031 #if ENABLE_HISTORIC_CHUNKS
9032 void SaveScore_OLD(int nr)
9033 {
9034   int i;
9035   char *filename = getScoreFilename(nr);
9036   FILE *file;
9037
9038   // used instead of "leveldir_current->subdir" (for network games)
9039   InitScoreDirectory(levelset.identifier);
9040
9041   if (!(file = fopen(filename, MODE_WRITE)))
9042   {
9043     Warn("cannot save score for level %d", nr);
9044
9045     return;
9046   }
9047
9048   fprintf(file, "%s\n\n", SCORE_COOKIE);
9049
9050   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9051     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9052
9053   fclose(file);
9054
9055   SetFilePermissions(filename, PERMS_PRIVATE);
9056 }
9057 #endif
9058
9059 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9060 {
9061   putFileVersion(file, scores->file_version);
9062   putFileVersion(file, scores->game_version);
9063 }
9064
9065 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9066 {
9067   int level_identifier_size = strlen(scores->level_identifier) + 1;
9068   int i;
9069
9070   putFile16BitBE(file, level_identifier_size);
9071
9072   for (i = 0; i < level_identifier_size; i++)
9073     putFile8Bit(file, scores->level_identifier[i]);
9074
9075   putFile16BitBE(file, scores->level_nr);
9076   putFile16BitBE(file, scores->num_entries);
9077 }
9078
9079 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9080 {
9081   int i, j;
9082
9083   for (i = 0; i < scores->num_entries; i++)
9084   {
9085     int name_size = strlen(scores->entry[i].name);
9086
9087     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9088       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9089   }
9090 }
9091
9092 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9093 {
9094   int i;
9095
9096   for (i = 0; i < scores->num_entries; i++)
9097     putFile16BitBE(file, scores->entry[i].score);
9098 }
9099
9100 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9101 {
9102   int i;
9103
9104   for (i = 0; i < scores->num_entries; i++)
9105     putFile32BitBE(file, scores->entry[i].score);
9106 }
9107
9108 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9109 {
9110   int i;
9111
9112   for (i = 0; i < scores->num_entries; i++)
9113     putFile32BitBE(file, scores->entry[i].time);
9114 }
9115
9116 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9117 {
9118   int i, j;
9119
9120   for (i = 0; i < scores->num_entries; i++)
9121   {
9122     int size = strlen(scores->entry[i].tape_basename);
9123
9124     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9125       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9126   }
9127 }
9128
9129 static void SaveScoreToFilename(char *filename)
9130 {
9131   FILE *file;
9132   int info_chunk_size;
9133   int name_chunk_size;
9134   int scor_chunk_size;
9135   int sc4r_chunk_size;
9136   int time_chunk_size;
9137   int tape_chunk_size;
9138   boolean has_large_score_values;
9139   int i;
9140
9141   if (!(file = fopen(filename, MODE_WRITE)))
9142   {
9143     Warn("cannot save score file '%s'", filename);
9144
9145     return;
9146   }
9147
9148   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9149   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9150   scor_chunk_size = scores.num_entries * 2;
9151   sc4r_chunk_size = scores.num_entries * 4;
9152   time_chunk_size = scores.num_entries * 4;
9153   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9154
9155   has_large_score_values = FALSE;
9156   for (i = 0; i < scores.num_entries; i++)
9157     if (scores.entry[i].score > 0xffff)
9158       has_large_score_values = TRUE;
9159
9160   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9161   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9162
9163   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9164   SaveScore_VERS(file, &scores);
9165
9166   putFileChunkBE(file, "INFO", info_chunk_size);
9167   SaveScore_INFO(file, &scores);
9168
9169   putFileChunkBE(file, "NAME", name_chunk_size);
9170   SaveScore_NAME(file, &scores);
9171
9172   if (has_large_score_values)
9173   {
9174     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9175     SaveScore_SC4R(file, &scores);
9176   }
9177   else
9178   {
9179     putFileChunkBE(file, "SCOR", scor_chunk_size);
9180     SaveScore_SCOR(file, &scores);
9181   }
9182
9183   putFileChunkBE(file, "TIME", time_chunk_size);
9184   SaveScore_TIME(file, &scores);
9185
9186   putFileChunkBE(file, "TAPE", tape_chunk_size);
9187   SaveScore_TAPE(file, &scores);
9188
9189   fclose(file);
9190
9191   SetFilePermissions(filename, PERMS_PRIVATE);
9192 }
9193
9194 void SaveScore(int nr)
9195 {
9196   char *filename = getScoreFilename(nr);
9197   int i;
9198
9199   // used instead of "leveldir_current->subdir" (for network games)
9200   InitScoreDirectory(levelset.identifier);
9201
9202   scores.file_version = FILE_VERSION_ACTUAL;
9203   scores.game_version = GAME_VERSION_ACTUAL;
9204
9205   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9206   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9207   scores.level_nr = level_nr;
9208
9209   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9210     if (scores.entry[i].score == 0 &&
9211         scores.entry[i].time == 0 &&
9212         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9213       break;
9214
9215   scores.num_entries = i;
9216
9217   if (scores.num_entries == 0)
9218     return;
9219
9220   SaveScoreToFilename(filename);
9221 }
9222
9223 static void LoadServerScoreFromCache(int nr)
9224 {
9225   struct ScoreEntry score_entry;
9226   struct
9227   {
9228     void *value;
9229     boolean is_string;
9230     int string_size;
9231   }
9232   score_mapping[] =
9233   {
9234     { &score_entry.score,               FALSE,  0                       },
9235     { &score_entry.time,                FALSE,  0                       },
9236     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9237     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9238     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9239     { &score_entry.id,                  FALSE,  0                       },
9240     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9241     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9242     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9243     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9244
9245     { NULL,                             FALSE,  0                       }
9246   };
9247   char *filename = getScoreCacheFilename(nr);
9248   SetupFileHash *score_hash = loadSetupFileHash(filename);
9249   int i, j;
9250
9251   server_scores.num_entries = 0;
9252
9253   if (score_hash == NULL)
9254     return;
9255
9256   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9257   {
9258     score_entry = server_scores.entry[i];
9259
9260     for (j = 0; score_mapping[j].value != NULL; j++)
9261     {
9262       char token[10];
9263
9264       sprintf(token, "%02d.%d", i, j);
9265
9266       char *value = getHashEntry(score_hash, token);
9267
9268       if (value == NULL)
9269         continue;
9270
9271       if (score_mapping[j].is_string)
9272       {
9273         char *score_value = (char *)score_mapping[j].value;
9274         int value_size = score_mapping[j].string_size;
9275
9276         strncpy(score_value, value, value_size);
9277         score_value[value_size] = '\0';
9278       }
9279       else
9280       {
9281         int *score_value = (int *)score_mapping[j].value;
9282
9283         *score_value = atoi(value);
9284       }
9285
9286       server_scores.num_entries = i + 1;
9287     }
9288
9289     server_scores.entry[i] = score_entry;
9290   }
9291
9292   freeSetupFileHash(score_hash);
9293 }
9294
9295 void LoadServerScore(int nr, boolean download_score)
9296 {
9297   if (!setup.use_api_server)
9298     return;
9299
9300   // always start with reliable default values
9301   setServerScoreInfoToDefaults();
9302
9303   // 1st step: load server scores from cache file (which may not exist)
9304   // (this should prevent reading it while the thread is writing to it)
9305   LoadServerScoreFromCache(nr);
9306
9307   if (download_score && runtime.use_api_server)
9308   {
9309     // 2nd step: download server scores from score server to cache file
9310     // (as thread, as it might time out if the server is not reachable)
9311     ApiGetScoreAsThread(nr);
9312   }
9313 }
9314
9315 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9316 {
9317   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9318
9319   // if score tape not uploaded, ask for uploading missing tapes later
9320   if (!setup.has_remaining_tapes)
9321     setup.ask_for_remaining_tapes = TRUE;
9322
9323   setup.provide_uploading_tapes = TRUE;
9324   setup.has_remaining_tapes = TRUE;
9325
9326   SaveSetup_ServerSetup();
9327 }
9328
9329 void SaveServerScore(int nr, boolean tape_saved)
9330 {
9331   if (!runtime.use_api_server)
9332   {
9333     PrepareScoreTapesForUpload(leveldir_current->subdir);
9334
9335     return;
9336   }
9337
9338   ApiAddScoreAsThread(nr, tape_saved, NULL);
9339 }
9340
9341 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9342                              char *score_tape_filename)
9343 {
9344   if (!runtime.use_api_server)
9345     return;
9346
9347   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9348 }
9349
9350 void LoadLocalAndServerScore(int nr, boolean download_score)
9351 {
9352   int last_added_local = scores.last_added_local;
9353   boolean force_last_added = scores.force_last_added;
9354
9355   // needed if only showing server scores
9356   setScoreInfoToDefaults();
9357
9358   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9359     LoadScore(nr);
9360
9361   // restore last added local score entry (before merging server scores)
9362   scores.last_added = scores.last_added_local = last_added_local;
9363
9364   if (setup.use_api_server &&
9365       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9366   {
9367     // load server scores from cache file and trigger update from server
9368     LoadServerScore(nr, download_score);
9369
9370     // merge local scores with scores from server
9371     MergeServerScore();
9372   }
9373
9374   if (force_last_added)
9375     scores.force_last_added = force_last_added;
9376 }
9377
9378
9379 // ============================================================================
9380 // setup file functions
9381 // ============================================================================
9382
9383 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9384
9385
9386 static struct TokenInfo global_setup_tokens[] =
9387 {
9388   {
9389     TYPE_STRING,
9390     &setup.player_name,                         "player_name"
9391   },
9392   {
9393     TYPE_SWITCH,
9394     &setup.multiple_users,                      "multiple_users"
9395   },
9396   {
9397     TYPE_SWITCH,
9398     &setup.sound,                               "sound"
9399   },
9400   {
9401     TYPE_SWITCH,
9402     &setup.sound_loops,                         "repeating_sound_loops"
9403   },
9404   {
9405     TYPE_SWITCH,
9406     &setup.sound_music,                         "background_music"
9407   },
9408   {
9409     TYPE_SWITCH,
9410     &setup.sound_simple,                        "simple_sound_effects"
9411   },
9412   {
9413     TYPE_SWITCH,
9414     &setup.toons,                               "toons"
9415   },
9416   {
9417     TYPE_SWITCH,
9418     &setup.scroll_delay,                        "scroll_delay"
9419   },
9420   {
9421     TYPE_SWITCH,
9422     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9423   },
9424   {
9425     TYPE_INTEGER,
9426     &setup.scroll_delay_value,                  "scroll_delay_value"
9427   },
9428   {
9429     TYPE_STRING,
9430     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9431   },
9432   {
9433     TYPE_INTEGER,
9434     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9435   },
9436   {
9437     TYPE_SWITCH,
9438     &setup.fade_screens,                        "fade_screens"
9439   },
9440   {
9441     TYPE_SWITCH,
9442     &setup.autorecord,                          "automatic_tape_recording"
9443   },
9444   {
9445     TYPE_SWITCH,
9446     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9447   },
9448   {
9449     TYPE_SWITCH,
9450     &setup.show_titlescreen,                    "show_titlescreen"
9451   },
9452   {
9453     TYPE_SWITCH,
9454     &setup.quick_doors,                         "quick_doors"
9455   },
9456   {
9457     TYPE_SWITCH,
9458     &setup.team_mode,                           "team_mode"
9459   },
9460   {
9461     TYPE_SWITCH,
9462     &setup.handicap,                            "handicap"
9463   },
9464   {
9465     TYPE_SWITCH,
9466     &setup.skip_levels,                         "skip_levels"
9467   },
9468   {
9469     TYPE_SWITCH,
9470     &setup.increment_levels,                    "increment_levels"
9471   },
9472   {
9473     TYPE_SWITCH,
9474     &setup.auto_play_next_level,                "auto_play_next_level"
9475   },
9476   {
9477     TYPE_SWITCH,
9478     &setup.count_score_after_game,              "count_score_after_game"
9479   },
9480   {
9481     TYPE_SWITCH,
9482     &setup.show_scores_after_game,              "show_scores_after_game"
9483   },
9484   {
9485     TYPE_SWITCH,
9486     &setup.time_limit,                          "time_limit"
9487   },
9488   {
9489     TYPE_SWITCH,
9490     &setup.fullscreen,                          "fullscreen"
9491   },
9492   {
9493     TYPE_INTEGER,
9494     &setup.window_scaling_percent,              "window_scaling_percent"
9495   },
9496   {
9497     TYPE_STRING,
9498     &setup.window_scaling_quality,              "window_scaling_quality"
9499   },
9500   {
9501     TYPE_STRING,
9502     &setup.screen_rendering_mode,               "screen_rendering_mode"
9503   },
9504   {
9505     TYPE_STRING,
9506     &setup.vsync_mode,                          "vsync_mode"
9507   },
9508   {
9509     TYPE_SWITCH,
9510     &setup.ask_on_escape,                       "ask_on_escape"
9511   },
9512   {
9513     TYPE_SWITCH,
9514     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
9515   },
9516   {
9517     TYPE_SWITCH,
9518     &setup.ask_on_game_over,                    "ask_on_game_over"
9519   },
9520   {
9521     TYPE_SWITCH,
9522     &setup.ask_on_quit_game,                    "ask_on_quit_game"
9523   },
9524   {
9525     TYPE_SWITCH,
9526     &setup.ask_on_quit_program,                 "ask_on_quit_program"
9527   },
9528   {
9529     TYPE_SWITCH,
9530     &setup.quick_switch,                        "quick_player_switch"
9531   },
9532   {
9533     TYPE_SWITCH,
9534     &setup.input_on_focus,                      "input_on_focus"
9535   },
9536   {
9537     TYPE_SWITCH,
9538     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
9539   },
9540   {
9541     TYPE_SWITCH,
9542     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
9543   },
9544   {
9545     TYPE_SWITCH,
9546     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
9547   },
9548   {
9549     TYPE_SWITCH,
9550     &setup.game_speed_extended,                 "game_speed_extended"
9551   },
9552   {
9553     TYPE_INTEGER,
9554     &setup.game_frame_delay,                    "game_frame_delay"
9555   },
9556   {
9557     TYPE_SWITCH,
9558     &setup.sp_show_border_elements,             "sp_show_border_elements"
9559   },
9560   {
9561     TYPE_SWITCH,
9562     &setup.small_game_graphics,                 "small_game_graphics"
9563   },
9564   {
9565     TYPE_SWITCH,
9566     &setup.show_load_save_buttons,              "show_load_save_buttons"
9567   },
9568   {
9569     TYPE_SWITCH,
9570     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
9571   },
9572   {
9573     TYPE_STRING,
9574     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
9575   },
9576   {
9577     TYPE_STRING,
9578     &setup.graphics_set,                        "graphics_set"
9579   },
9580   {
9581     TYPE_STRING,
9582     &setup.sounds_set,                          "sounds_set"
9583   },
9584   {
9585     TYPE_STRING,
9586     &setup.music_set,                           "music_set"
9587   },
9588   {
9589     TYPE_SWITCH3,
9590     &setup.override_level_graphics,             "override_level_graphics"
9591   },
9592   {
9593     TYPE_SWITCH3,
9594     &setup.override_level_sounds,               "override_level_sounds"
9595   },
9596   {
9597     TYPE_SWITCH3,
9598     &setup.override_level_music,                "override_level_music"
9599   },
9600   {
9601     TYPE_INTEGER,
9602     &setup.volume_simple,                       "volume_simple"
9603   },
9604   {
9605     TYPE_INTEGER,
9606     &setup.volume_loops,                        "volume_loops"
9607   },
9608   {
9609     TYPE_INTEGER,
9610     &setup.volume_music,                        "volume_music"
9611   },
9612   {
9613     TYPE_SWITCH,
9614     &setup.network_mode,                        "network_mode"
9615   },
9616   {
9617     TYPE_PLAYER,
9618     &setup.network_player_nr,                   "network_player"
9619   },
9620   {
9621     TYPE_STRING,
9622     &setup.network_server_hostname,             "network_server_hostname"
9623   },
9624   {
9625     TYPE_STRING,
9626     &setup.touch.control_type,                  "touch.control_type"
9627   },
9628   {
9629     TYPE_INTEGER,
9630     &setup.touch.move_distance,                 "touch.move_distance"
9631   },
9632   {
9633     TYPE_INTEGER,
9634     &setup.touch.drop_distance,                 "touch.drop_distance"
9635   },
9636   {
9637     TYPE_INTEGER,
9638     &setup.touch.transparency,                  "touch.transparency"
9639   },
9640   {
9641     TYPE_INTEGER,
9642     &setup.touch.draw_outlined,                 "touch.draw_outlined"
9643   },
9644   {
9645     TYPE_INTEGER,
9646     &setup.touch.draw_pressed,                  "touch.draw_pressed"
9647   },
9648   {
9649     TYPE_INTEGER,
9650     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
9651   },
9652   {
9653     TYPE_INTEGER,
9654     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
9655   },
9656   {
9657     TYPE_INTEGER,
9658     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
9659   },
9660   {
9661     TYPE_INTEGER,
9662     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
9663   },
9664   {
9665     TYPE_SWITCH,
9666     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
9667   },
9668 };
9669
9670 static struct TokenInfo auto_setup_tokens[] =
9671 {
9672   {
9673     TYPE_INTEGER,
9674     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
9675   },
9676 };
9677
9678 static struct TokenInfo server_setup_tokens[] =
9679 {
9680   {
9681     TYPE_STRING,
9682     &setup.player_uuid,                         "player_uuid"
9683   },
9684   {
9685     TYPE_INTEGER,
9686     &setup.player_version,                      "player_version"
9687   },
9688   {
9689     TYPE_SWITCH,
9690     &setup.use_api_server,          TEST_PREFIX "use_api_server"
9691   },
9692   {
9693     TYPE_STRING,
9694     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
9695   },
9696   {
9697     TYPE_STRING,
9698     &setup.api_server_password,     TEST_PREFIX "api_server_password"
9699   },
9700   {
9701     TYPE_SWITCH,
9702     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9703   },
9704   {
9705     TYPE_SWITCH,
9706     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9707   },
9708   {
9709     TYPE_SWITCH,
9710     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9711   },
9712   {
9713     TYPE_SWITCH,
9714     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9715   },
9716   {
9717     TYPE_SWITCH,
9718     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
9719   },
9720 };
9721
9722 static struct TokenInfo editor_setup_tokens[] =
9723 {
9724   {
9725     TYPE_SWITCH,
9726     &setup.editor.el_classic,                   "editor.el_classic"
9727   },
9728   {
9729     TYPE_SWITCH,
9730     &setup.editor.el_custom,                    "editor.el_custom"
9731   },
9732   {
9733     TYPE_SWITCH,
9734     &setup.editor.el_user_defined,              "editor.el_user_defined"
9735   },
9736   {
9737     TYPE_SWITCH,
9738     &setup.editor.el_dynamic,                   "editor.el_dynamic"
9739   },
9740   {
9741     TYPE_SWITCH,
9742     &setup.editor.el_headlines,                 "editor.el_headlines"
9743   },
9744   {
9745     TYPE_SWITCH,
9746     &setup.editor.show_element_token,           "editor.show_element_token"
9747   },
9748   {
9749     TYPE_SWITCH,
9750     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
9751   },
9752 };
9753
9754 static struct TokenInfo editor_cascade_setup_tokens[] =
9755 {
9756   {
9757     TYPE_SWITCH,
9758     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
9759   },
9760   {
9761     TYPE_SWITCH,
9762     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
9763   },
9764   {
9765     TYPE_SWITCH,
9766     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
9767   },
9768   {
9769     TYPE_SWITCH,
9770     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
9771   },
9772   {
9773     TYPE_SWITCH,
9774     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
9775   },
9776   {
9777     TYPE_SWITCH,
9778     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
9779   },
9780   {
9781     TYPE_SWITCH,
9782     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
9783   },
9784   {
9785     TYPE_SWITCH,
9786     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
9787   },
9788   {
9789     TYPE_SWITCH,
9790     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
9791   },
9792   {
9793     TYPE_SWITCH,
9794     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
9795   },
9796   {
9797     TYPE_SWITCH,
9798     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
9799   },
9800   {
9801     TYPE_SWITCH,
9802     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
9803   },
9804   {
9805     TYPE_SWITCH,
9806     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
9807   },
9808   {
9809     TYPE_SWITCH,
9810     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
9811   },
9812   {
9813     TYPE_SWITCH,
9814     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
9815   },
9816   {
9817     TYPE_SWITCH,
9818     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
9819   },
9820   {
9821     TYPE_SWITCH,
9822     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
9823   },
9824   {
9825     TYPE_SWITCH,
9826     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
9827   },
9828 };
9829
9830 static struct TokenInfo shortcut_setup_tokens[] =
9831 {
9832   {
9833     TYPE_KEY_X11,
9834     &setup.shortcut.save_game,                  "shortcut.save_game"
9835   },
9836   {
9837     TYPE_KEY_X11,
9838     &setup.shortcut.load_game,                  "shortcut.load_game"
9839   },
9840   {
9841     TYPE_KEY_X11,
9842     &setup.shortcut.restart_game,               "shortcut.restart_game"
9843   },
9844   {
9845     TYPE_KEY_X11,
9846     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
9847   },
9848   {
9849     TYPE_KEY_X11,
9850     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
9851   },
9852   {
9853     TYPE_KEY_X11,
9854     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
9855   },
9856   {
9857     TYPE_KEY_X11,
9858     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
9859   },
9860   {
9861     TYPE_KEY_X11,
9862     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
9863   },
9864   {
9865     TYPE_KEY_X11,
9866     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
9867   },
9868   {
9869     TYPE_KEY_X11,
9870     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
9871   },
9872   {
9873     TYPE_KEY_X11,
9874     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
9875   },
9876   {
9877     TYPE_KEY_X11,
9878     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
9879   },
9880   {
9881     TYPE_KEY_X11,
9882     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
9883   },
9884   {
9885     TYPE_KEY_X11,
9886     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
9887   },
9888   {
9889     TYPE_KEY_X11,
9890     &setup.shortcut.tape_record,                "shortcut.tape_record"
9891   },
9892   {
9893     TYPE_KEY_X11,
9894     &setup.shortcut.tape_play,                  "shortcut.tape_play"
9895   },
9896   {
9897     TYPE_KEY_X11,
9898     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
9899   },
9900   {
9901     TYPE_KEY_X11,
9902     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
9903   },
9904   {
9905     TYPE_KEY_X11,
9906     &setup.shortcut.sound_music,                "shortcut.sound_music"
9907   },
9908   {
9909     TYPE_KEY_X11,
9910     &setup.shortcut.snap_left,                  "shortcut.snap_left"
9911   },
9912   {
9913     TYPE_KEY_X11,
9914     &setup.shortcut.snap_right,                 "shortcut.snap_right"
9915   },
9916   {
9917     TYPE_KEY_X11,
9918     &setup.shortcut.snap_up,                    "shortcut.snap_up"
9919   },
9920   {
9921     TYPE_KEY_X11,
9922     &setup.shortcut.snap_down,                  "shortcut.snap_down"
9923   },
9924 };
9925
9926 static struct SetupInputInfo setup_input;
9927 static struct TokenInfo player_setup_tokens[] =
9928 {
9929   {
9930     TYPE_BOOLEAN,
9931     &setup_input.use_joystick,                  ".use_joystick"
9932   },
9933   {
9934     TYPE_STRING,
9935     &setup_input.joy.device_name,               ".joy.device_name"
9936   },
9937   {
9938     TYPE_INTEGER,
9939     &setup_input.joy.xleft,                     ".joy.xleft"
9940   },
9941   {
9942     TYPE_INTEGER,
9943     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
9944   },
9945   {
9946     TYPE_INTEGER,
9947     &setup_input.joy.xright,                    ".joy.xright"
9948   },
9949   {
9950     TYPE_INTEGER,
9951     &setup_input.joy.yupper,                    ".joy.yupper"
9952   },
9953   {
9954     TYPE_INTEGER,
9955     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
9956   },
9957   {
9958     TYPE_INTEGER,
9959     &setup_input.joy.ylower,                    ".joy.ylower"
9960   },
9961   {
9962     TYPE_INTEGER,
9963     &setup_input.joy.snap,                      ".joy.snap_field"
9964   },
9965   {
9966     TYPE_INTEGER,
9967     &setup_input.joy.drop,                      ".joy.place_bomb"
9968   },
9969   {
9970     TYPE_KEY_X11,
9971     &setup_input.key.left,                      ".key.move_left"
9972   },
9973   {
9974     TYPE_KEY_X11,
9975     &setup_input.key.right,                     ".key.move_right"
9976   },
9977   {
9978     TYPE_KEY_X11,
9979     &setup_input.key.up,                        ".key.move_up"
9980   },
9981   {
9982     TYPE_KEY_X11,
9983     &setup_input.key.down,                      ".key.move_down"
9984   },
9985   {
9986     TYPE_KEY_X11,
9987     &setup_input.key.snap,                      ".key.snap_field"
9988   },
9989   {
9990     TYPE_KEY_X11,
9991     &setup_input.key.drop,                      ".key.place_bomb"
9992   },
9993 };
9994
9995 static struct TokenInfo system_setup_tokens[] =
9996 {
9997   {
9998     TYPE_STRING,
9999     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10000   },
10001   {
10002     TYPE_STRING,
10003     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10004   },
10005   {
10006     TYPE_STRING,
10007     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10008   },
10009   {
10010     TYPE_INTEGER,
10011     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10012   },
10013 };
10014
10015 static struct TokenInfo internal_setup_tokens[] =
10016 {
10017   {
10018     TYPE_STRING,
10019     &setup.internal.program_title,              "program_title"
10020   },
10021   {
10022     TYPE_STRING,
10023     &setup.internal.program_version,            "program_version"
10024   },
10025   {
10026     TYPE_STRING,
10027     &setup.internal.program_author,             "program_author"
10028   },
10029   {
10030     TYPE_STRING,
10031     &setup.internal.program_email,              "program_email"
10032   },
10033   {
10034     TYPE_STRING,
10035     &setup.internal.program_website,            "program_website"
10036   },
10037   {
10038     TYPE_STRING,
10039     &setup.internal.program_copyright,          "program_copyright"
10040   },
10041   {
10042     TYPE_STRING,
10043     &setup.internal.program_company,            "program_company"
10044   },
10045   {
10046     TYPE_STRING,
10047     &setup.internal.program_icon_file,          "program_icon_file"
10048   },
10049   {
10050     TYPE_STRING,
10051     &setup.internal.default_graphics_set,       "default_graphics_set"
10052   },
10053   {
10054     TYPE_STRING,
10055     &setup.internal.default_sounds_set,         "default_sounds_set"
10056   },
10057   {
10058     TYPE_STRING,
10059     &setup.internal.default_music_set,          "default_music_set"
10060   },
10061   {
10062     TYPE_STRING,
10063     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10064   },
10065   {
10066     TYPE_STRING,
10067     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10068   },
10069   {
10070     TYPE_STRING,
10071     &setup.internal.fallback_music_file,        "fallback_music_file"
10072   },
10073   {
10074     TYPE_STRING,
10075     &setup.internal.default_level_series,       "default_level_series"
10076   },
10077   {
10078     TYPE_INTEGER,
10079     &setup.internal.default_window_width,       "default_window_width"
10080   },
10081   {
10082     TYPE_INTEGER,
10083     &setup.internal.default_window_height,      "default_window_height"
10084   },
10085   {
10086     TYPE_BOOLEAN,
10087     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10088   },
10089   {
10090     TYPE_BOOLEAN,
10091     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10092   },
10093   {
10094     TYPE_BOOLEAN,
10095     &setup.internal.create_user_levelset,       "create_user_levelset"
10096   },
10097   {
10098     TYPE_BOOLEAN,
10099     &setup.internal.menu_game,                  "menu_game"
10100   },
10101   {
10102     TYPE_BOOLEAN,
10103     &setup.internal.menu_editor,                "menu_editor"
10104   },
10105   {
10106     TYPE_BOOLEAN,
10107     &setup.internal.menu_graphics,              "menu_graphics"
10108   },
10109   {
10110     TYPE_BOOLEAN,
10111     &setup.internal.menu_sound,                 "menu_sound"
10112   },
10113   {
10114     TYPE_BOOLEAN,
10115     &setup.internal.menu_artwork,               "menu_artwork"
10116   },
10117   {
10118     TYPE_BOOLEAN,
10119     &setup.internal.menu_input,                 "menu_input"
10120   },
10121   {
10122     TYPE_BOOLEAN,
10123     &setup.internal.menu_touch,                 "menu_touch"
10124   },
10125   {
10126     TYPE_BOOLEAN,
10127     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10128   },
10129   {
10130     TYPE_BOOLEAN,
10131     &setup.internal.menu_exit,                  "menu_exit"
10132   },
10133   {
10134     TYPE_BOOLEAN,
10135     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10136   },
10137   {
10138     TYPE_BOOLEAN,
10139     &setup.internal.info_title,                 "info_title"
10140   },
10141   {
10142     TYPE_BOOLEAN,
10143     &setup.internal.info_elements,              "info_elements"
10144   },
10145   {
10146     TYPE_BOOLEAN,
10147     &setup.internal.info_music,                 "info_music"
10148   },
10149   {
10150     TYPE_BOOLEAN,
10151     &setup.internal.info_credits,               "info_credits"
10152   },
10153   {
10154     TYPE_BOOLEAN,
10155     &setup.internal.info_program,               "info_program"
10156   },
10157   {
10158     TYPE_BOOLEAN,
10159     &setup.internal.info_version,               "info_version"
10160   },
10161   {
10162     TYPE_BOOLEAN,
10163     &setup.internal.info_levelset,              "info_levelset"
10164   },
10165   {
10166     TYPE_BOOLEAN,
10167     &setup.internal.info_exit,                  "info_exit"
10168   },
10169 };
10170
10171 static struct TokenInfo debug_setup_tokens[] =
10172 {
10173   {
10174     TYPE_INTEGER,
10175     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10176   },
10177   {
10178     TYPE_INTEGER,
10179     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10180   },
10181   {
10182     TYPE_INTEGER,
10183     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10184   },
10185   {
10186     TYPE_INTEGER,
10187     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10188   },
10189   {
10190     TYPE_INTEGER,
10191     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10192   },
10193   {
10194     TYPE_INTEGER,
10195     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10196   },
10197   {
10198     TYPE_INTEGER,
10199     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10200   },
10201   {
10202     TYPE_INTEGER,
10203     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10204   },
10205   {
10206     TYPE_INTEGER,
10207     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10208   },
10209   {
10210     TYPE_INTEGER,
10211     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10212   },
10213   {
10214     TYPE_KEY_X11,
10215     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10216   },
10217   {
10218     TYPE_KEY_X11,
10219     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10220   },
10221   {
10222     TYPE_KEY_X11,
10223     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10224   },
10225   {
10226     TYPE_KEY_X11,
10227     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10228   },
10229   {
10230     TYPE_KEY_X11,
10231     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10232   },
10233   {
10234     TYPE_KEY_X11,
10235     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10236   },
10237   {
10238     TYPE_KEY_X11,
10239     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10240   },
10241   {
10242     TYPE_KEY_X11,
10243     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10244   },
10245   {
10246     TYPE_KEY_X11,
10247     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10248   },
10249   {
10250     TYPE_KEY_X11,
10251     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10252   },
10253   {
10254     TYPE_BOOLEAN,
10255     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10256   {
10257     TYPE_BOOLEAN,
10258     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10259   },
10260   {
10261     TYPE_BOOLEAN,
10262     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10263   },
10264   {
10265     TYPE_SWITCH3,
10266     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10267   },
10268   {
10269     TYPE_INTEGER,
10270     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10271   },
10272 };
10273
10274 static struct TokenInfo options_setup_tokens[] =
10275 {
10276   {
10277     TYPE_BOOLEAN,
10278     &setup.options.verbose,                     "options.verbose"
10279   },
10280 };
10281
10282 static void setSetupInfoToDefaults(struct SetupInfo *si)
10283 {
10284   int i;
10285
10286   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10287
10288   si->multiple_users = TRUE;
10289
10290   si->sound = TRUE;
10291   si->sound_loops = TRUE;
10292   si->sound_music = TRUE;
10293   si->sound_simple = TRUE;
10294   si->toons = TRUE;
10295   si->scroll_delay = TRUE;
10296   si->forced_scroll_delay = FALSE;
10297   si->scroll_delay_value = STD_SCROLL_DELAY;
10298   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10299   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10300   si->fade_screens = TRUE;
10301   si->autorecord = TRUE;
10302   si->auto_pause_on_start = FALSE;
10303   si->show_titlescreen = TRUE;
10304   si->quick_doors = FALSE;
10305   si->team_mode = FALSE;
10306   si->handicap = TRUE;
10307   si->skip_levels = TRUE;
10308   si->increment_levels = TRUE;
10309   si->auto_play_next_level = TRUE;
10310   si->count_score_after_game = TRUE;
10311   si->show_scores_after_game = TRUE;
10312   si->time_limit = TRUE;
10313   si->fullscreen = FALSE;
10314   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10315   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10316   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10317   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10318   si->ask_on_escape = TRUE;
10319   si->ask_on_escape_editor = TRUE;
10320   si->ask_on_game_over = TRUE;
10321   si->ask_on_quit_game = TRUE;
10322   si->ask_on_quit_program = TRUE;
10323   si->quick_switch = FALSE;
10324   si->input_on_focus = FALSE;
10325   si->prefer_aga_graphics = TRUE;
10326   si->prefer_lowpass_sounds = FALSE;
10327   si->prefer_extra_panel_items = TRUE;
10328   si->game_speed_extended = FALSE;
10329   si->game_frame_delay = GAME_FRAME_DELAY;
10330   si->sp_show_border_elements = FALSE;
10331   si->small_game_graphics = FALSE;
10332   si->show_load_save_buttons = FALSE;
10333   si->show_undo_redo_buttons = FALSE;
10334   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10335
10336   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10337   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10338   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10339
10340   si->override_level_graphics = FALSE;
10341   si->override_level_sounds = FALSE;
10342   si->override_level_music = FALSE;
10343
10344   si->volume_simple = 100;              // percent
10345   si->volume_loops = 100;               // percent
10346   si->volume_music = 100;               // percent
10347
10348   si->network_mode = FALSE;
10349   si->network_player_nr = 0;            // first player
10350   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10351
10352   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10353   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10354   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10355   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10356   si->touch.draw_outlined = TRUE;
10357   si->touch.draw_pressed = TRUE;
10358
10359   for (i = 0; i < 2; i++)
10360   {
10361     char *default_grid_button[6][2] =
10362     {
10363       { "      ", "  ^^  " },
10364       { "      ", "  ^^  " },
10365       { "      ", "<<  >>" },
10366       { "      ", "<<  >>" },
10367       { "111222", "  vv  " },
10368       { "111222", "  vv  " }
10369     };
10370     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10371     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10372     int min_xsize = MIN(6, grid_xsize);
10373     int min_ysize = MIN(6, grid_ysize);
10374     int startx = grid_xsize - min_xsize;
10375     int starty = grid_ysize - min_ysize;
10376     int x, y;
10377
10378     // virtual buttons grid can only be set to defaults if video is initialized
10379     // (this will be repeated if virtual buttons are not loaded from setup file)
10380     if (video.initialized)
10381     {
10382       si->touch.grid_xsize[i] = grid_xsize;
10383       si->touch.grid_ysize[i] = grid_ysize;
10384     }
10385     else
10386     {
10387       si->touch.grid_xsize[i] = -1;
10388       si->touch.grid_ysize[i] = -1;
10389     }
10390
10391     for (x = 0; x < MAX_GRID_XSIZE; x++)
10392       for (y = 0; y < MAX_GRID_YSIZE; y++)
10393         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10394
10395     for (x = 0; x < min_xsize; x++)
10396       for (y = 0; y < min_ysize; y++)
10397         si->touch.grid_button[i][x][starty + y] =
10398           default_grid_button[y][0][x];
10399
10400     for (x = 0; x < min_xsize; x++)
10401       for (y = 0; y < min_ysize; y++)
10402         si->touch.grid_button[i][startx + x][starty + y] =
10403           default_grid_button[y][1][x];
10404   }
10405
10406   si->touch.grid_initialized            = video.initialized;
10407
10408   si->touch.overlay_buttons             = FALSE;
10409
10410   si->editor.el_boulderdash             = TRUE;
10411   si->editor.el_emerald_mine            = TRUE;
10412   si->editor.el_emerald_mine_club       = TRUE;
10413   si->editor.el_more                    = TRUE;
10414   si->editor.el_sokoban                 = TRUE;
10415   si->editor.el_supaplex                = TRUE;
10416   si->editor.el_diamond_caves           = TRUE;
10417   si->editor.el_dx_boulderdash          = TRUE;
10418
10419   si->editor.el_mirror_magic            = TRUE;
10420   si->editor.el_deflektor               = TRUE;
10421
10422   si->editor.el_chars                   = TRUE;
10423   si->editor.el_steel_chars             = TRUE;
10424
10425   si->editor.el_classic                 = TRUE;
10426   si->editor.el_custom                  = TRUE;
10427
10428   si->editor.el_user_defined            = FALSE;
10429   si->editor.el_dynamic                 = TRUE;
10430
10431   si->editor.el_headlines               = TRUE;
10432
10433   si->editor.show_element_token         = FALSE;
10434
10435   si->editor.show_read_only_warning     = TRUE;
10436
10437   si->editor.use_template_for_new_levels = TRUE;
10438
10439   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10440   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10441   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10442   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10443   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10444
10445   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10446   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10447   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10448   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10449   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10450
10451   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10452   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10453   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10454   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10455   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10456   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10457
10458   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10459   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10460   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10461
10462   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10463   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10464   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10465   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10466
10467   for (i = 0; i < MAX_PLAYERS; i++)
10468   {
10469     si->input[i].use_joystick = FALSE;
10470     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10471     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10472     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10473     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10474     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10475     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10476     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10477     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10478     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10479     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10480     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10481     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10482     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10483     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10484     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10485   }
10486
10487   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10488   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10489   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10490   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10491
10492   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10493   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10494   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10495   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10496   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
10497   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10498   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
10499
10500   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10501
10502   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10503   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10504   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10505
10506   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10507   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
10508   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
10509
10510   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10511   si->internal.choose_from_top_leveldir = FALSE;
10512   si->internal.show_scaling_in_title = TRUE;
10513   si->internal.create_user_levelset = TRUE;
10514
10515   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
10516   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10517
10518   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10519   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10520   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10521   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10522   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10523   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10524   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10525   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10526   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10527   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10528
10529   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10530   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10531   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10532   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10533   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10534   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10535   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10536   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10537   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10538   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10539
10540   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10541   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
10542
10543   si->debug.show_frames_per_second = FALSE;
10544
10545   si->debug.xsn_mode = AUTO;
10546   si->debug.xsn_percent = 0;
10547
10548   si->options.verbose = FALSE;
10549
10550 #if defined(PLATFORM_ANDROID)
10551   si->fullscreen = TRUE;
10552   si->touch.overlay_buttons = TRUE;
10553 #endif
10554
10555   setHideSetupEntry(&setup.debug.xsn_mode);
10556 }
10557
10558 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10559 {
10560   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10561 }
10562
10563 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10564 {
10565   si->player_uuid = NULL;       // (will be set later)
10566   si->player_version = 1;       // (will be set later)
10567
10568   si->use_api_server = TRUE;
10569   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10570   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10571   si->ask_for_uploading_tapes = TRUE;
10572   si->ask_for_remaining_tapes = FALSE;
10573   si->provide_uploading_tapes = TRUE;
10574   si->ask_for_using_api_server = TRUE;
10575   si->has_remaining_tapes = FALSE;
10576 }
10577
10578 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10579 {
10580   si->editor_cascade.el_bd              = TRUE;
10581   si->editor_cascade.el_em              = TRUE;
10582   si->editor_cascade.el_emc             = TRUE;
10583   si->editor_cascade.el_rnd             = TRUE;
10584   si->editor_cascade.el_sb              = TRUE;
10585   si->editor_cascade.el_sp              = TRUE;
10586   si->editor_cascade.el_dc              = TRUE;
10587   si->editor_cascade.el_dx              = TRUE;
10588
10589   si->editor_cascade.el_mm              = TRUE;
10590   si->editor_cascade.el_df              = TRUE;
10591
10592   si->editor_cascade.el_chars           = FALSE;
10593   si->editor_cascade.el_steel_chars     = FALSE;
10594   si->editor_cascade.el_ce              = FALSE;
10595   si->editor_cascade.el_ge              = FALSE;
10596   si->editor_cascade.el_es              = FALSE;
10597   si->editor_cascade.el_ref             = FALSE;
10598   si->editor_cascade.el_user            = FALSE;
10599   si->editor_cascade.el_dynamic         = FALSE;
10600 }
10601
10602 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
10603
10604 static char *getHideSetupToken(void *setup_value)
10605 {
10606   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10607
10608   if (setup_value != NULL)
10609     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10610
10611   return hide_setup_token;
10612 }
10613
10614 void setHideSetupEntry(void *setup_value)
10615 {
10616   char *hide_setup_token = getHideSetupToken(setup_value);
10617
10618   if (hide_setup_hash == NULL)
10619     hide_setup_hash = newSetupFileHash();
10620
10621   if (setup_value != NULL)
10622     setHashEntry(hide_setup_hash, hide_setup_token, "");
10623 }
10624
10625 void removeHideSetupEntry(void *setup_value)
10626 {
10627   char *hide_setup_token = getHideSetupToken(setup_value);
10628
10629   if (setup_value != NULL)
10630     removeHashEntry(hide_setup_hash, hide_setup_token);
10631 }
10632
10633 boolean hideSetupEntry(void *setup_value)
10634 {
10635   char *hide_setup_token = getHideSetupToken(setup_value);
10636
10637   return (setup_value != NULL &&
10638           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10639 }
10640
10641 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10642                                       struct TokenInfo *token_info,
10643                                       int token_nr, char *token_text)
10644 {
10645   char *token_hide_text = getStringCat2(token_text, ".hide");
10646   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10647
10648   // set the value of this setup option in the setup option structure
10649   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10650
10651   // check if this setup option should be hidden in the setup menu
10652   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10653     setHideSetupEntry(token_info[token_nr].value);
10654
10655   free(token_hide_text);
10656 }
10657
10658 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10659                                       struct TokenInfo *token_info,
10660                                       int token_nr)
10661 {
10662   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10663                             token_info[token_nr].text);
10664 }
10665
10666 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10667 {
10668   int i, pnr;
10669
10670   if (!setup_file_hash)
10671     return;
10672
10673   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10674     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10675
10676   setup.touch.grid_initialized = TRUE;
10677   for (i = 0; i < 2; i++)
10678   {
10679     int grid_xsize = setup.touch.grid_xsize[i];
10680     int grid_ysize = setup.touch.grid_ysize[i];
10681     int x, y;
10682
10683     // if virtual buttons are not loaded from setup file, repeat initializing
10684     // virtual buttons grid with default values later when video is initialized
10685     if (grid_xsize == -1 ||
10686         grid_ysize == -1)
10687     {
10688       setup.touch.grid_initialized = FALSE;
10689
10690       continue;
10691     }
10692
10693     for (y = 0; y < grid_ysize; y++)
10694     {
10695       char token_string[MAX_LINE_LEN];
10696
10697       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10698
10699       char *value_string = getHashEntry(setup_file_hash, token_string);
10700
10701       if (value_string == NULL)
10702         continue;
10703
10704       for (x = 0; x < grid_xsize; x++)
10705       {
10706         char c = value_string[x];
10707
10708         setup.touch.grid_button[i][x][y] =
10709           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10710       }
10711     }
10712   }
10713
10714   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10715     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10716
10717   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10718     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10719
10720   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10721   {
10722     char prefix[30];
10723
10724     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10725
10726     setup_input = setup.input[pnr];
10727     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10728     {
10729       char full_token[100];
10730
10731       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10732       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10733                                 full_token);
10734     }
10735     setup.input[pnr] = setup_input;
10736   }
10737
10738   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10739     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10740
10741   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10742     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10743
10744   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10745     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10746
10747   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10748     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10749
10750   setHideRelatedSetupEntries();
10751 }
10752
10753 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10754 {
10755   int i;
10756
10757   if (!setup_file_hash)
10758     return;
10759
10760   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10761     setSetupInfo(auto_setup_tokens, i,
10762                  getHashEntry(setup_file_hash,
10763                               auto_setup_tokens[i].text));
10764 }
10765
10766 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10767 {
10768   int i;
10769
10770   if (!setup_file_hash)
10771     return;
10772
10773   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10774     setSetupInfo(server_setup_tokens, i,
10775                  getHashEntry(setup_file_hash,
10776                               server_setup_tokens[i].text));
10777 }
10778
10779 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10780 {
10781   int i;
10782
10783   if (!setup_file_hash)
10784     return;
10785
10786   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10787     setSetupInfo(editor_cascade_setup_tokens, i,
10788                  getHashEntry(setup_file_hash,
10789                               editor_cascade_setup_tokens[i].text));
10790 }
10791
10792 void LoadUserNames(void)
10793 {
10794   int last_user_nr = user.nr;
10795   int i;
10796
10797   if (global.user_names != NULL)
10798   {
10799     for (i = 0; i < MAX_PLAYER_NAMES; i++)
10800       checked_free(global.user_names[i]);
10801
10802     checked_free(global.user_names);
10803   }
10804
10805   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10806
10807   for (i = 0; i < MAX_PLAYER_NAMES; i++)
10808   {
10809     user.nr = i;
10810
10811     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10812
10813     if (setup_file_hash)
10814     {
10815       char *player_name = getHashEntry(setup_file_hash, "player_name");
10816
10817       global.user_names[i] = getFixedUserName(player_name);
10818
10819       freeSetupFileHash(setup_file_hash);
10820     }
10821
10822     if (global.user_names[i] == NULL)
10823       global.user_names[i] = getStringCopy(getDefaultUserName(i));
10824   }
10825
10826   user.nr = last_user_nr;
10827 }
10828
10829 void LoadSetupFromFilename(char *filename)
10830 {
10831   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10832
10833   if (setup_file_hash)
10834   {
10835     decodeSetupFileHash_Default(setup_file_hash);
10836
10837     freeSetupFileHash(setup_file_hash);
10838   }
10839   else
10840   {
10841     Debug("setup", "using default setup values");
10842   }
10843 }
10844
10845 static void LoadSetup_SpecialPostProcessing(void)
10846 {
10847   char *player_name_new;
10848
10849   // needed to work around problems with fixed length strings
10850   player_name_new = getFixedUserName(setup.player_name);
10851   free(setup.player_name);
10852   setup.player_name = player_name_new;
10853
10854   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10855   if (setup.scroll_delay == FALSE)
10856   {
10857     setup.scroll_delay_value = MIN_SCROLL_DELAY;
10858     setup.scroll_delay = TRUE;                  // now always "on"
10859   }
10860
10861   // make sure that scroll delay value stays inside valid range
10862   setup.scroll_delay_value =
10863     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10864 }
10865
10866 void LoadSetup_Default(void)
10867 {
10868   char *filename;
10869
10870   // always start with reliable default values
10871   setSetupInfoToDefaults(&setup);
10872
10873   // try to load setup values from default setup file
10874   filename = getDefaultSetupFilename();
10875
10876   if (fileExists(filename))
10877     LoadSetupFromFilename(filename);
10878
10879   // try to load setup values from platform setup file
10880   filename = getPlatformSetupFilename();
10881
10882   if (fileExists(filename))
10883     LoadSetupFromFilename(filename);
10884
10885   // try to load setup values from user setup file
10886   filename = getSetupFilename();
10887
10888   LoadSetupFromFilename(filename);
10889
10890   LoadSetup_SpecialPostProcessing();
10891 }
10892
10893 void LoadSetup_AutoSetup(void)
10894 {
10895   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10896   SetupFileHash *setup_file_hash = NULL;
10897
10898   // always start with reliable default values
10899   setSetupInfoToDefaults_AutoSetup(&setup);
10900
10901   setup_file_hash = loadSetupFileHash(filename);
10902
10903   if (setup_file_hash)
10904   {
10905     decodeSetupFileHash_AutoSetup(setup_file_hash);
10906
10907     freeSetupFileHash(setup_file_hash);
10908   }
10909
10910   free(filename);
10911 }
10912
10913 void LoadSetup_ServerSetup(void)
10914 {
10915   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10916   SetupFileHash *setup_file_hash = NULL;
10917
10918   // always start with reliable default values
10919   setSetupInfoToDefaults_ServerSetup(&setup);
10920
10921   setup_file_hash = loadSetupFileHash(filename);
10922
10923   if (setup_file_hash)
10924   {
10925     decodeSetupFileHash_ServerSetup(setup_file_hash);
10926
10927     freeSetupFileHash(setup_file_hash);
10928   }
10929
10930   free(filename);
10931
10932   if (setup.player_uuid == NULL)
10933   {
10934     // player UUID does not yet exist in setup file
10935     setup.player_uuid = getStringCopy(getUUID());
10936     setup.player_version = 2;
10937
10938     SaveSetup_ServerSetup();
10939   }
10940 }
10941
10942 void LoadSetup_EditorCascade(void)
10943 {
10944   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10945   SetupFileHash *setup_file_hash = NULL;
10946
10947   // always start with reliable default values
10948   setSetupInfoToDefaults_EditorCascade(&setup);
10949
10950   setup_file_hash = loadSetupFileHash(filename);
10951
10952   if (setup_file_hash)
10953   {
10954     decodeSetupFileHash_EditorCascade(setup_file_hash);
10955
10956     freeSetupFileHash(setup_file_hash);
10957   }
10958
10959   free(filename);
10960 }
10961
10962 void LoadSetup(void)
10963 {
10964   LoadSetup_Default();
10965   LoadSetup_AutoSetup();
10966   LoadSetup_ServerSetup();
10967   LoadSetup_EditorCascade();
10968 }
10969
10970 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10971                                            char *mapping_line)
10972 {
10973   char mapping_guid[MAX_LINE_LEN];
10974   char *mapping_start, *mapping_end;
10975
10976   // get GUID from game controller mapping line: copy complete line
10977   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10978   mapping_guid[MAX_LINE_LEN - 1] = '\0';
10979
10980   // get GUID from game controller mapping line: cut after GUID part
10981   mapping_start = strchr(mapping_guid, ',');
10982   if (mapping_start != NULL)
10983     *mapping_start = '\0';
10984
10985   // cut newline from game controller mapping line
10986   mapping_end = strchr(mapping_line, '\n');
10987   if (mapping_end != NULL)
10988     *mapping_end = '\0';
10989
10990   // add mapping entry to game controller mappings hash
10991   setHashEntry(mappings_hash, mapping_guid, mapping_line);
10992 }
10993
10994 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
10995                                                  char *filename)
10996 {
10997   FILE *file;
10998
10999   if (!(file = fopen(filename, MODE_READ)))
11000   {
11001     Warn("cannot read game controller mappings file '%s'", filename);
11002
11003     return;
11004   }
11005
11006   while (!feof(file))
11007   {
11008     char line[MAX_LINE_LEN];
11009
11010     if (!fgets(line, MAX_LINE_LEN, file))
11011       break;
11012
11013     addGameControllerMappingToHash(mappings_hash, line);
11014   }
11015
11016   fclose(file);
11017 }
11018
11019 void SaveSetup_Default(void)
11020 {
11021   char *filename = getSetupFilename();
11022   FILE *file;
11023   int i, pnr;
11024
11025   InitUserDataDirectory();
11026
11027   if (!(file = fopen(filename, MODE_WRITE)))
11028   {
11029     Warn("cannot write setup file '%s'", filename);
11030
11031     return;
11032   }
11033
11034   fprintFileHeader(file, SETUP_FILENAME);
11035
11036   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11037   {
11038     // just to make things nicer :)
11039     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11040         global_setup_tokens[i].value == &setup.sound                    ||
11041         global_setup_tokens[i].value == &setup.graphics_set             ||
11042         global_setup_tokens[i].value == &setup.volume_simple            ||
11043         global_setup_tokens[i].value == &setup.network_mode             ||
11044         global_setup_tokens[i].value == &setup.touch.control_type       ||
11045         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11046         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11047       fprintf(file, "\n");
11048
11049     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11050   }
11051
11052   for (i = 0; i < 2; i++)
11053   {
11054     int grid_xsize = setup.touch.grid_xsize[i];
11055     int grid_ysize = setup.touch.grid_ysize[i];
11056     int x, y;
11057
11058     fprintf(file, "\n");
11059
11060     for (y = 0; y < grid_ysize; y++)
11061     {
11062       char token_string[MAX_LINE_LEN];
11063       char value_string[MAX_LINE_LEN];
11064
11065       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11066
11067       for (x = 0; x < grid_xsize; x++)
11068       {
11069         char c = setup.touch.grid_button[i][x][y];
11070
11071         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11072       }
11073
11074       value_string[grid_xsize] = '\0';
11075
11076       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11077     }
11078   }
11079
11080   fprintf(file, "\n");
11081   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11082     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11083
11084   fprintf(file, "\n");
11085   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11086     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11087
11088   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11089   {
11090     char prefix[30];
11091
11092     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11093     fprintf(file, "\n");
11094
11095     setup_input = setup.input[pnr];
11096     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11097       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11098   }
11099
11100   fprintf(file, "\n");
11101   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11102     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11103
11104   // (internal setup values not saved to user setup file)
11105
11106   fprintf(file, "\n");
11107   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11108     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11109         setup.debug.xsn_mode != AUTO)
11110       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11111
11112   fprintf(file, "\n");
11113   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11114     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11115
11116   fclose(file);
11117
11118   SetFilePermissions(filename, PERMS_PRIVATE);
11119 }
11120
11121 void SaveSetup_AutoSetup(void)
11122 {
11123   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11124   FILE *file;
11125   int i;
11126
11127   InitUserDataDirectory();
11128
11129   if (!(file = fopen(filename, MODE_WRITE)))
11130   {
11131     Warn("cannot write auto setup file '%s'", filename);
11132
11133     free(filename);
11134
11135     return;
11136   }
11137
11138   fprintFileHeader(file, AUTOSETUP_FILENAME);
11139
11140   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11141     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11142
11143   fclose(file);
11144
11145   SetFilePermissions(filename, PERMS_PRIVATE);
11146
11147   free(filename);
11148 }
11149
11150 void SaveSetup_ServerSetup(void)
11151 {
11152   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11153   FILE *file;
11154   int i;
11155
11156   InitUserDataDirectory();
11157
11158   if (!(file = fopen(filename, MODE_WRITE)))
11159   {
11160     Warn("cannot write server setup file '%s'", filename);
11161
11162     free(filename);
11163
11164     return;
11165   }
11166
11167   fprintFileHeader(file, SERVERSETUP_FILENAME);
11168
11169   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11170   {
11171     // just to make things nicer :)
11172     if (server_setup_tokens[i].value == &setup.use_api_server)
11173       fprintf(file, "\n");
11174
11175     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11176   }
11177
11178   fclose(file);
11179
11180   SetFilePermissions(filename, PERMS_PRIVATE);
11181
11182   free(filename);
11183 }
11184
11185 void SaveSetup_EditorCascade(void)
11186 {
11187   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11188   FILE *file;
11189   int i;
11190
11191   InitUserDataDirectory();
11192
11193   if (!(file = fopen(filename, MODE_WRITE)))
11194   {
11195     Warn("cannot write editor cascade state file '%s'", filename);
11196
11197     free(filename);
11198
11199     return;
11200   }
11201
11202   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11203
11204   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11205     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11206
11207   fclose(file);
11208
11209   SetFilePermissions(filename, PERMS_PRIVATE);
11210
11211   free(filename);
11212 }
11213
11214 void SaveSetup(void)
11215 {
11216   SaveSetup_Default();
11217   SaveSetup_AutoSetup();
11218   SaveSetup_ServerSetup();
11219   SaveSetup_EditorCascade();
11220 }
11221
11222 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11223                                                   char *filename)
11224 {
11225   FILE *file;
11226
11227   if (!(file = fopen(filename, MODE_WRITE)))
11228   {
11229     Warn("cannot write game controller mappings file '%s'", filename);
11230
11231     return;
11232   }
11233
11234   BEGIN_HASH_ITERATION(mappings_hash, itr)
11235   {
11236     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11237   }
11238   END_HASH_ITERATION(mappings_hash, itr)
11239
11240   fclose(file);
11241 }
11242
11243 void SaveSetup_AddGameControllerMapping(char *mapping)
11244 {
11245   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11246   SetupFileHash *mappings_hash = newSetupFileHash();
11247
11248   InitUserDataDirectory();
11249
11250   // load existing personal game controller mappings
11251   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11252
11253   // add new mapping to personal game controller mappings
11254   addGameControllerMappingToHash(mappings_hash, mapping);
11255
11256   // save updated personal game controller mappings
11257   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11258
11259   freeSetupFileHash(mappings_hash);
11260   free(filename);
11261 }
11262
11263 void LoadCustomElementDescriptions(void)
11264 {
11265   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11266   SetupFileHash *setup_file_hash;
11267   int i;
11268
11269   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11270   {
11271     if (element_info[i].custom_description != NULL)
11272     {
11273       free(element_info[i].custom_description);
11274       element_info[i].custom_description = NULL;
11275     }
11276   }
11277
11278   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11279     return;
11280
11281   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11282   {
11283     char *token = getStringCat2(element_info[i].token_name, ".name");
11284     char *value = getHashEntry(setup_file_hash, token);
11285
11286     if (value != NULL)
11287       element_info[i].custom_description = getStringCopy(value);
11288
11289     free(token);
11290   }
11291
11292   freeSetupFileHash(setup_file_hash);
11293 }
11294
11295 static int getElementFromToken(char *token)
11296 {
11297   char *value = getHashEntry(element_token_hash, token);
11298
11299   if (value != NULL)
11300     return atoi(value);
11301
11302   Warn("unknown element token '%s'", token);
11303
11304   return EL_UNDEFINED;
11305 }
11306
11307 void FreeGlobalAnimEventInfo(void)
11308 {
11309   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11310
11311   if (gaei->event_list == NULL)
11312     return;
11313
11314   int i;
11315
11316   for (i = 0; i < gaei->num_event_lists; i++)
11317   {
11318     checked_free(gaei->event_list[i]->event_value);
11319     checked_free(gaei->event_list[i]);
11320   }
11321
11322   checked_free(gaei->event_list);
11323
11324   gaei->event_list = NULL;
11325   gaei->num_event_lists = 0;
11326 }
11327
11328 static int AddGlobalAnimEventList(void)
11329 {
11330   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11331   int list_pos = gaei->num_event_lists++;
11332
11333   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11334                                      sizeof(struct GlobalAnimEventListInfo *));
11335
11336   gaei->event_list[list_pos] =
11337     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11338
11339   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11340
11341   gaeli->event_value = NULL;
11342   gaeli->num_event_values = 0;
11343
11344   return list_pos;
11345 }
11346
11347 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11348 {
11349   // do not add empty global animation events
11350   if (event_value == ANIM_EVENT_NONE)
11351     return list_pos;
11352
11353   // if list position is undefined, create new list
11354   if (list_pos == ANIM_EVENT_UNDEFINED)
11355     list_pos = AddGlobalAnimEventList();
11356
11357   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11358   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11359   int value_pos = gaeli->num_event_values++;
11360
11361   gaeli->event_value = checked_realloc(gaeli->event_value,
11362                                        gaeli->num_event_values * sizeof(int *));
11363
11364   gaeli->event_value[value_pos] = event_value;
11365
11366   return list_pos;
11367 }
11368
11369 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11370 {
11371   if (list_pos == ANIM_EVENT_UNDEFINED)
11372     return ANIM_EVENT_NONE;
11373
11374   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11375   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11376
11377   return gaeli->event_value[value_pos];
11378 }
11379
11380 int GetGlobalAnimEventValueCount(int list_pos)
11381 {
11382   if (list_pos == ANIM_EVENT_UNDEFINED)
11383     return 0;
11384
11385   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11386   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11387
11388   return gaeli->num_event_values;
11389 }
11390
11391 // This function checks if a string <s> of the format "string1, string2, ..."
11392 // exactly contains a string <s_contained>.
11393
11394 static boolean string_has_parameter(char *s, char *s_contained)
11395 {
11396   char *substring;
11397
11398   if (s == NULL || s_contained == NULL)
11399     return FALSE;
11400
11401   if (strlen(s_contained) > strlen(s))
11402     return FALSE;
11403
11404   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11405   {
11406     char next_char = s[strlen(s_contained)];
11407
11408     // check if next character is delimiter or whitespace
11409     return (next_char == ',' || next_char == '\0' ||
11410             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11411   }
11412
11413   // check if string contains another parameter string after a comma
11414   substring = strchr(s, ',');
11415   if (substring == NULL)        // string does not contain a comma
11416     return FALSE;
11417
11418   // advance string pointer to next character after the comma
11419   substring++;
11420
11421   // skip potential whitespaces after the comma
11422   while (*substring == ' ' || *substring == '\t')
11423     substring++;
11424
11425   return string_has_parameter(substring, s_contained);
11426 }
11427
11428 static int get_anim_parameter_value(char *s)
11429 {
11430   int event_value[] =
11431   {
11432     ANIM_EVENT_CLICK,
11433     ANIM_EVENT_INIT,
11434     ANIM_EVENT_START,
11435     ANIM_EVENT_END,
11436     ANIM_EVENT_POST
11437   };
11438   char *pattern_1[] =
11439   {
11440     "click:anim_",
11441     "init:anim_",
11442     "start:anim_",
11443     "end:anim_",
11444     "post:anim_"
11445   };
11446   char *pattern_2 = ".part_";
11447   char *matching_char = NULL;
11448   char *s_ptr = s;
11449   int pattern_1_len = 0;
11450   int result = ANIM_EVENT_NONE;
11451   int i;
11452
11453   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11454   {
11455     matching_char = strstr(s_ptr, pattern_1[i]);
11456     pattern_1_len = strlen(pattern_1[i]);
11457     result = event_value[i];
11458
11459     if (matching_char != NULL)
11460       break;
11461   }
11462
11463   if (matching_char == NULL)
11464     return ANIM_EVENT_NONE;
11465
11466   s_ptr = matching_char + pattern_1_len;
11467
11468   // check for main animation number ("anim_X" or "anim_XX")
11469   if (*s_ptr >= '0' && *s_ptr <= '9')
11470   {
11471     int gic_anim_nr = (*s_ptr++ - '0');
11472
11473     if (*s_ptr >= '0' && *s_ptr <= '9')
11474       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11475
11476     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11477       return ANIM_EVENT_NONE;
11478
11479     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11480   }
11481   else
11482   {
11483     // invalid main animation number specified
11484
11485     return ANIM_EVENT_NONE;
11486   }
11487
11488   // check for animation part number ("part_X" or "part_XX") (optional)
11489   if (strPrefix(s_ptr, pattern_2))
11490   {
11491     s_ptr += strlen(pattern_2);
11492
11493     if (*s_ptr >= '0' && *s_ptr <= '9')
11494     {
11495       int gic_part_nr = (*s_ptr++ - '0');
11496
11497       if (*s_ptr >= '0' && *s_ptr <= '9')
11498         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11499
11500       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11501         return ANIM_EVENT_NONE;
11502
11503       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11504     }
11505     else
11506     {
11507       // invalid animation part number specified
11508
11509       return ANIM_EVENT_NONE;
11510     }
11511   }
11512
11513   // discard result if next character is neither delimiter nor whitespace
11514   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11515         *s_ptr == ' ' || *s_ptr == '\t'))
11516     return ANIM_EVENT_NONE;
11517
11518   return result;
11519 }
11520
11521 static int get_anim_parameter_values(char *s)
11522 {
11523   int list_pos = ANIM_EVENT_UNDEFINED;
11524   int event_value = ANIM_EVENT_DEFAULT;
11525
11526   if (string_has_parameter(s, "any"))
11527     event_value |= ANIM_EVENT_ANY;
11528
11529   if (string_has_parameter(s, "click:self") ||
11530       string_has_parameter(s, "click") ||
11531       string_has_parameter(s, "self"))
11532     event_value |= ANIM_EVENT_SELF;
11533
11534   if (string_has_parameter(s, "unclick:any"))
11535     event_value |= ANIM_EVENT_UNCLICK_ANY;
11536
11537   // if animation event found, add it to global animation event list
11538   if (event_value != ANIM_EVENT_NONE)
11539     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11540
11541   while (s != NULL)
11542   {
11543     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11544     event_value = get_anim_parameter_value(s);
11545
11546     // if animation event found, add it to global animation event list
11547     if (event_value != ANIM_EVENT_NONE)
11548       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11549
11550     // continue with next part of the string, starting with next comma
11551     s = strchr(s + 1, ',');
11552   }
11553
11554   return list_pos;
11555 }
11556
11557 static int get_anim_action_parameter_value(char *token)
11558 {
11559   // check most common default case first to massively speed things up
11560   if (strEqual(token, ARG_UNDEFINED))
11561     return ANIM_EVENT_ACTION_NONE;
11562
11563   int result = getImageIDFromToken(token);
11564
11565   if (result == -1)
11566   {
11567     char *gfx_token = getStringCat2("gfx.", token);
11568
11569     result = getImageIDFromToken(gfx_token);
11570
11571     checked_free(gfx_token);
11572   }
11573
11574   if (result == -1)
11575   {
11576     Key key = getKeyFromX11KeyName(token);
11577
11578     if (key != KSYM_UNDEFINED)
11579       result = -(int)key;
11580   }
11581
11582   if (result == -1)
11583   {
11584     if (isURL(token))
11585     {
11586       result = get_hash_from_key(token);        // unsigned int => int
11587       result = ABS(result);                     // may be negative now
11588       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11589
11590       setHashEntry(anim_url_hash, int2str(result, 0), token);
11591     }
11592   }
11593
11594   if (result == -1)
11595     result = ANIM_EVENT_ACTION_NONE;
11596
11597   return result;
11598 }
11599
11600 int get_parameter_value(char *value_raw, char *suffix, int type)
11601 {
11602   char *value = getStringToLower(value_raw);
11603   int result = 0;       // probably a save default value
11604
11605   if (strEqual(suffix, ".direction"))
11606   {
11607     result = (strEqual(value, "left")  ? MV_LEFT :
11608               strEqual(value, "right") ? MV_RIGHT :
11609               strEqual(value, "up")    ? MV_UP :
11610               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
11611   }
11612   else if (strEqual(suffix, ".position"))
11613   {
11614     result = (strEqual(value, "left")   ? POS_LEFT :
11615               strEqual(value, "right")  ? POS_RIGHT :
11616               strEqual(value, "top")    ? POS_TOP :
11617               strEqual(value, "upper")  ? POS_UPPER :
11618               strEqual(value, "middle") ? POS_MIDDLE :
11619               strEqual(value, "lower")  ? POS_LOWER :
11620               strEqual(value, "bottom") ? POS_BOTTOM :
11621               strEqual(value, "any")    ? POS_ANY :
11622               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
11623   }
11624   else if (strEqual(suffix, ".align"))
11625   {
11626     result = (strEqual(value, "left")   ? ALIGN_LEFT :
11627               strEqual(value, "right")  ? ALIGN_RIGHT :
11628               strEqual(value, "center") ? ALIGN_CENTER :
11629               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11630   }
11631   else if (strEqual(suffix, ".valign"))
11632   {
11633     result = (strEqual(value, "top")    ? VALIGN_TOP :
11634               strEqual(value, "bottom") ? VALIGN_BOTTOM :
11635               strEqual(value, "middle") ? VALIGN_MIDDLE :
11636               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11637   }
11638   else if (strEqual(suffix, ".anim_mode"))
11639   {
11640     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
11641               string_has_parameter(value, "loop")       ? ANIM_LOOP :
11642               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
11643               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
11644               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
11645               string_has_parameter(value, "random")     ? ANIM_RANDOM :
11646               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11647               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
11648               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
11649               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
11650               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11651               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
11652               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
11653               string_has_parameter(value, "all")        ? ANIM_ALL :
11654               string_has_parameter(value, "tiled")      ? ANIM_TILED :
11655               ANIM_DEFAULT);
11656
11657     if (string_has_parameter(value, "once"))
11658       result |= ANIM_ONCE;
11659
11660     if (string_has_parameter(value, "reverse"))
11661       result |= ANIM_REVERSE;
11662
11663     if (string_has_parameter(value, "opaque_player"))
11664       result |= ANIM_OPAQUE_PLAYER;
11665
11666     if (string_has_parameter(value, "static_panel"))
11667       result |= ANIM_STATIC_PANEL;
11668   }
11669   else if (strEqual(suffix, ".init_event") ||
11670            strEqual(suffix, ".anim_event"))
11671   {
11672     result = get_anim_parameter_values(value);
11673   }
11674   else if (strEqual(suffix, ".init_delay_action") ||
11675            strEqual(suffix, ".anim_delay_action") ||
11676            strEqual(suffix, ".post_delay_action") ||
11677            strEqual(suffix, ".init_event_action") ||
11678            strEqual(suffix, ".anim_event_action"))
11679   {
11680     result = get_anim_action_parameter_value(value_raw);
11681   }
11682   else if (strEqual(suffix, ".class"))
11683   {
11684     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11685               get_hash_from_key(value));
11686   }
11687   else if (strEqual(suffix, ".style"))
11688   {
11689     result = STYLE_DEFAULT;
11690
11691     if (string_has_parameter(value, "accurate_borders"))
11692       result |= STYLE_ACCURATE_BORDERS;
11693
11694     if (string_has_parameter(value, "inner_corners"))
11695       result |= STYLE_INNER_CORNERS;
11696
11697     if (string_has_parameter(value, "reverse"))
11698       result |= STYLE_REVERSE;
11699
11700     if (string_has_parameter(value, "leftmost_position"))
11701       result |= STYLE_LEFTMOST_POSITION;
11702
11703     if (string_has_parameter(value, "block_clicks"))
11704       result |= STYLE_BLOCK;
11705
11706     if (string_has_parameter(value, "passthrough_clicks"))
11707       result |= STYLE_PASSTHROUGH;
11708
11709     if (string_has_parameter(value, "multiple_actions"))
11710       result |= STYLE_MULTIPLE_ACTIONS;
11711   }
11712   else if (strEqual(suffix, ".fade_mode"))
11713   {
11714     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
11715               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
11716               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
11717               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
11718               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
11719               FADE_MODE_DEFAULT);
11720   }
11721   else if (strEqual(suffix, ".auto_delay_unit"))
11722   {
11723     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
11724               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11725               AUTO_DELAY_UNIT_DEFAULT);
11726   }
11727   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
11728   {
11729     result = gfx.get_font_from_token_function(value);
11730   }
11731   else          // generic parameter of type integer or boolean
11732   {
11733     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11734               type == TYPE_INTEGER ? get_integer_from_string(value) :
11735               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11736               ARG_UNDEFINED_VALUE);
11737   }
11738
11739   free(value);
11740
11741   return result;
11742 }
11743
11744 static int get_token_parameter_value(char *token, char *value_raw)
11745 {
11746   char *suffix;
11747
11748   if (token == NULL || value_raw == NULL)
11749     return ARG_UNDEFINED_VALUE;
11750
11751   suffix = strrchr(token, '.');
11752   if (suffix == NULL)
11753     suffix = token;
11754
11755   if (strEqual(suffix, ".element"))
11756     return getElementFromToken(value_raw);
11757
11758   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11759   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11760 }
11761
11762 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11763                                      boolean ignore_defaults)
11764 {
11765   int i;
11766
11767   for (i = 0; image_config_vars[i].token != NULL; i++)
11768   {
11769     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11770
11771     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11772     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11773       continue;
11774
11775     if (value != NULL)
11776       *image_config_vars[i].value =
11777         get_token_parameter_value(image_config_vars[i].token, value);
11778   }
11779 }
11780
11781 void InitMenuDesignSettings_Static(void)
11782 {
11783   // always start with reliable default values from static default config
11784   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11785 }
11786
11787 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11788 {
11789   int i;
11790
11791   // the following initializes hierarchical values from static configuration
11792
11793   // special case: initialize "ARG_DEFAULT" values in static default config
11794   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11795   titlescreen_initial_first_default.fade_mode  =
11796     title_initial_first_default.fade_mode;
11797   titlescreen_initial_first_default.fade_delay =
11798     title_initial_first_default.fade_delay;
11799   titlescreen_initial_first_default.post_delay =
11800     title_initial_first_default.post_delay;
11801   titlescreen_initial_first_default.auto_delay =
11802     title_initial_first_default.auto_delay;
11803   titlescreen_initial_first_default.auto_delay_unit =
11804     title_initial_first_default.auto_delay_unit;
11805   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
11806   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11807   titlescreen_first_default.post_delay = title_first_default.post_delay;
11808   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11809   titlescreen_first_default.auto_delay_unit =
11810     title_first_default.auto_delay_unit;
11811   titlemessage_initial_first_default.fade_mode  =
11812     title_initial_first_default.fade_mode;
11813   titlemessage_initial_first_default.fade_delay =
11814     title_initial_first_default.fade_delay;
11815   titlemessage_initial_first_default.post_delay =
11816     title_initial_first_default.post_delay;
11817   titlemessage_initial_first_default.auto_delay =
11818     title_initial_first_default.auto_delay;
11819   titlemessage_initial_first_default.auto_delay_unit =
11820     title_initial_first_default.auto_delay_unit;
11821   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
11822   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11823   titlemessage_first_default.post_delay = title_first_default.post_delay;
11824   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11825   titlemessage_first_default.auto_delay_unit =
11826     title_first_default.auto_delay_unit;
11827
11828   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
11829   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11830   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11831   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11832   titlescreen_initial_default.auto_delay_unit =
11833     title_initial_default.auto_delay_unit;
11834   titlescreen_default.fade_mode  = title_default.fade_mode;
11835   titlescreen_default.fade_delay = title_default.fade_delay;
11836   titlescreen_default.post_delay = title_default.post_delay;
11837   titlescreen_default.auto_delay = title_default.auto_delay;
11838   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11839   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
11840   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11841   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11842   titlemessage_initial_default.auto_delay_unit =
11843     title_initial_default.auto_delay_unit;
11844   titlemessage_default.fade_mode  = title_default.fade_mode;
11845   titlemessage_default.fade_delay = title_default.fade_delay;
11846   titlemessage_default.post_delay = title_default.post_delay;
11847   titlemessage_default.auto_delay = title_default.auto_delay;
11848   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11849
11850   // special case: initialize "ARG_DEFAULT" values in static default config
11851   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11852   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11853   {
11854     titlescreen_initial_first[i] = titlescreen_initial_first_default;
11855     titlescreen_first[i] = titlescreen_first_default;
11856     titlemessage_initial_first[i] = titlemessage_initial_first_default;
11857     titlemessage_first[i] = titlemessage_first_default;
11858
11859     titlescreen_initial[i] = titlescreen_initial_default;
11860     titlescreen[i] = titlescreen_default;
11861     titlemessage_initial[i] = titlemessage_initial_default;
11862     titlemessage[i] = titlemessage_default;
11863   }
11864
11865   // special case: initialize "ARG_DEFAULT" values in static default config
11866   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11867   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11868   {
11869     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
11870       continue;
11871
11872     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11873     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11874     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11875   }
11876
11877   // special case: initialize "ARG_DEFAULT" values in static default config
11878   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11879   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11880   {
11881     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11882     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11883     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11884
11885     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
11886       continue;
11887
11888     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11889   }
11890 }
11891
11892 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11893 {
11894   static struct
11895   {
11896     struct XY *dst, *src;
11897   }
11898   game_buttons_xy[] =
11899   {
11900     { &game.button.save,        &game.button.stop       },
11901     { &game.button.pause2,      &game.button.pause      },
11902     { &game.button.load,        &game.button.play       },
11903     { &game.button.undo,        &game.button.stop       },
11904     { &game.button.redo,        &game.button.play       },
11905
11906     { NULL,                     NULL                    }
11907   };
11908   int i, j;
11909
11910   // special case: initialize later added SETUP list size from LEVELS value
11911   if (menu.list_size[GAME_MODE_SETUP] == -1)
11912     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11913
11914   // set default position for snapshot buttons to stop/pause/play buttons
11915   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11916     if ((*game_buttons_xy[i].dst).x == -1 &&
11917         (*game_buttons_xy[i].dst).y == -1)
11918       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11919
11920   // --------------------------------------------------------------------------
11921   // dynamic viewports (including playfield margins, borders and alignments)
11922   // --------------------------------------------------------------------------
11923
11924   // dynamic viewports currently only supported for landscape mode
11925   int display_width  = MAX(video.display_width, video.display_height);
11926   int display_height = MIN(video.display_width, video.display_height);
11927
11928   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11929   {
11930     struct RectWithBorder *vp_window    = &viewport.window[i];
11931     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11932     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
11933     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
11934     boolean dynamic_window_width     = (vp_window->min_width     != -1);
11935     boolean dynamic_window_height    = (vp_window->min_height    != -1);
11936     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
11937     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11938
11939     // adjust window size if min/max width/height is specified
11940
11941     if (vp_window->min_width != -1)
11942     {
11943       int window_width = display_width;
11944
11945       // when using static window height, use aspect ratio of display
11946       if (vp_window->min_height == -1)
11947         window_width = vp_window->height * display_width / display_height;
11948
11949       vp_window->width = MAX(vp_window->min_width, window_width);
11950     }
11951
11952     if (vp_window->min_height != -1)
11953     {
11954       int window_height = display_height;
11955
11956       // when using static window width, use aspect ratio of display
11957       if (vp_window->min_width == -1)
11958         window_height = vp_window->width * display_height / display_width;
11959
11960       vp_window->height = MAX(vp_window->min_height, window_height);
11961     }
11962
11963     if (vp_window->max_width != -1)
11964       vp_window->width = MIN(vp_window->width, vp_window->max_width);
11965
11966     if (vp_window->max_height != -1)
11967       vp_window->height = MIN(vp_window->height, vp_window->max_height);
11968
11969     int playfield_width  = vp_window->width;
11970     int playfield_height = vp_window->height;
11971
11972     // adjust playfield size and position according to specified margins
11973
11974     playfield_width  -= vp_playfield->margin_left;
11975     playfield_width  -= vp_playfield->margin_right;
11976
11977     playfield_height -= vp_playfield->margin_top;
11978     playfield_height -= vp_playfield->margin_bottom;
11979
11980     // adjust playfield size if min/max width/height is specified
11981
11982     if (vp_playfield->min_width != -1)
11983       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11984
11985     if (vp_playfield->min_height != -1)
11986       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11987
11988     if (vp_playfield->max_width != -1)
11989       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11990
11991     if (vp_playfield->max_height != -1)
11992       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11993
11994     // adjust playfield position according to specified alignment
11995
11996     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
11997       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
11998     else if (vp_playfield->align == ALIGN_CENTER)
11999       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12000     else if (vp_playfield->align == ALIGN_RIGHT)
12001       vp_playfield->x += playfield_width - vp_playfield->width;
12002
12003     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12004       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12005     else if (vp_playfield->valign == VALIGN_MIDDLE)
12006       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12007     else if (vp_playfield->valign == VALIGN_BOTTOM)
12008       vp_playfield->y += playfield_height - vp_playfield->height;
12009
12010     vp_playfield->x += vp_playfield->margin_left;
12011     vp_playfield->y += vp_playfield->margin_top;
12012
12013     // adjust individual playfield borders if only default border is specified
12014
12015     if (vp_playfield->border_left == -1)
12016       vp_playfield->border_left = vp_playfield->border_size;
12017     if (vp_playfield->border_right == -1)
12018       vp_playfield->border_right = vp_playfield->border_size;
12019     if (vp_playfield->border_top == -1)
12020       vp_playfield->border_top = vp_playfield->border_size;
12021     if (vp_playfield->border_bottom == -1)
12022       vp_playfield->border_bottom = vp_playfield->border_size;
12023
12024     // set dynamic playfield borders if borders are specified as undefined
12025     // (but only if window size was dynamic and playfield size was static)
12026
12027     if (dynamic_window_width && !dynamic_playfield_width)
12028     {
12029       if (vp_playfield->border_left == -1)
12030       {
12031         vp_playfield->border_left = (vp_playfield->x -
12032                                      vp_playfield->margin_left);
12033         vp_playfield->x     -= vp_playfield->border_left;
12034         vp_playfield->width += vp_playfield->border_left;
12035       }
12036
12037       if (vp_playfield->border_right == -1)
12038       {
12039         vp_playfield->border_right = (vp_window->width -
12040                                       vp_playfield->x -
12041                                       vp_playfield->width -
12042                                       vp_playfield->margin_right);
12043         vp_playfield->width += vp_playfield->border_right;
12044       }
12045     }
12046
12047     if (dynamic_window_height && !dynamic_playfield_height)
12048     {
12049       if (vp_playfield->border_top == -1)
12050       {
12051         vp_playfield->border_top = (vp_playfield->y -
12052                                     vp_playfield->margin_top);
12053         vp_playfield->y      -= vp_playfield->border_top;
12054         vp_playfield->height += vp_playfield->border_top;
12055       }
12056
12057       if (vp_playfield->border_bottom == -1)
12058       {
12059         vp_playfield->border_bottom = (vp_window->height -
12060                                        vp_playfield->y -
12061                                        vp_playfield->height -
12062                                        vp_playfield->margin_bottom);
12063         vp_playfield->height += vp_playfield->border_bottom;
12064       }
12065     }
12066
12067     // adjust playfield size to be a multiple of a defined alignment tile size
12068
12069     int align_size = vp_playfield->align_size;
12070     int playfield_xtiles = vp_playfield->width  / align_size;
12071     int playfield_ytiles = vp_playfield->height / align_size;
12072     int playfield_width_corrected  = playfield_xtiles * align_size;
12073     int playfield_height_corrected = playfield_ytiles * align_size;
12074     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12075                                  i == GFX_SPECIAL_ARG_EDITOR);
12076
12077     if (is_playfield_mode &&
12078         dynamic_playfield_width &&
12079         vp_playfield->width != playfield_width_corrected)
12080     {
12081       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12082
12083       vp_playfield->width = playfield_width_corrected;
12084
12085       if (vp_playfield->align == ALIGN_LEFT)
12086       {
12087         vp_playfield->border_left += playfield_xdiff;
12088       }
12089       else if (vp_playfield->align == ALIGN_RIGHT)
12090       {
12091         vp_playfield->border_right += playfield_xdiff;
12092       }
12093       else if (vp_playfield->align == ALIGN_CENTER)
12094       {
12095         int border_left_diff  = playfield_xdiff / 2;
12096         int border_right_diff = playfield_xdiff - border_left_diff;
12097
12098         vp_playfield->border_left  += border_left_diff;
12099         vp_playfield->border_right += border_right_diff;
12100       }
12101     }
12102
12103     if (is_playfield_mode &&
12104         dynamic_playfield_height &&
12105         vp_playfield->height != playfield_height_corrected)
12106     {
12107       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12108
12109       vp_playfield->height = playfield_height_corrected;
12110
12111       if (vp_playfield->valign == VALIGN_TOP)
12112       {
12113         vp_playfield->border_top += playfield_ydiff;
12114       }
12115       else if (vp_playfield->align == VALIGN_BOTTOM)
12116       {
12117         vp_playfield->border_right += playfield_ydiff;
12118       }
12119       else if (vp_playfield->align == VALIGN_MIDDLE)
12120       {
12121         int border_top_diff    = playfield_ydiff / 2;
12122         int border_bottom_diff = playfield_ydiff - border_top_diff;
12123
12124         vp_playfield->border_top    += border_top_diff;
12125         vp_playfield->border_bottom += border_bottom_diff;
12126       }
12127     }
12128
12129     // adjust door positions according to specified alignment
12130
12131     for (j = 0; j < 2; j++)
12132     {
12133       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12134
12135       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12136         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12137       else if (vp_door->align == ALIGN_CENTER)
12138         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12139       else if (vp_door->align == ALIGN_RIGHT)
12140         vp_door->x += vp_window->width - vp_door->width;
12141
12142       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12143         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12144       else if (vp_door->valign == VALIGN_MIDDLE)
12145         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12146       else if (vp_door->valign == VALIGN_BOTTOM)
12147         vp_door->y += vp_window->height - vp_door->height;
12148     }
12149   }
12150 }
12151
12152 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12153 {
12154   static struct
12155   {
12156     struct XYTileSize *dst, *src;
12157     int graphic;
12158   }
12159   editor_buttons_xy[] =
12160   {
12161     {
12162       &editor.button.element_left,      &editor.palette.element_left,
12163       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12164     },
12165     {
12166       &editor.button.element_middle,    &editor.palette.element_middle,
12167       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12168     },
12169     {
12170       &editor.button.element_right,     &editor.palette.element_right,
12171       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12172     },
12173
12174     { NULL,                     NULL                    }
12175   };
12176   int i;
12177
12178   // set default position for element buttons to element graphics
12179   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12180   {
12181     if ((*editor_buttons_xy[i].dst).x == -1 &&
12182         (*editor_buttons_xy[i].dst).y == -1)
12183     {
12184       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12185
12186       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12187
12188       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12189     }
12190   }
12191
12192   // adjust editor palette rows and columns if specified to be dynamic
12193
12194   if (editor.palette.cols == -1)
12195   {
12196     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12197     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12198     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12199
12200     editor.palette.cols = (vp_width - sc_width) / bt_width;
12201
12202     if (editor.palette.x == -1)
12203     {
12204       int palette_width = editor.palette.cols * bt_width + sc_width;
12205
12206       editor.palette.x = (vp_width - palette_width) / 2;
12207     }
12208   }
12209
12210   if (editor.palette.rows == -1)
12211   {
12212     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12213     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12214     int tx_height = getFontHeight(FONT_TEXT_2);
12215
12216     editor.palette.rows = (vp_height - tx_height) / bt_height;
12217
12218     if (editor.palette.y == -1)
12219     {
12220       int palette_height = editor.palette.rows * bt_height + tx_height;
12221
12222       editor.palette.y = (vp_height - palette_height) / 2;
12223     }
12224   }
12225 }
12226
12227 static void LoadMenuDesignSettingsFromFilename(char *filename)
12228 {
12229   static struct TitleFadingInfo tfi;
12230   static struct TitleMessageInfo tmi;
12231   static struct TokenInfo title_tokens[] =
12232   {
12233     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12234     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12235     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12236     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12237     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12238
12239     { -1,               NULL,                   NULL                    }
12240   };
12241   static struct TokenInfo titlemessage_tokens[] =
12242   {
12243     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12244     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12245     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12246     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12247     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12248     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12249     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12250     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12251     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12252     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12253     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12254     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12255     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12256     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12257     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12258     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12259     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12260     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12261
12262     { -1,               NULL,                   NULL                    }
12263   };
12264   static struct
12265   {
12266     struct TitleFadingInfo *info;
12267     char *text;
12268   }
12269   title_info[] =
12270   {
12271     // initialize first titles from "enter screen" definitions, if defined
12272     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12273     { &title_first_default,             "menu.enter_screen.TITLE"       },
12274
12275     // initialize title screens from "next screen" definitions, if defined
12276     { &title_initial_default,           "menu.next_screen.TITLE"        },
12277     { &title_default,                   "menu.next_screen.TITLE"        },
12278
12279     { NULL,                             NULL                            }
12280   };
12281   static struct
12282   {
12283     struct TitleMessageInfo *array;
12284     char *text;
12285   }
12286   titlemessage_arrays[] =
12287   {
12288     // initialize first titles from "enter screen" definitions, if defined
12289     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12290     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12291     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12292     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12293
12294     // initialize titles from "next screen" definitions, if defined
12295     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12296     { titlescreen,                      "menu.next_screen.TITLE"        },
12297     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12298     { titlemessage,                     "menu.next_screen.TITLE"        },
12299
12300     // overwrite titles with title definitions, if defined
12301     { titlescreen_initial_first,        "[title_initial]"               },
12302     { titlescreen_first,                "[title]"                       },
12303     { titlemessage_initial_first,       "[title_initial]"               },
12304     { titlemessage_first,               "[title]"                       },
12305
12306     { titlescreen_initial,              "[title_initial]"               },
12307     { titlescreen,                      "[title]"                       },
12308     { titlemessage_initial,             "[title_initial]"               },
12309     { titlemessage,                     "[title]"                       },
12310
12311     // overwrite titles with title screen/message definitions, if defined
12312     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12313     { titlescreen_first,                "[titlescreen]"                 },
12314     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12315     { titlemessage_first,               "[titlemessage]"                },
12316
12317     { titlescreen_initial,              "[titlescreen_initial]"         },
12318     { titlescreen,                      "[titlescreen]"                 },
12319     { titlemessage_initial,             "[titlemessage_initial]"        },
12320     { titlemessage,                     "[titlemessage]"                },
12321
12322     { NULL,                             NULL                            }
12323   };
12324   SetupFileHash *setup_file_hash;
12325   int i, j, k;
12326
12327   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12328     return;
12329
12330   // the following initializes hierarchical values from dynamic configuration
12331
12332   // special case: initialize with default values that may be overwritten
12333   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12334   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12335   {
12336     struct TokenIntPtrInfo menu_config[] =
12337     {
12338       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12339       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12340       { "menu.list_size",       &menu.list_size[i]      }
12341     };
12342
12343     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12344     {
12345       char *token = menu_config[j].token;
12346       char *value = getHashEntry(setup_file_hash, token);
12347
12348       if (value != NULL)
12349         *menu_config[j].value = get_integer_from_string(value);
12350     }
12351   }
12352
12353   // special case: initialize with default values that may be overwritten
12354   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12355   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12356   {
12357     struct TokenIntPtrInfo menu_config[] =
12358     {
12359       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12360       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12361       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12362     };
12363
12364     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12365     {
12366       char *token = menu_config[j].token;
12367       char *value = getHashEntry(setup_file_hash, token);
12368
12369       if (value != NULL)
12370         *menu_config[j].value = get_integer_from_string(value);
12371     }
12372   }
12373
12374   // special case: initialize with default values that may be overwritten
12375   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12376   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12377   {
12378     struct TokenIntPtrInfo menu_config[] =
12379     {
12380       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12381       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12382     };
12383
12384     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12385     {
12386       char *token = menu_config[j].token;
12387       char *value = getHashEntry(setup_file_hash, token);
12388
12389       if (value != NULL)
12390         *menu_config[j].value = get_integer_from_string(value);
12391     }
12392   }
12393
12394   // special case: initialize with default values that may be overwritten
12395   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12396   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12397   {
12398     struct TokenIntPtrInfo menu_config[] =
12399     {
12400       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12401       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12402       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12403       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12404       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12405       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12406       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12407       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12408       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12409     };
12410
12411     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12412     {
12413       char *token = menu_config[j].token;
12414       char *value = getHashEntry(setup_file_hash, token);
12415
12416       if (value != NULL)
12417         *menu_config[j].value = get_integer_from_string(value);
12418     }
12419   }
12420
12421   // special case: initialize with default values that may be overwritten
12422   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12423   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12424   {
12425     struct TokenIntPtrInfo menu_config[] =
12426     {
12427       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12428       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12429       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12430       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12431       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12432       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12433       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12434       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12435       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12436     };
12437
12438     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12439     {
12440       char *token = menu_config[j].token;
12441       char *value = getHashEntry(setup_file_hash, token);
12442
12443       if (value != NULL)
12444         *menu_config[j].value = get_token_parameter_value(token, value);
12445     }
12446   }
12447
12448   // special case: initialize with default values that may be overwritten
12449   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12450   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12451   {
12452     struct
12453     {
12454       char *token_prefix;
12455       struct RectWithBorder *struct_ptr;
12456     }
12457     vp_struct[] =
12458     {
12459       { "viewport.window",      &viewport.window[i]     },
12460       { "viewport.playfield",   &viewport.playfield[i]  },
12461       { "viewport.door_1",      &viewport.door_1[i]     },
12462       { "viewport.door_2",      &viewport.door_2[i]     }
12463     };
12464
12465     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12466     {
12467       struct TokenIntPtrInfo vp_config[] =
12468       {
12469         { ".x",                 &vp_struct[j].struct_ptr->x             },
12470         { ".y",                 &vp_struct[j].struct_ptr->y             },
12471         { ".width",             &vp_struct[j].struct_ptr->width         },
12472         { ".height",            &vp_struct[j].struct_ptr->height        },
12473         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12474         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12475         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12476         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12477         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12478         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12479         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12480         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12481         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12482         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12483         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12484         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12485         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12486         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12487         { ".align",             &vp_struct[j].struct_ptr->align         },
12488         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12489       };
12490
12491       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12492       {
12493         char *token = getStringCat2(vp_struct[j].token_prefix,
12494                                     vp_config[k].token);
12495         char *value = getHashEntry(setup_file_hash, token);
12496
12497         if (value != NULL)
12498           *vp_config[k].value = get_token_parameter_value(token, value);
12499
12500         free(token);
12501       }
12502     }
12503   }
12504
12505   // special case: initialize with default values that may be overwritten
12506   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12507   for (i = 0; title_info[i].info != NULL; i++)
12508   {
12509     struct TitleFadingInfo *info = title_info[i].info;
12510     char *base_token = title_info[i].text;
12511
12512     for (j = 0; title_tokens[j].type != -1; j++)
12513     {
12514       char *token = getStringCat2(base_token, title_tokens[j].text);
12515       char *value = getHashEntry(setup_file_hash, token);
12516
12517       if (value != NULL)
12518       {
12519         int parameter_value = get_token_parameter_value(token, value);
12520
12521         tfi = *info;
12522
12523         *(int *)title_tokens[j].value = (int)parameter_value;
12524
12525         *info = tfi;
12526       }
12527
12528       free(token);
12529     }
12530   }
12531
12532   // special case: initialize with default values that may be overwritten
12533   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12534   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12535   {
12536     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12537     char *base_token = titlemessage_arrays[i].text;
12538
12539     for (j = 0; titlemessage_tokens[j].type != -1; j++)
12540     {
12541       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12542       char *value = getHashEntry(setup_file_hash, token);
12543
12544       if (value != NULL)
12545       {
12546         int parameter_value = get_token_parameter_value(token, value);
12547
12548         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12549         {
12550           tmi = array[k];
12551
12552           if (titlemessage_tokens[j].type == TYPE_INTEGER)
12553             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
12554           else
12555             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12556
12557           array[k] = tmi;
12558         }
12559       }
12560
12561       free(token);
12562     }
12563   }
12564
12565   // special case: check if network and preview player positions are redefined,
12566   // to compare this later against the main menu level preview being redefined
12567   struct TokenIntPtrInfo menu_config_players[] =
12568   {
12569     { "main.network_players.x", &menu.main.network_players.redefined    },
12570     { "main.network_players.y", &menu.main.network_players.redefined    },
12571     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12572     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12573     { "preview.x",              &preview.redefined                      },
12574     { "preview.y",              &preview.redefined                      }
12575   };
12576
12577   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12578     *menu_config_players[i].value = FALSE;
12579
12580   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12581     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12582       *menu_config_players[i].value = TRUE;
12583
12584   // read (and overwrite with) values that may be specified in config file
12585   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12586
12587   freeSetupFileHash(setup_file_hash);
12588 }
12589
12590 void LoadMenuDesignSettings(void)
12591 {
12592   char *filename_base = UNDEFINED_FILENAME, *filename_local;
12593
12594   InitMenuDesignSettings_Static();
12595   InitMenuDesignSettings_SpecialPreProcessing();
12596
12597   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12598   {
12599     // first look for special settings configured in level series config
12600     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12601
12602     if (fileExists(filename_base))
12603       LoadMenuDesignSettingsFromFilename(filename_base);
12604   }
12605
12606   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12607
12608   if (filename_local != NULL && !strEqual(filename_base, filename_local))
12609     LoadMenuDesignSettingsFromFilename(filename_local);
12610
12611   InitMenuDesignSettings_SpecialPostProcessing();
12612 }
12613
12614 void LoadMenuDesignSettings_AfterGraphics(void)
12615 {
12616   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12617 }
12618
12619 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12620 {
12621   char *filename = getEditorSetupFilename();
12622   SetupFileList *setup_file_list, *list;
12623   SetupFileHash *element_hash;
12624   int num_unknown_tokens = 0;
12625   int i;
12626
12627   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12628     return;
12629
12630   element_hash = newSetupFileHash();
12631
12632   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12633     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12634
12635   // determined size may be larger than needed (due to unknown elements)
12636   *num_elements = 0;
12637   for (list = setup_file_list; list != NULL; list = list->next)
12638     (*num_elements)++;
12639
12640   // add space for up to 3 more elements for padding that may be needed
12641   *num_elements += 3;
12642
12643   // free memory for old list of elements, if needed
12644   checked_free(*elements);
12645
12646   // allocate memory for new list of elements
12647   *elements = checked_malloc(*num_elements * sizeof(int));
12648
12649   *num_elements = 0;
12650   for (list = setup_file_list; list != NULL; list = list->next)
12651   {
12652     char *value = getHashEntry(element_hash, list->token);
12653
12654     if (value == NULL)          // try to find obsolete token mapping
12655     {
12656       char *mapped_token = get_mapped_token(list->token);
12657
12658       if (mapped_token != NULL)
12659       {
12660         value = getHashEntry(element_hash, mapped_token);
12661
12662         free(mapped_token);
12663       }
12664     }
12665
12666     if (value != NULL)
12667     {
12668       (*elements)[(*num_elements)++] = atoi(value);
12669     }
12670     else
12671     {
12672       if (num_unknown_tokens == 0)
12673       {
12674         Warn("---");
12675         Warn("unknown token(s) found in config file:");
12676         Warn("- config file: '%s'", filename);
12677
12678         num_unknown_tokens++;
12679       }
12680
12681       Warn("- token: '%s'", list->token);
12682     }
12683   }
12684
12685   if (num_unknown_tokens > 0)
12686     Warn("---");
12687
12688   while (*num_elements % 4)     // pad with empty elements, if needed
12689     (*elements)[(*num_elements)++] = EL_EMPTY;
12690
12691   freeSetupFileList(setup_file_list);
12692   freeSetupFileHash(element_hash);
12693
12694 #if 0
12695   for (i = 0; i < *num_elements; i++)
12696     Debug("editor", "element '%s' [%d]\n",
12697           element_info[(*elements)[i]].token_name, (*elements)[i]);
12698 #endif
12699 }
12700
12701 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12702                                                      boolean is_sound)
12703 {
12704   SetupFileHash *setup_file_hash = NULL;
12705   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12706   char *filename_music, *filename_prefix, *filename_info;
12707   struct
12708   {
12709     char *token;
12710     char **value_ptr;
12711   }
12712   token_to_value_ptr[] =
12713   {
12714     { "title_header",   &tmp_music_file_info.title_header       },
12715     { "artist_header",  &tmp_music_file_info.artist_header      },
12716     { "album_header",   &tmp_music_file_info.album_header       },
12717     { "year_header",    &tmp_music_file_info.year_header        },
12718
12719     { "title",          &tmp_music_file_info.title              },
12720     { "artist",         &tmp_music_file_info.artist             },
12721     { "album",          &tmp_music_file_info.album              },
12722     { "year",           &tmp_music_file_info.year               },
12723
12724     { NULL,             NULL                                    },
12725   };
12726   int i;
12727
12728   filename_music = (is_sound ? getCustomSoundFilename(basename) :
12729                     getCustomMusicFilename(basename));
12730
12731   if (filename_music == NULL)
12732     return NULL;
12733
12734   // ---------- try to replace file extension ----------
12735
12736   filename_prefix = getStringCopy(filename_music);
12737   if (strrchr(filename_prefix, '.') != NULL)
12738     *strrchr(filename_prefix, '.') = '\0';
12739   filename_info = getStringCat2(filename_prefix, ".txt");
12740
12741   if (fileExists(filename_info))
12742     setup_file_hash = loadSetupFileHash(filename_info);
12743
12744   free(filename_prefix);
12745   free(filename_info);
12746
12747   if (setup_file_hash == NULL)
12748   {
12749     // ---------- try to add file extension ----------
12750
12751     filename_prefix = getStringCopy(filename_music);
12752     filename_info = getStringCat2(filename_prefix, ".txt");
12753
12754     if (fileExists(filename_info))
12755       setup_file_hash = loadSetupFileHash(filename_info);
12756
12757     free(filename_prefix);
12758     free(filename_info);
12759   }
12760
12761   if (setup_file_hash == NULL)
12762     return NULL;
12763
12764   // ---------- music file info found ----------
12765
12766   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12767
12768   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12769   {
12770     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12771
12772     *token_to_value_ptr[i].value_ptr =
12773       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12774   }
12775
12776   tmp_music_file_info.basename = getStringCopy(basename);
12777   tmp_music_file_info.music = music;
12778   tmp_music_file_info.is_sound = is_sound;
12779
12780   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12781   *new_music_file_info = tmp_music_file_info;
12782
12783   return new_music_file_info;
12784 }
12785
12786 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12787 {
12788   return get_music_file_info_ext(basename, music, FALSE);
12789 }
12790
12791 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12792 {
12793   return get_music_file_info_ext(basename, sound, TRUE);
12794 }
12795
12796 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12797                                      char *basename, boolean is_sound)
12798 {
12799   for (; list != NULL; list = list->next)
12800     if (list->is_sound == is_sound && strEqual(list->basename, basename))
12801       return TRUE;
12802
12803   return FALSE;
12804 }
12805
12806 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12807 {
12808   return music_info_listed_ext(list, basename, FALSE);
12809 }
12810
12811 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12812 {
12813   return music_info_listed_ext(list, basename, TRUE);
12814 }
12815
12816 void LoadMusicInfo(void)
12817 {
12818   char *music_directory = getCustomMusicDirectory();
12819   int num_music = getMusicListSize();
12820   int num_music_noconf = 0;
12821   int num_sounds = getSoundListSize();
12822   Directory *dir;
12823   DirectoryEntry *dir_entry;
12824   struct FileInfo *music, *sound;
12825   struct MusicFileInfo *next, **new;
12826   int i;
12827
12828   while (music_file_info != NULL)
12829   {
12830     next = music_file_info->next;
12831
12832     checked_free(music_file_info->basename);
12833
12834     checked_free(music_file_info->title_header);
12835     checked_free(music_file_info->artist_header);
12836     checked_free(music_file_info->album_header);
12837     checked_free(music_file_info->year_header);
12838
12839     checked_free(music_file_info->title);
12840     checked_free(music_file_info->artist);
12841     checked_free(music_file_info->album);
12842     checked_free(music_file_info->year);
12843
12844     free(music_file_info);
12845
12846     music_file_info = next;
12847   }
12848
12849   new = &music_file_info;
12850
12851   for (i = 0; i < num_music; i++)
12852   {
12853     music = getMusicListEntry(i);
12854
12855     if (music->filename == NULL)
12856       continue;
12857
12858     if (strEqual(music->filename, UNDEFINED_FILENAME))
12859       continue;
12860
12861     // a configured file may be not recognized as music
12862     if (!FileIsMusic(music->filename))
12863       continue;
12864
12865     if (!music_info_listed(music_file_info, music->filename))
12866     {
12867       *new = get_music_file_info(music->filename, i);
12868
12869       if (*new != NULL)
12870         new = &(*new)->next;
12871     }
12872   }
12873
12874   if ((dir = openDirectory(music_directory)) == NULL)
12875   {
12876     Warn("cannot read music directory '%s'", music_directory);
12877
12878     return;
12879   }
12880
12881   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
12882   {
12883     char *basename = dir_entry->basename;
12884     boolean music_already_used = FALSE;
12885     int i;
12886
12887     // skip all music files that are configured in music config file
12888     for (i = 0; i < num_music; i++)
12889     {
12890       music = getMusicListEntry(i);
12891
12892       if (music->filename == NULL)
12893         continue;
12894
12895       if (strEqual(basename, music->filename))
12896       {
12897         music_already_used = TRUE;
12898         break;
12899       }
12900     }
12901
12902     if (music_already_used)
12903       continue;
12904
12905     if (!FileIsMusic(dir_entry->filename))
12906       continue;
12907
12908     if (!music_info_listed(music_file_info, basename))
12909     {
12910       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12911
12912       if (*new != NULL)
12913         new = &(*new)->next;
12914     }
12915
12916     num_music_noconf++;
12917   }
12918
12919   closeDirectory(dir);
12920
12921   for (i = 0; i < num_sounds; i++)
12922   {
12923     sound = getSoundListEntry(i);
12924
12925     if (sound->filename == NULL)
12926       continue;
12927
12928     if (strEqual(sound->filename, UNDEFINED_FILENAME))
12929       continue;
12930
12931     // a configured file may be not recognized as sound
12932     if (!FileIsSound(sound->filename))
12933       continue;
12934
12935     if (!sound_info_listed(music_file_info, sound->filename))
12936     {
12937       *new = get_sound_file_info(sound->filename, i);
12938       if (*new != NULL)
12939         new = &(*new)->next;
12940     }
12941   }
12942
12943   // add pointers to previous list nodes
12944
12945   struct MusicFileInfo *node = music_file_info;
12946
12947   while (node != NULL)
12948   {
12949     if (node->next)
12950       node->next->prev = node;
12951
12952     node = node->next;
12953   }
12954 }
12955
12956 static void add_helpanim_entry(int element, int action, int direction,
12957                                int delay, int *num_list_entries)
12958 {
12959   struct HelpAnimInfo *new_list_entry;
12960   (*num_list_entries)++;
12961
12962   helpanim_info =
12963     checked_realloc(helpanim_info,
12964                     *num_list_entries * sizeof(struct HelpAnimInfo));
12965   new_list_entry = &helpanim_info[*num_list_entries - 1];
12966
12967   new_list_entry->element = element;
12968   new_list_entry->action = action;
12969   new_list_entry->direction = direction;
12970   new_list_entry->delay = delay;
12971 }
12972
12973 static void print_unknown_token(char *filename, char *token, int token_nr)
12974 {
12975   if (token_nr == 0)
12976   {
12977     Warn("---");
12978     Warn("unknown token(s) found in config file:");
12979     Warn("- config file: '%s'", filename);
12980   }
12981
12982   Warn("- token: '%s'", token);
12983 }
12984
12985 static void print_unknown_token_end(int token_nr)
12986 {
12987   if (token_nr > 0)
12988     Warn("---");
12989 }
12990
12991 void LoadHelpAnimInfo(void)
12992 {
12993   char *filename = getHelpAnimFilename();
12994   SetupFileList *setup_file_list = NULL, *list;
12995   SetupFileHash *element_hash, *action_hash, *direction_hash;
12996   int num_list_entries = 0;
12997   int num_unknown_tokens = 0;
12998   int i;
12999
13000   if (fileExists(filename))
13001     setup_file_list = loadSetupFileList(filename);
13002
13003   if (setup_file_list == NULL)
13004   {
13005     // use reliable default values from static configuration
13006     SetupFileList *insert_ptr;
13007
13008     insert_ptr = setup_file_list =
13009       newSetupFileList(helpanim_config[0].token,
13010                        helpanim_config[0].value);
13011
13012     for (i = 1; helpanim_config[i].token; i++)
13013       insert_ptr = addListEntry(insert_ptr,
13014                                 helpanim_config[i].token,
13015                                 helpanim_config[i].value);
13016   }
13017
13018   element_hash   = newSetupFileHash();
13019   action_hash    = newSetupFileHash();
13020   direction_hash = newSetupFileHash();
13021
13022   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13023     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13024
13025   for (i = 0; i < NUM_ACTIONS; i++)
13026     setHashEntry(action_hash, element_action_info[i].suffix,
13027                  i_to_a(element_action_info[i].value));
13028
13029   // do not store direction index (bit) here, but direction value!
13030   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13031     setHashEntry(direction_hash, element_direction_info[i].suffix,
13032                  i_to_a(1 << element_direction_info[i].value));
13033
13034   for (list = setup_file_list; list != NULL; list = list->next)
13035   {
13036     char *element_token, *action_token, *direction_token;
13037     char *element_value, *action_value, *direction_value;
13038     int delay = atoi(list->value);
13039
13040     if (strEqual(list->token, "end"))
13041     {
13042       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13043
13044       continue;
13045     }
13046
13047     /* first try to break element into element/action/direction parts;
13048        if this does not work, also accept combined "element[.act][.dir]"
13049        elements (like "dynamite.active"), which are unique elements */
13050
13051     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13052     {
13053       element_value = getHashEntry(element_hash, list->token);
13054       if (element_value != NULL)        // element found
13055         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13056                            &num_list_entries);
13057       else
13058       {
13059         // no further suffixes found -- this is not an element
13060         print_unknown_token(filename, list->token, num_unknown_tokens++);
13061       }
13062
13063       continue;
13064     }
13065
13066     // token has format "<prefix>.<something>"
13067
13068     action_token = strchr(list->token, '.');    // suffix may be action ...
13069     direction_token = action_token;             // ... or direction
13070
13071     element_token = getStringCopy(list->token);
13072     *strchr(element_token, '.') = '\0';
13073
13074     element_value = getHashEntry(element_hash, element_token);
13075
13076     if (element_value == NULL)          // this is no element
13077     {
13078       element_value = getHashEntry(element_hash, list->token);
13079       if (element_value != NULL)        // combined element found
13080         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13081                            &num_list_entries);
13082       else
13083         print_unknown_token(filename, list->token, num_unknown_tokens++);
13084
13085       free(element_token);
13086
13087       continue;
13088     }
13089
13090     action_value = getHashEntry(action_hash, action_token);
13091
13092     if (action_value != NULL)           // action found
13093     {
13094       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13095                     &num_list_entries);
13096
13097       free(element_token);
13098
13099       continue;
13100     }
13101
13102     direction_value = getHashEntry(direction_hash, direction_token);
13103
13104     if (direction_value != NULL)        // direction found
13105     {
13106       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13107                          &num_list_entries);
13108
13109       free(element_token);
13110
13111       continue;
13112     }
13113
13114     if (strchr(action_token + 1, '.') == NULL)
13115     {
13116       // no further suffixes found -- this is not an action nor direction
13117
13118       element_value = getHashEntry(element_hash, list->token);
13119       if (element_value != NULL)        // combined element found
13120         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13121                            &num_list_entries);
13122       else
13123         print_unknown_token(filename, list->token, num_unknown_tokens++);
13124
13125       free(element_token);
13126
13127       continue;
13128     }
13129
13130     // token has format "<prefix>.<suffix>.<something>"
13131
13132     direction_token = strchr(action_token + 1, '.');
13133
13134     action_token = getStringCopy(action_token);
13135     *strchr(action_token + 1, '.') = '\0';
13136
13137     action_value = getHashEntry(action_hash, action_token);
13138
13139     if (action_value == NULL)           // this is no action
13140     {
13141       element_value = getHashEntry(element_hash, list->token);
13142       if (element_value != NULL)        // combined element found
13143         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13144                            &num_list_entries);
13145       else
13146         print_unknown_token(filename, list->token, num_unknown_tokens++);
13147
13148       free(element_token);
13149       free(action_token);
13150
13151       continue;
13152     }
13153
13154     direction_value = getHashEntry(direction_hash, direction_token);
13155
13156     if (direction_value != NULL)        // direction found
13157     {
13158       add_helpanim_entry(atoi(element_value), atoi(action_value),
13159                          atoi(direction_value), delay, &num_list_entries);
13160
13161       free(element_token);
13162       free(action_token);
13163
13164       continue;
13165     }
13166
13167     // this is no direction
13168
13169     element_value = getHashEntry(element_hash, list->token);
13170     if (element_value != NULL)          // combined element found
13171       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13172                          &num_list_entries);
13173     else
13174       print_unknown_token(filename, list->token, num_unknown_tokens++);
13175
13176     free(element_token);
13177     free(action_token);
13178   }
13179
13180   print_unknown_token_end(num_unknown_tokens);
13181
13182   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13183   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13184
13185   freeSetupFileList(setup_file_list);
13186   freeSetupFileHash(element_hash);
13187   freeSetupFileHash(action_hash);
13188   freeSetupFileHash(direction_hash);
13189
13190 #if 0
13191   for (i = 0; i < num_list_entries; i++)
13192     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13193           EL_NAME(helpanim_info[i].element),
13194           helpanim_info[i].element,
13195           helpanim_info[i].action,
13196           helpanim_info[i].direction,
13197           helpanim_info[i].delay);
13198 #endif
13199 }
13200
13201 void LoadHelpTextInfo(void)
13202 {
13203   char *filename = getHelpTextFilename();
13204   int i;
13205
13206   if (helptext_info != NULL)
13207   {
13208     freeSetupFileHash(helptext_info);
13209     helptext_info = NULL;
13210   }
13211
13212   if (fileExists(filename))
13213     helptext_info = loadSetupFileHash(filename);
13214
13215   if (helptext_info == NULL)
13216   {
13217     // use reliable default values from static configuration
13218     helptext_info = newSetupFileHash();
13219
13220     for (i = 0; helptext_config[i].token; i++)
13221       setHashEntry(helptext_info,
13222                    helptext_config[i].token,
13223                    helptext_config[i].value);
13224   }
13225
13226 #if 0
13227   BEGIN_HASH_ITERATION(helptext_info, itr)
13228   {
13229     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13230           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13231   }
13232   END_HASH_ITERATION(hash, itr)
13233 #endif
13234 }
13235
13236
13237 // ----------------------------------------------------------------------------
13238 // convert levels
13239 // ----------------------------------------------------------------------------
13240
13241 #define MAX_NUM_CONVERT_LEVELS          1000
13242
13243 void ConvertLevels(void)
13244 {
13245   static LevelDirTree *convert_leveldir = NULL;
13246   static int convert_level_nr = -1;
13247   static int num_levels_handled = 0;
13248   static int num_levels_converted = 0;
13249   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13250   int i;
13251
13252   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13253                                                global.convert_leveldir);
13254
13255   if (convert_leveldir == NULL)
13256     Fail("no such level identifier: '%s'", global.convert_leveldir);
13257
13258   leveldir_current = convert_leveldir;
13259
13260   if (global.convert_level_nr != -1)
13261   {
13262     convert_leveldir->first_level = global.convert_level_nr;
13263     convert_leveldir->last_level  = global.convert_level_nr;
13264   }
13265
13266   convert_level_nr = convert_leveldir->first_level;
13267
13268   PrintLine("=", 79);
13269   Print("Converting levels\n");
13270   PrintLine("-", 79);
13271   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13272   Print("Level series name:       '%s'\n", convert_leveldir->name);
13273   Print("Level series author:     '%s'\n", convert_leveldir->author);
13274   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13275   PrintLine("=", 79);
13276   Print("\n");
13277
13278   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13279     levels_failed[i] = FALSE;
13280
13281   while (convert_level_nr <= convert_leveldir->last_level)
13282   {
13283     char *level_filename;
13284     boolean new_level;
13285
13286     level_nr = convert_level_nr++;
13287
13288     Print("Level %03d: ", level_nr);
13289
13290     LoadLevel(level_nr);
13291     if (level.no_level_file || level.no_valid_file)
13292     {
13293       Print("(no level)\n");
13294       continue;
13295     }
13296
13297     Print("converting level ... ");
13298
13299 #if 0
13300     // special case: conversion of some EMC levels as requested by ACME
13301     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13302 #endif
13303
13304     level_filename = getDefaultLevelFilename(level_nr);
13305     new_level = !fileExists(level_filename);
13306
13307     if (new_level)
13308     {
13309       SaveLevel(level_nr);
13310
13311       num_levels_converted++;
13312
13313       Print("converted.\n");
13314     }
13315     else
13316     {
13317       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13318         levels_failed[level_nr] = TRUE;
13319
13320       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13321     }
13322
13323     num_levels_handled++;
13324   }
13325
13326   Print("\n");
13327   PrintLine("=", 79);
13328   Print("Number of levels handled: %d\n", num_levels_handled);
13329   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13330          (num_levels_handled ?
13331           num_levels_converted * 100 / num_levels_handled : 0));
13332   PrintLine("-", 79);
13333   Print("Summary (for automatic parsing by scripts):\n");
13334   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13335          convert_leveldir->identifier, num_levels_converted,
13336          num_levels_handled,
13337          (num_levels_handled ?
13338           num_levels_converted * 100 / num_levels_handled : 0));
13339
13340   if (num_levels_handled != num_levels_converted)
13341   {
13342     Print(", FAILED:");
13343     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13344       if (levels_failed[i])
13345         Print(" %03d", i);
13346   }
13347
13348   Print("\n");
13349   PrintLine("=", 79);
13350
13351   CloseAllAndExit(0);
13352 }
13353
13354
13355 // ----------------------------------------------------------------------------
13356 // create and save images for use in level sketches (raw BMP format)
13357 // ----------------------------------------------------------------------------
13358
13359 void CreateLevelSketchImages(void)
13360 {
13361   Bitmap *bitmap1;
13362   Bitmap *bitmap2;
13363   int i;
13364
13365   InitElementPropertiesGfxElement();
13366
13367   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13368   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13369
13370   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13371   {
13372     int element = getMappedElement(i);
13373     char basename1[16];
13374     char basename2[16];
13375     char *filename1;
13376     char *filename2;
13377
13378     sprintf(basename1, "%04d.bmp", i);
13379     sprintf(basename2, "%04ds.bmp", i);
13380
13381     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13382     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13383
13384     DrawSizedElement(0, 0, element, TILESIZE);
13385     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13386
13387     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13388       Fail("cannot save level sketch image file '%s'", filename1);
13389
13390     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13391     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13392
13393     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13394       Fail("cannot save level sketch image file '%s'", filename2);
13395
13396     free(filename1);
13397     free(filename2);
13398
13399     // create corresponding SQL statements (for normal and small images)
13400     if (i < 1000)
13401     {
13402       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13403       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13404     }
13405
13406     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13407     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13408
13409     // optional: create content for forum level sketch demonstration post
13410     if (options.debug)
13411       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13412   }
13413
13414   FreeBitmap(bitmap1);
13415   FreeBitmap(bitmap2);
13416
13417   if (options.debug)
13418     fprintf(stderr, "\n");
13419
13420   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13421
13422   CloseAllAndExit(0);
13423 }
13424
13425
13426 // ----------------------------------------------------------------------------
13427 // create and save images for element collecting animations (raw BMP format)
13428 // ----------------------------------------------------------------------------
13429
13430 static boolean createCollectImage(int element)
13431 {
13432   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13433 }
13434
13435 void CreateCollectElementImages(void)
13436 {
13437   int i, j;
13438   int num_steps = 8;
13439   int anim_frames = num_steps - 1;
13440   int tile_size = TILESIZE;
13441   int anim_width  = tile_size * anim_frames;
13442   int anim_height = tile_size;
13443   int num_collect_images = 0;
13444   int pos_collect_images = 0;
13445
13446   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13447     if (createCollectImage(i))
13448       num_collect_images++;
13449
13450   Info("Creating %d element collecting animation images ...",
13451        num_collect_images);
13452
13453   int dst_width  = anim_width * 2;
13454   int dst_height = anim_height * num_collect_images / 2;
13455   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13456   char *basename = "RocksCollect.bmp";
13457   char *filename = getPath2(global.create_collect_images_dir, basename);
13458
13459   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13460   {
13461     if (!createCollectImage(i))
13462       continue;
13463
13464     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13465     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13466     int graphic = el2img(i);
13467     char *token_name = element_info[i].token_name;
13468     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13469     Bitmap *src_bitmap;
13470     int src_x, src_y;
13471
13472     Info("- creating collecting image for '%s' ...", token_name);
13473
13474     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13475
13476     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13477                tile_size, tile_size, 0, 0);
13478
13479     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13480
13481     for (j = 0; j < anim_frames; j++)
13482     {
13483       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13484       int frame_size = frame_size_final * num_steps;
13485       int offset = (tile_size - frame_size_final) / 2;
13486       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13487
13488       while (frame_size > frame_size_final)
13489       {
13490         frame_size /= 2;
13491
13492         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13493
13494         FreeBitmap(frame_bitmap);
13495
13496         frame_bitmap = half_bitmap;
13497       }
13498
13499       BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13500                  frame_size_final, frame_size_final,
13501                  dst_x + j * tile_size + offset, dst_y + offset);
13502
13503       FreeBitmap(frame_bitmap);
13504     }
13505
13506     tmp_bitmap->surface_masked = NULL;
13507
13508     FreeBitmap(tmp_bitmap);
13509
13510     pos_collect_images++;
13511   }
13512
13513   if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13514     Fail("cannot save element collecting image file '%s'", filename);
13515
13516   FreeBitmap(dst_bitmap);
13517
13518   Info("Done.");
13519
13520   CloseAllAndExit(0);
13521 }
13522
13523
13524 // ----------------------------------------------------------------------------
13525 // create and save images for custom and group elements (raw BMP format)
13526 // ----------------------------------------------------------------------------
13527
13528 void CreateCustomElementImages(char *directory)
13529 {
13530   char *src_basename = "RocksCE-template.ilbm";
13531   char *dst_basename = "RocksCE.bmp";
13532   char *src_filename = getPath2(directory, src_basename);
13533   char *dst_filename = getPath2(directory, dst_basename);
13534   Bitmap *src_bitmap;
13535   Bitmap *bitmap;
13536   int yoffset_ce = 0;
13537   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13538   int i;
13539
13540   InitVideoDefaults();
13541
13542   ReCreateBitmap(&backbuffer, video.width, video.height);
13543
13544   src_bitmap = LoadImage(src_filename);
13545
13546   bitmap = CreateBitmap(TILEX * 16 * 2,
13547                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13548                         DEFAULT_DEPTH);
13549
13550   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13551   {
13552     int x = i % 16;
13553     int y = i / 16;
13554     int ii = i + 1;
13555     int j;
13556
13557     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13558                TILEX * x, TILEY * y + yoffset_ce);
13559
13560     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13561                TILEX, TILEY,
13562                TILEX * x + TILEX * 16,
13563                TILEY * y + yoffset_ce);
13564
13565     for (j = 2; j >= 0; j--)
13566     {
13567       int c = ii % 10;
13568
13569       BlitBitmap(src_bitmap, bitmap,
13570                  TILEX + c * 7, 0, 6, 10,
13571                  TILEX * x + 6 + j * 7,
13572                  TILEY * y + 11 + yoffset_ce);
13573
13574       BlitBitmap(src_bitmap, bitmap,
13575                  TILEX + c * 8, TILEY, 6, 10,
13576                  TILEX * 16 + TILEX * x + 6 + j * 8,
13577                  TILEY * y + 10 + yoffset_ce);
13578
13579       ii /= 10;
13580     }
13581   }
13582
13583   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13584   {
13585     int x = i % 16;
13586     int y = i / 16;
13587     int ii = i + 1;
13588     int j;
13589
13590     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13591                TILEX * x, TILEY * y + yoffset_ge);
13592
13593     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13594                TILEX, TILEY,
13595                TILEX * x + TILEX * 16,
13596                TILEY * y + yoffset_ge);
13597
13598     for (j = 1; j >= 0; j--)
13599     {
13600       int c = ii % 10;
13601
13602       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13603                  TILEX * x + 6 + j * 10,
13604                  TILEY * y + 11 + yoffset_ge);
13605
13606       BlitBitmap(src_bitmap, bitmap,
13607                  TILEX + c * 8, TILEY + 12, 6, 10,
13608                  TILEX * 16 + TILEX * x + 10 + j * 8,
13609                  TILEY * y + 10 + yoffset_ge);
13610
13611       ii /= 10;
13612     }
13613   }
13614
13615   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13616     Fail("cannot save CE graphics file '%s'", dst_filename);
13617
13618   FreeBitmap(bitmap);
13619
13620   CloseAllAndExit(0);
13621 }