fixed crash bug when accessing invalid group data for broken level files
[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     // level file might contain invalid change page number
3359     if (xx_current_change_page >= ei->num_change_pages)
3360       break;
3361
3362     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3363
3364     xx_change = *change;        // copy change data into temporary buffer
3365
3366     resetEventBits();           // reset bits; change page might have changed
3367
3368     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3369                                             -1, element);
3370
3371     *change = xx_change;
3372
3373     setEventFlagsFromEventBits(change);
3374
3375     if (real_chunk_size >= chunk_size)
3376       break;
3377   }
3378
3379   level->file_has_custom_elements = TRUE;
3380
3381   return real_chunk_size;
3382 }
3383
3384 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3385 {
3386   int element = getMappedElement(getFile16BitBE(file));
3387   int real_chunk_size = 2;
3388   struct ElementInfo *ei = &element_info[element];
3389   struct ElementGroupInfo *group = ei->group;
3390
3391   if (group == NULL)
3392     return -1;
3393
3394   xx_ei = *ei;          // copy element data into temporary buffer
3395   xx_group = *group;    // copy group data into temporary buffer
3396
3397   while (!checkEndOfFile(file))
3398   {
3399     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3400                                             -1, element);
3401
3402     if (real_chunk_size >= chunk_size)
3403       break;
3404   }
3405
3406   *ei = xx_ei;
3407   *group = xx_group;
3408
3409   level->file_has_custom_elements = TRUE;
3410
3411   return real_chunk_size;
3412 }
3413
3414 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3415 {
3416   int element = getMappedElement(getFile16BitBE(file));
3417   int real_chunk_size = 2;
3418   struct ElementInfo *ei = &element_info[element];
3419
3420   xx_ei = *ei;          // copy element data into temporary buffer
3421
3422   while (!checkEndOfFile(file))
3423   {
3424     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3425                                             -1, element);
3426
3427     if (real_chunk_size >= chunk_size)
3428       break;
3429   }
3430
3431   *ei = xx_ei;
3432
3433   level->file_has_custom_elements = TRUE;
3434
3435   return real_chunk_size;
3436 }
3437
3438 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3439                                       struct LevelFileInfo *level_file_info,
3440                                       boolean level_info_only)
3441 {
3442   char *filename = level_file_info->filename;
3443   char cookie[MAX_LINE_LEN];
3444   char chunk_name[CHUNK_ID_LEN + 1];
3445   int chunk_size;
3446   File *file;
3447
3448   if (!(file = openFile(filename, MODE_READ)))
3449   {
3450     level->no_valid_file = TRUE;
3451     level->no_level_file = TRUE;
3452
3453     if (level_info_only)
3454       return;
3455
3456     Warn("cannot read level '%s' -- using empty level", filename);
3457
3458     if (!setup.editor.use_template_for_new_levels)
3459       return;
3460
3461     // if level file not found, try to initialize level data from template
3462     filename = getGlobalLevelTemplateFilename();
3463
3464     if (!(file = openFile(filename, MODE_READ)))
3465       return;
3466
3467     // default: for empty levels, use level template for custom elements
3468     level->use_custom_template = TRUE;
3469
3470     level->no_valid_file = FALSE;
3471   }
3472
3473   getFileChunkBE(file, chunk_name, NULL);
3474   if (strEqual(chunk_name, "RND1"))
3475   {
3476     getFile32BitBE(file);               // not used
3477
3478     getFileChunkBE(file, chunk_name, NULL);
3479     if (!strEqual(chunk_name, "CAVE"))
3480     {
3481       level->no_valid_file = TRUE;
3482
3483       Warn("unknown format of level file '%s'", filename);
3484
3485       closeFile(file);
3486
3487       return;
3488     }
3489   }
3490   else  // check for pre-2.0 file format with cookie string
3491   {
3492     strcpy(cookie, chunk_name);
3493     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3494       cookie[4] = '\0';
3495     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3496       cookie[strlen(cookie) - 1] = '\0';
3497
3498     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3499     {
3500       level->no_valid_file = TRUE;
3501
3502       Warn("unknown format of level file '%s'", filename);
3503
3504       closeFile(file);
3505
3506       return;
3507     }
3508
3509     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3510     {
3511       level->no_valid_file = TRUE;
3512
3513       Warn("unsupported version of level file '%s'", filename);
3514
3515       closeFile(file);
3516
3517       return;
3518     }
3519
3520     // pre-2.0 level files have no game version, so use file version here
3521     level->game_version = level->file_version;
3522   }
3523
3524   if (level->file_version < FILE_VERSION_1_2)
3525   {
3526     // level files from versions before 1.2.0 without chunk structure
3527     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3528     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3529   }
3530   else
3531   {
3532     static struct
3533     {
3534       char *name;
3535       int size;
3536       int (*loader)(File *, int, struct LevelInfo *);
3537     }
3538     chunk_info[] =
3539     {
3540       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3541       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3542       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3543       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3544       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3545       { "INFO", -1,                     LoadLevel_INFO },
3546       { "BODY", -1,                     LoadLevel_BODY },
3547       { "CONT", -1,                     LoadLevel_CONT },
3548       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3549       { "CNT3", -1,                     LoadLevel_CNT3 },
3550       { "CUS1", -1,                     LoadLevel_CUS1 },
3551       { "CUS2", -1,                     LoadLevel_CUS2 },
3552       { "CUS3", -1,                     LoadLevel_CUS3 },
3553       { "CUS4", -1,                     LoadLevel_CUS4 },
3554       { "GRP1", -1,                     LoadLevel_GRP1 },
3555       { "CONF", -1,                     LoadLevel_CONF },
3556       { "ELEM", -1,                     LoadLevel_ELEM },
3557       { "NOTE", -1,                     LoadLevel_NOTE },
3558       { "CUSX", -1,                     LoadLevel_CUSX },
3559       { "GRPX", -1,                     LoadLevel_GRPX },
3560       { "EMPX", -1,                     LoadLevel_EMPX },
3561
3562       {  NULL,  0,                      NULL }
3563     };
3564
3565     while (getFileChunkBE(file, chunk_name, &chunk_size))
3566     {
3567       int i = 0;
3568
3569       while (chunk_info[i].name != NULL &&
3570              !strEqual(chunk_name, chunk_info[i].name))
3571         i++;
3572
3573       if (chunk_info[i].name == NULL)
3574       {
3575         Warn("unknown chunk '%s' in level file '%s'",
3576              chunk_name, filename);
3577
3578         ReadUnusedBytesFromFile(file, chunk_size);
3579       }
3580       else if (chunk_info[i].size != -1 &&
3581                chunk_info[i].size != chunk_size)
3582       {
3583         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3584              chunk_size, chunk_name, filename);
3585
3586         ReadUnusedBytesFromFile(file, chunk_size);
3587       }
3588       else
3589       {
3590         // call function to load this level chunk
3591         int chunk_size_expected =
3592           (chunk_info[i].loader)(file, chunk_size, level);
3593
3594         // the size of some chunks cannot be checked before reading other
3595         // chunks first (like "HEAD" and "BODY") that contain some header
3596         // information, so check them here
3597         if (chunk_size_expected != chunk_size)
3598         {
3599           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3600                chunk_size, chunk_name, filename);
3601         }
3602       }
3603     }
3604   }
3605
3606   closeFile(file);
3607 }
3608
3609
3610 // ----------------------------------------------------------------------------
3611 // functions for loading EM level
3612 // ----------------------------------------------------------------------------
3613
3614 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3615 {
3616   static int ball_xy[8][2] =
3617   {
3618     { 0, 0 },
3619     { 1, 0 },
3620     { 2, 0 },
3621     { 0, 1 },
3622     { 2, 1 },
3623     { 0, 2 },
3624     { 1, 2 },
3625     { 2, 2 },
3626   };
3627   struct LevelInfo_EM *level_em = level->native_em_level;
3628   struct CAVE *cav = level_em->cav;
3629   int i, j, x, y;
3630
3631   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3632   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3633
3634   cav->time_seconds     = level->time;
3635   cav->gems_needed      = level->gems_needed;
3636
3637   cav->emerald_score    = level->score[SC_EMERALD];
3638   cav->diamond_score    = level->score[SC_DIAMOND];
3639   cav->alien_score      = level->score[SC_ROBOT];
3640   cav->tank_score       = level->score[SC_SPACESHIP];
3641   cav->bug_score        = level->score[SC_BUG];
3642   cav->eater_score      = level->score[SC_YAMYAM];
3643   cav->nut_score        = level->score[SC_NUT];
3644   cav->dynamite_score   = level->score[SC_DYNAMITE];
3645   cav->key_score        = level->score[SC_KEY];
3646   cav->exit_score       = level->score[SC_TIME_BONUS];
3647
3648   cav->num_eater_arrays = level->num_yamyam_contents;
3649
3650   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3651     for (y = 0; y < 3; y++)
3652       for (x = 0; x < 3; x++)
3653         cav->eater_array[i][y * 3 + x] =
3654           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3655
3656   cav->amoeba_time              = level->amoeba_speed;
3657   cav->wonderwall_time          = level->time_magic_wall;
3658   cav->wheel_time               = level->time_wheel;
3659
3660   cav->android_move_time        = level->android_move_time;
3661   cav->android_clone_time       = level->android_clone_time;
3662   cav->ball_random              = level->ball_random;
3663   cav->ball_active              = level->ball_active_initial;
3664   cav->ball_time                = level->ball_time;
3665   cav->num_ball_arrays          = level->num_ball_contents;
3666
3667   cav->lenses_score             = level->lenses_score;
3668   cav->magnify_score            = level->magnify_score;
3669   cav->slurp_score              = level->slurp_score;
3670
3671   cav->lenses_time              = level->lenses_time;
3672   cav->magnify_time             = level->magnify_time;
3673
3674   cav->wind_direction =
3675     map_direction_RND_to_EM(level->wind_direction_initial);
3676
3677   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3678     for (j = 0; j < 8; j++)
3679       cav->ball_array[i][j] =
3680         map_element_RND_to_EM_cave(level->ball_content[i].
3681                                    e[ball_xy[j][0]][ball_xy[j][1]]);
3682
3683   map_android_clone_elements_RND_to_EM(level);
3684
3685   // first fill the complete playfield with the empty space element
3686   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3687     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3688       cav->cave[x][y] = Cblank;
3689
3690   // then copy the real level contents from level file into the playfield
3691   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3692   {
3693     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3694
3695     if (level->field[x][y] == EL_AMOEBA_DEAD)
3696       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3697
3698     cav->cave[x][y] = new_element;
3699   }
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     cav->player_x[i] = -1;
3704     cav->player_y[i] = -1;
3705   }
3706
3707   // initialize player positions and delete players from the playfield
3708   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3709   {
3710     if (IS_PLAYER_ELEMENT(level->field[x][y]))
3711     {
3712       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3713
3714       cav->player_x[player_nr] = x;
3715       cav->player_y[player_nr] = y;
3716
3717       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3718     }
3719   }
3720 }
3721
3722 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3723 {
3724   static int ball_xy[8][2] =
3725   {
3726     { 0, 0 },
3727     { 1, 0 },
3728     { 2, 0 },
3729     { 0, 1 },
3730     { 2, 1 },
3731     { 0, 2 },
3732     { 1, 2 },
3733     { 2, 2 },
3734   };
3735   struct LevelInfo_EM *level_em = level->native_em_level;
3736   struct CAVE *cav = level_em->cav;
3737   int i, j, x, y;
3738
3739   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
3740   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3741
3742   level->time        = cav->time_seconds;
3743   level->gems_needed = cav->gems_needed;
3744
3745   sprintf(level->name, "Level %d", level->file_info.nr);
3746
3747   level->score[SC_EMERALD]      = cav->emerald_score;
3748   level->score[SC_DIAMOND]      = cav->diamond_score;
3749   level->score[SC_ROBOT]        = cav->alien_score;
3750   level->score[SC_SPACESHIP]    = cav->tank_score;
3751   level->score[SC_BUG]          = cav->bug_score;
3752   level->score[SC_YAMYAM]       = cav->eater_score;
3753   level->score[SC_NUT]          = cav->nut_score;
3754   level->score[SC_DYNAMITE]     = cav->dynamite_score;
3755   level->score[SC_KEY]          = cav->key_score;
3756   level->score[SC_TIME_BONUS]   = cav->exit_score;
3757
3758   level->num_yamyam_contents    = cav->num_eater_arrays;
3759
3760   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3761     for (y = 0; y < 3; y++)
3762       for (x = 0; x < 3; x++)
3763         level->yamyam_content[i].e[x][y] =
3764           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3765
3766   level->amoeba_speed           = cav->amoeba_time;
3767   level->time_magic_wall        = cav->wonderwall_time;
3768   level->time_wheel             = cav->wheel_time;
3769
3770   level->android_move_time      = cav->android_move_time;
3771   level->android_clone_time     = cav->android_clone_time;
3772   level->ball_random            = cav->ball_random;
3773   level->ball_active_initial    = cav->ball_active;
3774   level->ball_time              = cav->ball_time;
3775   level->num_ball_contents      = cav->num_ball_arrays;
3776
3777   level->lenses_score           = cav->lenses_score;
3778   level->magnify_score          = cav->magnify_score;
3779   level->slurp_score            = cav->slurp_score;
3780
3781   level->lenses_time            = cav->lenses_time;
3782   level->magnify_time           = cav->magnify_time;
3783
3784   level->wind_direction_initial =
3785     map_direction_EM_to_RND(cav->wind_direction);
3786
3787   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3788     for (j = 0; j < 8; j++)
3789       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3790         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3791
3792   map_android_clone_elements_EM_to_RND(level);
3793
3794   // convert the playfield (some elements need special treatment)
3795   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3796   {
3797     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3798
3799     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3800       new_element = EL_AMOEBA_DEAD;
3801
3802     level->field[x][y] = new_element;
3803   }
3804
3805   for (i = 0; i < MAX_PLAYERS; i++)
3806   {
3807     // in case of all players set to the same field, use the first player
3808     int nr = MAX_PLAYERS - i - 1;
3809     int jx = cav->player_x[nr];
3810     int jy = cav->player_y[nr];
3811
3812     if (jx != -1 && jy != -1)
3813       level->field[jx][jy] = EL_PLAYER_1 + nr;
3814   }
3815
3816   // time score is counted for each 10 seconds left in Emerald Mine levels
3817   level->time_score_base = 10;
3818 }
3819
3820
3821 // ----------------------------------------------------------------------------
3822 // functions for loading SP level
3823 // ----------------------------------------------------------------------------
3824
3825 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3826 {
3827   struct LevelInfo_SP *level_sp = level->native_sp_level;
3828   LevelInfoType *header = &level_sp->header;
3829   int i, x, y;
3830
3831   level_sp->width  = level->fieldx;
3832   level_sp->height = level->fieldy;
3833
3834   for (x = 0; x < level->fieldx; x++)
3835     for (y = 0; y < level->fieldy; y++)
3836       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3837
3838   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3839
3840   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3841     header->LevelTitle[i] = level->name[i];
3842   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3843
3844   header->InfotronsNeeded = level->gems_needed;
3845
3846   header->SpecialPortCount = 0;
3847
3848   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3849   {
3850     boolean gravity_port_found = FALSE;
3851     boolean gravity_port_valid = FALSE;
3852     int gravity_port_flag;
3853     int gravity_port_base_element;
3854     int element = level->field[x][y];
3855
3856     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3857         element <= EL_SP_GRAVITY_ON_PORT_UP)
3858     {
3859       gravity_port_found = TRUE;
3860       gravity_port_valid = TRUE;
3861       gravity_port_flag = 1;
3862       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3863     }
3864     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3865              element <= EL_SP_GRAVITY_OFF_PORT_UP)
3866     {
3867       gravity_port_found = TRUE;
3868       gravity_port_valid = TRUE;
3869       gravity_port_flag = 0;
3870       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3871     }
3872     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3873              element <= EL_SP_GRAVITY_PORT_UP)
3874     {
3875       // change R'n'D style gravity inverting special port to normal port
3876       // (there are no gravity inverting ports in native Supaplex engine)
3877
3878       gravity_port_found = TRUE;
3879       gravity_port_valid = FALSE;
3880       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3881     }
3882
3883     if (gravity_port_found)
3884     {
3885       if (gravity_port_valid &&
3886           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3887       {
3888         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3889
3890         port->PortLocation = (y * level->fieldx + x) * 2;
3891         port->Gravity = gravity_port_flag;
3892
3893         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3894
3895         header->SpecialPortCount++;
3896       }
3897       else
3898       {
3899         // change special gravity port to normal port
3900
3901         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3902       }
3903
3904       level_sp->playfield[x][y] = element - EL_SP_START;
3905     }
3906   }
3907 }
3908
3909 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3910 {
3911   struct LevelInfo_SP *level_sp = level->native_sp_level;
3912   LevelInfoType *header = &level_sp->header;
3913   boolean num_invalid_elements = 0;
3914   int i, j, x, y;
3915
3916   level->fieldx = level_sp->width;
3917   level->fieldy = level_sp->height;
3918
3919   for (x = 0; x < level->fieldx; x++)
3920   {
3921     for (y = 0; y < level->fieldy; y++)
3922     {
3923       int element_old = level_sp->playfield[x][y];
3924       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3925
3926       if (element_new == EL_UNKNOWN)
3927       {
3928         num_invalid_elements++;
3929
3930         Debug("level:native:SP", "invalid element %d at position %d, %d",
3931               element_old, x, y);
3932       }
3933
3934       level->field[x][y] = element_new;
3935     }
3936   }
3937
3938   if (num_invalid_elements > 0)
3939     Warn("found %d invalid elements%s", num_invalid_elements,
3940          (!options.debug ? " (use '--debug' for more details)" : ""));
3941
3942   for (i = 0; i < MAX_PLAYERS; i++)
3943     level->initial_player_gravity[i] =
3944       (header->InitialGravity == 1 ? TRUE : FALSE);
3945
3946   // skip leading spaces
3947   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3948     if (header->LevelTitle[i] != ' ')
3949       break;
3950
3951   // copy level title
3952   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3953     level->name[j] = header->LevelTitle[i];
3954   level->name[j] = '\0';
3955
3956   // cut trailing spaces
3957   for (; j > 0; j--)
3958     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3959       level->name[j - 1] = '\0';
3960
3961   level->gems_needed = header->InfotronsNeeded;
3962
3963   for (i = 0; i < header->SpecialPortCount; i++)
3964   {
3965     SpecialPortType *port = &header->SpecialPort[i];
3966     int port_location = port->PortLocation;
3967     int gravity = port->Gravity;
3968     int port_x, port_y, port_element;
3969
3970     port_x = (port_location / 2) % level->fieldx;
3971     port_y = (port_location / 2) / level->fieldx;
3972
3973     if (port_x < 0 || port_x >= level->fieldx ||
3974         port_y < 0 || port_y >= level->fieldy)
3975     {
3976       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3977
3978       continue;
3979     }
3980
3981     port_element = level->field[port_x][port_y];
3982
3983     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3984         port_element > EL_SP_GRAVITY_PORT_UP)
3985     {
3986       Warn("no special port at position (%d, %d)", port_x, port_y);
3987
3988       continue;
3989     }
3990
3991     // change previous (wrong) gravity inverting special port to either
3992     // gravity enabling special port or gravity disabling special port
3993     level->field[port_x][port_y] +=
3994       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3995        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3996   }
3997
3998   // change special gravity ports without database entries to normal ports
3999   for (x = 0; x < level->fieldx; x++)
4000     for (y = 0; y < level->fieldy; y++)
4001       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4002           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4003         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4004
4005   level->time = 0;                      // no time limit
4006   level->amoeba_speed = 0;
4007   level->time_magic_wall = 0;
4008   level->time_wheel = 0;
4009   level->amoeba_content = EL_EMPTY;
4010
4011   // original Supaplex does not use score values -- rate by playing time
4012   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4013     level->score[i] = 0;
4014
4015   level->rate_time_over_score = TRUE;
4016
4017   // there are no yamyams in supaplex levels
4018   for (i = 0; i < level->num_yamyam_contents; i++)
4019     for (x = 0; x < 3; x++)
4020       for (y = 0; y < 3; y++)
4021         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4022 }
4023
4024 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4025 {
4026   struct LevelInfo_SP *level_sp = level->native_sp_level;
4027   struct DemoInfo_SP *demo = &level_sp->demo;
4028   int i, j;
4029
4030   // always start with reliable default values
4031   demo->is_available = FALSE;
4032   demo->length = 0;
4033
4034   if (TAPE_IS_EMPTY(tape))
4035     return;
4036
4037   demo->level_nr = tape.level_nr;       // (currently not used)
4038
4039   level_sp->header.DemoRandomSeed = tape.random_seed;
4040
4041   demo->length = 0;
4042
4043   for (i = 0; i < tape.length; i++)
4044   {
4045     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4046     int demo_repeat = tape.pos[i].delay;
4047     int demo_entries = (demo_repeat + 15) / 16;
4048
4049     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4050     {
4051       Warn("tape truncated: size exceeds maximum SP demo size %d",
4052            SP_MAX_TAPE_LEN);
4053
4054       break;
4055     }
4056
4057     for (j = 0; j < demo_repeat / 16; j++)
4058       demo->data[demo->length++] = 0xf0 | demo_action;
4059
4060     if (demo_repeat % 16)
4061       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4062   }
4063
4064   demo->is_available = TRUE;
4065 }
4066
4067 static void setTapeInfoToDefaults(void);
4068
4069 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4070 {
4071   struct LevelInfo_SP *level_sp = level->native_sp_level;
4072   struct DemoInfo_SP *demo = &level_sp->demo;
4073   char *filename = level->file_info.filename;
4074   int i;
4075
4076   // always start with reliable default values
4077   setTapeInfoToDefaults();
4078
4079   if (!demo->is_available)
4080     return;
4081
4082   tape.level_nr = demo->level_nr;       // (currently not used)
4083   tape.random_seed = level_sp->header.DemoRandomSeed;
4084
4085   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4086
4087   tape.counter = 0;
4088   tape.pos[tape.counter].delay = 0;
4089
4090   for (i = 0; i < demo->length; i++)
4091   {
4092     int demo_action = demo->data[i] & 0x0f;
4093     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4094     int tape_action = map_key_SP_to_RND(demo_action);
4095     int tape_repeat = demo_repeat + 1;
4096     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4097     boolean success = 0;
4098     int j;
4099
4100     for (j = 0; j < tape_repeat; j++)
4101       success = TapeAddAction(action);
4102
4103     if (!success)
4104     {
4105       Warn("SP demo truncated: size exceeds maximum tape size %d",
4106            MAX_TAPE_LEN);
4107
4108       break;
4109     }
4110   }
4111
4112   TapeHaltRecording();
4113 }
4114
4115
4116 // ----------------------------------------------------------------------------
4117 // functions for loading MM level
4118 // ----------------------------------------------------------------------------
4119
4120 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4121 {
4122   struct LevelInfo_MM *level_mm = level->native_mm_level;
4123   int x, y;
4124
4125   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4126   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4127
4128   level_mm->time = level->time;
4129   level_mm->kettles_needed = level->gems_needed;
4130   level_mm->auto_count_kettles = level->auto_count_gems;
4131
4132   level_mm->laser_red = level->mm_laser_red;
4133   level_mm->laser_green = level->mm_laser_green;
4134   level_mm->laser_blue = level->mm_laser_blue;
4135
4136   strcpy(level_mm->name, level->name);
4137   strcpy(level_mm->author, level->author);
4138
4139   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4140   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4141   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4142   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4143   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4144
4145   level_mm->amoeba_speed = level->amoeba_speed;
4146   level_mm->time_fuse    = level->mm_time_fuse;
4147   level_mm->time_bomb    = level->mm_time_bomb;
4148   level_mm->time_ball    = level->mm_time_ball;
4149   level_mm->time_block   = level->mm_time_block;
4150
4151   for (x = 0; x < level->fieldx; x++)
4152     for (y = 0; y < level->fieldy; y++)
4153       Ur[x][y] =
4154         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4155 }
4156
4157 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4158 {
4159   struct LevelInfo_MM *level_mm = level->native_mm_level;
4160   int x, y;
4161
4162   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4163   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4164
4165   level->time = level_mm->time;
4166   level->gems_needed = level_mm->kettles_needed;
4167   level->auto_count_gems = level_mm->auto_count_kettles;
4168
4169   level->mm_laser_red = level_mm->laser_red;
4170   level->mm_laser_green = level_mm->laser_green;
4171   level->mm_laser_blue = level_mm->laser_blue;
4172
4173   strcpy(level->name, level_mm->name);
4174
4175   // only overwrite author from 'levelinfo.conf' if author defined in level
4176   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4177     strcpy(level->author, level_mm->author);
4178
4179   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4180   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4181   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4182   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4183   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4184
4185   level->amoeba_speed  = level_mm->amoeba_speed;
4186   level->mm_time_fuse  = level_mm->time_fuse;
4187   level->mm_time_bomb  = level_mm->time_bomb;
4188   level->mm_time_ball  = level_mm->time_ball;
4189   level->mm_time_block = level_mm->time_block;
4190
4191   for (x = 0; x < level->fieldx; x++)
4192     for (y = 0; y < level->fieldy; y++)
4193       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4194 }
4195
4196
4197 // ----------------------------------------------------------------------------
4198 // functions for loading DC level
4199 // ----------------------------------------------------------------------------
4200
4201 #define DC_LEVEL_HEADER_SIZE            344
4202
4203 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4204                                         boolean init)
4205 {
4206   static int last_data_encoded;
4207   static int offset1;
4208   static int offset2;
4209   int diff;
4210   int diff_hi, diff_lo;
4211   int data_hi, data_lo;
4212   unsigned short data_decoded;
4213
4214   if (init)
4215   {
4216     last_data_encoded = 0;
4217     offset1 = -1;
4218     offset2 = 0;
4219
4220     return 0;
4221   }
4222
4223   diff = data_encoded - last_data_encoded;
4224   diff_hi = diff & ~0xff;
4225   diff_lo = diff &  0xff;
4226
4227   offset2 += diff_lo;
4228
4229   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4230   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4231   data_hi = data_hi & 0xff00;
4232
4233   data_decoded = data_hi | data_lo;
4234
4235   last_data_encoded = data_encoded;
4236
4237   offset1 = (offset1 + 1) % 31;
4238   offset2 = offset2 & 0xff;
4239
4240   return data_decoded;
4241 }
4242
4243 static int getMappedElement_DC(int element)
4244 {
4245   switch (element)
4246   {
4247     case 0x0000:
4248       element = EL_ROCK;
4249       break;
4250
4251       // 0x0117 - 0x036e: (?)
4252       // EL_DIAMOND
4253
4254       // 0x042d - 0x0684: (?)
4255       // EL_EMERALD
4256
4257     case 0x06f1:
4258       element = EL_NUT;
4259       break;
4260
4261     case 0x074c:
4262       element = EL_BOMB;
4263       break;
4264
4265     case 0x07a4:
4266       element = EL_PEARL;
4267       break;
4268
4269     case 0x0823:
4270       element = EL_CRYSTAL;
4271       break;
4272
4273     case 0x0e77:        // quicksand (boulder)
4274       element = EL_QUICKSAND_FAST_FULL;
4275       break;
4276
4277     case 0x0e99:        // slow quicksand (boulder)
4278       element = EL_QUICKSAND_FULL;
4279       break;
4280
4281     case 0x0ed2:
4282       element = EL_EM_EXIT_OPEN;
4283       break;
4284
4285     case 0x0ee3:
4286       element = EL_EM_EXIT_CLOSED;
4287       break;
4288
4289     case 0x0eeb:
4290       element = EL_EM_STEEL_EXIT_OPEN;
4291       break;
4292
4293     case 0x0efc:
4294       element = EL_EM_STEEL_EXIT_CLOSED;
4295       break;
4296
4297     case 0x0f4f:        // dynamite (lit 1)
4298       element = EL_EM_DYNAMITE_ACTIVE;
4299       break;
4300
4301     case 0x0f57:        // dynamite (lit 2)
4302       element = EL_EM_DYNAMITE_ACTIVE;
4303       break;
4304
4305     case 0x0f5f:        // dynamite (lit 3)
4306       element = EL_EM_DYNAMITE_ACTIVE;
4307       break;
4308
4309     case 0x0f67:        // dynamite (lit 4)
4310       element = EL_EM_DYNAMITE_ACTIVE;
4311       break;
4312
4313     case 0x0f81:
4314     case 0x0f82:
4315     case 0x0f83:
4316     case 0x0f84:
4317       element = EL_AMOEBA_WET;
4318       break;
4319
4320     case 0x0f85:
4321       element = EL_AMOEBA_DROP;
4322       break;
4323
4324     case 0x0fb9:
4325       element = EL_DC_MAGIC_WALL;
4326       break;
4327
4328     case 0x0fd0:
4329       element = EL_SPACESHIP_UP;
4330       break;
4331
4332     case 0x0fd9:
4333       element = EL_SPACESHIP_DOWN;
4334       break;
4335
4336     case 0x0ff1:
4337       element = EL_SPACESHIP_LEFT;
4338       break;
4339
4340     case 0x0ff9:
4341       element = EL_SPACESHIP_RIGHT;
4342       break;
4343
4344     case 0x1057:
4345       element = EL_BUG_UP;
4346       break;
4347
4348     case 0x1060:
4349       element = EL_BUG_DOWN;
4350       break;
4351
4352     case 0x1078:
4353       element = EL_BUG_LEFT;
4354       break;
4355
4356     case 0x1080:
4357       element = EL_BUG_RIGHT;
4358       break;
4359
4360     case 0x10de:
4361       element = EL_MOLE_UP;
4362       break;
4363
4364     case 0x10e7:
4365       element = EL_MOLE_DOWN;
4366       break;
4367
4368     case 0x10ff:
4369       element = EL_MOLE_LEFT;
4370       break;
4371
4372     case 0x1107:
4373       element = EL_MOLE_RIGHT;
4374       break;
4375
4376     case 0x11c0:
4377       element = EL_ROBOT;
4378       break;
4379
4380     case 0x13f5:
4381       element = EL_YAMYAM_UP;
4382       break;
4383
4384     case 0x1425:
4385       element = EL_SWITCHGATE_OPEN;
4386       break;
4387
4388     case 0x1426:
4389       element = EL_SWITCHGATE_CLOSED;
4390       break;
4391
4392     case 0x1437:
4393       element = EL_DC_SWITCHGATE_SWITCH_UP;
4394       break;
4395
4396     case 0x143a:
4397       element = EL_TIMEGATE_CLOSED;
4398       break;
4399
4400     case 0x144c:        // conveyor belt switch (green)
4401       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4402       break;
4403
4404     case 0x144f:        // conveyor belt switch (red)
4405       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4406       break;
4407
4408     case 0x1452:        // conveyor belt switch (blue)
4409       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4410       break;
4411
4412     case 0x145b:
4413       element = EL_CONVEYOR_BELT_3_MIDDLE;
4414       break;
4415
4416     case 0x1463:
4417       element = EL_CONVEYOR_BELT_3_LEFT;
4418       break;
4419
4420     case 0x146b:
4421       element = EL_CONVEYOR_BELT_3_RIGHT;
4422       break;
4423
4424     case 0x1473:
4425       element = EL_CONVEYOR_BELT_1_MIDDLE;
4426       break;
4427
4428     case 0x147b:
4429       element = EL_CONVEYOR_BELT_1_LEFT;
4430       break;
4431
4432     case 0x1483:
4433       element = EL_CONVEYOR_BELT_1_RIGHT;
4434       break;
4435
4436     case 0x148b:
4437       element = EL_CONVEYOR_BELT_4_MIDDLE;
4438       break;
4439
4440     case 0x1493:
4441       element = EL_CONVEYOR_BELT_4_LEFT;
4442       break;
4443
4444     case 0x149b:
4445       element = EL_CONVEYOR_BELT_4_RIGHT;
4446       break;
4447
4448     case 0x14ac:
4449       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4450       break;
4451
4452     case 0x14bd:
4453       element = EL_EXPANDABLE_WALL_VERTICAL;
4454       break;
4455
4456     case 0x14c6:
4457       element = EL_EXPANDABLE_WALL_ANY;
4458       break;
4459
4460     case 0x14ce:        // growing steel wall (left/right)
4461       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4462       break;
4463
4464     case 0x14df:        // growing steel wall (up/down)
4465       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4466       break;
4467
4468     case 0x14e8:        // growing steel wall (up/down/left/right)
4469       element = EL_EXPANDABLE_STEELWALL_ANY;
4470       break;
4471
4472     case 0x14e9:
4473       element = EL_SHIELD_DEADLY;
4474       break;
4475
4476     case 0x1501:
4477       element = EL_EXTRA_TIME;
4478       break;
4479
4480     case 0x154f:
4481       element = EL_ACID;
4482       break;
4483
4484     case 0x1577:
4485       element = EL_EMPTY_SPACE;
4486       break;
4487
4488     case 0x1578:        // quicksand (empty)
4489       element = EL_QUICKSAND_FAST_EMPTY;
4490       break;
4491
4492     case 0x1579:        // slow quicksand (empty)
4493       element = EL_QUICKSAND_EMPTY;
4494       break;
4495
4496       // 0x157c - 0x158b:
4497       // EL_SAND
4498
4499       // 0x1590 - 0x159f:
4500       // EL_DC_LANDMINE
4501
4502     case 0x15a0:
4503       element = EL_EM_DYNAMITE;
4504       break;
4505
4506     case 0x15a1:        // key (red)
4507       element = EL_EM_KEY_1;
4508       break;
4509
4510     case 0x15a2:        // key (yellow)
4511       element = EL_EM_KEY_2;
4512       break;
4513
4514     case 0x15a3:        // key (blue)
4515       element = EL_EM_KEY_4;
4516       break;
4517
4518     case 0x15a4:        // key (green)
4519       element = EL_EM_KEY_3;
4520       break;
4521
4522     case 0x15a5:        // key (white)
4523       element = EL_DC_KEY_WHITE;
4524       break;
4525
4526     case 0x15a6:
4527       element = EL_WALL_SLIPPERY;
4528       break;
4529
4530     case 0x15a7:
4531       element = EL_WALL;
4532       break;
4533
4534     case 0x15a8:        // wall (not round)
4535       element = EL_WALL;
4536       break;
4537
4538     case 0x15a9:        // (blue)
4539       element = EL_CHAR_A;
4540       break;
4541
4542     case 0x15aa:        // (blue)
4543       element = EL_CHAR_B;
4544       break;
4545
4546     case 0x15ab:        // (blue)
4547       element = EL_CHAR_C;
4548       break;
4549
4550     case 0x15ac:        // (blue)
4551       element = EL_CHAR_D;
4552       break;
4553
4554     case 0x15ad:        // (blue)
4555       element = EL_CHAR_E;
4556       break;
4557
4558     case 0x15ae:        // (blue)
4559       element = EL_CHAR_F;
4560       break;
4561
4562     case 0x15af:        // (blue)
4563       element = EL_CHAR_G;
4564       break;
4565
4566     case 0x15b0:        // (blue)
4567       element = EL_CHAR_H;
4568       break;
4569
4570     case 0x15b1:        // (blue)
4571       element = EL_CHAR_I;
4572       break;
4573
4574     case 0x15b2:        // (blue)
4575       element = EL_CHAR_J;
4576       break;
4577
4578     case 0x15b3:        // (blue)
4579       element = EL_CHAR_K;
4580       break;
4581
4582     case 0x15b4:        // (blue)
4583       element = EL_CHAR_L;
4584       break;
4585
4586     case 0x15b5:        // (blue)
4587       element = EL_CHAR_M;
4588       break;
4589
4590     case 0x15b6:        // (blue)
4591       element = EL_CHAR_N;
4592       break;
4593
4594     case 0x15b7:        // (blue)
4595       element = EL_CHAR_O;
4596       break;
4597
4598     case 0x15b8:        // (blue)
4599       element = EL_CHAR_P;
4600       break;
4601
4602     case 0x15b9:        // (blue)
4603       element = EL_CHAR_Q;
4604       break;
4605
4606     case 0x15ba:        // (blue)
4607       element = EL_CHAR_R;
4608       break;
4609
4610     case 0x15bb:        // (blue)
4611       element = EL_CHAR_S;
4612       break;
4613
4614     case 0x15bc:        // (blue)
4615       element = EL_CHAR_T;
4616       break;
4617
4618     case 0x15bd:        // (blue)
4619       element = EL_CHAR_U;
4620       break;
4621
4622     case 0x15be:        // (blue)
4623       element = EL_CHAR_V;
4624       break;
4625
4626     case 0x15bf:        // (blue)
4627       element = EL_CHAR_W;
4628       break;
4629
4630     case 0x15c0:        // (blue)
4631       element = EL_CHAR_X;
4632       break;
4633
4634     case 0x15c1:        // (blue)
4635       element = EL_CHAR_Y;
4636       break;
4637
4638     case 0x15c2:        // (blue)
4639       element = EL_CHAR_Z;
4640       break;
4641
4642     case 0x15c3:        // (blue)
4643       element = EL_CHAR_AUMLAUT;
4644       break;
4645
4646     case 0x15c4:        // (blue)
4647       element = EL_CHAR_OUMLAUT;
4648       break;
4649
4650     case 0x15c5:        // (blue)
4651       element = EL_CHAR_UUMLAUT;
4652       break;
4653
4654     case 0x15c6:        // (blue)
4655       element = EL_CHAR_0;
4656       break;
4657
4658     case 0x15c7:        // (blue)
4659       element = EL_CHAR_1;
4660       break;
4661
4662     case 0x15c8:        // (blue)
4663       element = EL_CHAR_2;
4664       break;
4665
4666     case 0x15c9:        // (blue)
4667       element = EL_CHAR_3;
4668       break;
4669
4670     case 0x15ca:        // (blue)
4671       element = EL_CHAR_4;
4672       break;
4673
4674     case 0x15cb:        // (blue)
4675       element = EL_CHAR_5;
4676       break;
4677
4678     case 0x15cc:        // (blue)
4679       element = EL_CHAR_6;
4680       break;
4681
4682     case 0x15cd:        // (blue)
4683       element = EL_CHAR_7;
4684       break;
4685
4686     case 0x15ce:        // (blue)
4687       element = EL_CHAR_8;
4688       break;
4689
4690     case 0x15cf:        // (blue)
4691       element = EL_CHAR_9;
4692       break;
4693
4694     case 0x15d0:        // (blue)
4695       element = EL_CHAR_PERIOD;
4696       break;
4697
4698     case 0x15d1:        // (blue)
4699       element = EL_CHAR_EXCLAM;
4700       break;
4701
4702     case 0x15d2:        // (blue)
4703       element = EL_CHAR_COLON;
4704       break;
4705
4706     case 0x15d3:        // (blue)
4707       element = EL_CHAR_LESS;
4708       break;
4709
4710     case 0x15d4:        // (blue)
4711       element = EL_CHAR_GREATER;
4712       break;
4713
4714     case 0x15d5:        // (blue)
4715       element = EL_CHAR_QUESTION;
4716       break;
4717
4718     case 0x15d6:        // (blue)
4719       element = EL_CHAR_COPYRIGHT;
4720       break;
4721
4722     case 0x15d7:        // (blue)
4723       element = EL_CHAR_UP;
4724       break;
4725
4726     case 0x15d8:        // (blue)
4727       element = EL_CHAR_DOWN;
4728       break;
4729
4730     case 0x15d9:        // (blue)
4731       element = EL_CHAR_BUTTON;
4732       break;
4733
4734     case 0x15da:        // (blue)
4735       element = EL_CHAR_PLUS;
4736       break;
4737
4738     case 0x15db:        // (blue)
4739       element = EL_CHAR_MINUS;
4740       break;
4741
4742     case 0x15dc:        // (blue)
4743       element = EL_CHAR_APOSTROPHE;
4744       break;
4745
4746     case 0x15dd:        // (blue)
4747       element = EL_CHAR_PARENLEFT;
4748       break;
4749
4750     case 0x15de:        // (blue)
4751       element = EL_CHAR_PARENRIGHT;
4752       break;
4753
4754     case 0x15df:        // (green)
4755       element = EL_CHAR_A;
4756       break;
4757
4758     case 0x15e0:        // (green)
4759       element = EL_CHAR_B;
4760       break;
4761
4762     case 0x15e1:        // (green)
4763       element = EL_CHAR_C;
4764       break;
4765
4766     case 0x15e2:        // (green)
4767       element = EL_CHAR_D;
4768       break;
4769
4770     case 0x15e3:        // (green)
4771       element = EL_CHAR_E;
4772       break;
4773
4774     case 0x15e4:        // (green)
4775       element = EL_CHAR_F;
4776       break;
4777
4778     case 0x15e5:        // (green)
4779       element = EL_CHAR_G;
4780       break;
4781
4782     case 0x15e6:        // (green)
4783       element = EL_CHAR_H;
4784       break;
4785
4786     case 0x15e7:        // (green)
4787       element = EL_CHAR_I;
4788       break;
4789
4790     case 0x15e8:        // (green)
4791       element = EL_CHAR_J;
4792       break;
4793
4794     case 0x15e9:        // (green)
4795       element = EL_CHAR_K;
4796       break;
4797
4798     case 0x15ea:        // (green)
4799       element = EL_CHAR_L;
4800       break;
4801
4802     case 0x15eb:        // (green)
4803       element = EL_CHAR_M;
4804       break;
4805
4806     case 0x15ec:        // (green)
4807       element = EL_CHAR_N;
4808       break;
4809
4810     case 0x15ed:        // (green)
4811       element = EL_CHAR_O;
4812       break;
4813
4814     case 0x15ee:        // (green)
4815       element = EL_CHAR_P;
4816       break;
4817
4818     case 0x15ef:        // (green)
4819       element = EL_CHAR_Q;
4820       break;
4821
4822     case 0x15f0:        // (green)
4823       element = EL_CHAR_R;
4824       break;
4825
4826     case 0x15f1:        // (green)
4827       element = EL_CHAR_S;
4828       break;
4829
4830     case 0x15f2:        // (green)
4831       element = EL_CHAR_T;
4832       break;
4833
4834     case 0x15f3:        // (green)
4835       element = EL_CHAR_U;
4836       break;
4837
4838     case 0x15f4:        // (green)
4839       element = EL_CHAR_V;
4840       break;
4841
4842     case 0x15f5:        // (green)
4843       element = EL_CHAR_W;
4844       break;
4845
4846     case 0x15f6:        // (green)
4847       element = EL_CHAR_X;
4848       break;
4849
4850     case 0x15f7:        // (green)
4851       element = EL_CHAR_Y;
4852       break;
4853
4854     case 0x15f8:        // (green)
4855       element = EL_CHAR_Z;
4856       break;
4857
4858     case 0x15f9:        // (green)
4859       element = EL_CHAR_AUMLAUT;
4860       break;
4861
4862     case 0x15fa:        // (green)
4863       element = EL_CHAR_OUMLAUT;
4864       break;
4865
4866     case 0x15fb:        // (green)
4867       element = EL_CHAR_UUMLAUT;
4868       break;
4869
4870     case 0x15fc:        // (green)
4871       element = EL_CHAR_0;
4872       break;
4873
4874     case 0x15fd:        // (green)
4875       element = EL_CHAR_1;
4876       break;
4877
4878     case 0x15fe:        // (green)
4879       element = EL_CHAR_2;
4880       break;
4881
4882     case 0x15ff:        // (green)
4883       element = EL_CHAR_3;
4884       break;
4885
4886     case 0x1600:        // (green)
4887       element = EL_CHAR_4;
4888       break;
4889
4890     case 0x1601:        // (green)
4891       element = EL_CHAR_5;
4892       break;
4893
4894     case 0x1602:        // (green)
4895       element = EL_CHAR_6;
4896       break;
4897
4898     case 0x1603:        // (green)
4899       element = EL_CHAR_7;
4900       break;
4901
4902     case 0x1604:        // (green)
4903       element = EL_CHAR_8;
4904       break;
4905
4906     case 0x1605:        // (green)
4907       element = EL_CHAR_9;
4908       break;
4909
4910     case 0x1606:        // (green)
4911       element = EL_CHAR_PERIOD;
4912       break;
4913
4914     case 0x1607:        // (green)
4915       element = EL_CHAR_EXCLAM;
4916       break;
4917
4918     case 0x1608:        // (green)
4919       element = EL_CHAR_COLON;
4920       break;
4921
4922     case 0x1609:        // (green)
4923       element = EL_CHAR_LESS;
4924       break;
4925
4926     case 0x160a:        // (green)
4927       element = EL_CHAR_GREATER;
4928       break;
4929
4930     case 0x160b:        // (green)
4931       element = EL_CHAR_QUESTION;
4932       break;
4933
4934     case 0x160c:        // (green)
4935       element = EL_CHAR_COPYRIGHT;
4936       break;
4937
4938     case 0x160d:        // (green)
4939       element = EL_CHAR_UP;
4940       break;
4941
4942     case 0x160e:        // (green)
4943       element = EL_CHAR_DOWN;
4944       break;
4945
4946     case 0x160f:        // (green)
4947       element = EL_CHAR_BUTTON;
4948       break;
4949
4950     case 0x1610:        // (green)
4951       element = EL_CHAR_PLUS;
4952       break;
4953
4954     case 0x1611:        // (green)
4955       element = EL_CHAR_MINUS;
4956       break;
4957
4958     case 0x1612:        // (green)
4959       element = EL_CHAR_APOSTROPHE;
4960       break;
4961
4962     case 0x1613:        // (green)
4963       element = EL_CHAR_PARENLEFT;
4964       break;
4965
4966     case 0x1614:        // (green)
4967       element = EL_CHAR_PARENRIGHT;
4968       break;
4969
4970     case 0x1615:        // (blue steel)
4971       element = EL_STEEL_CHAR_A;
4972       break;
4973
4974     case 0x1616:        // (blue steel)
4975       element = EL_STEEL_CHAR_B;
4976       break;
4977
4978     case 0x1617:        // (blue steel)
4979       element = EL_STEEL_CHAR_C;
4980       break;
4981
4982     case 0x1618:        // (blue steel)
4983       element = EL_STEEL_CHAR_D;
4984       break;
4985
4986     case 0x1619:        // (blue steel)
4987       element = EL_STEEL_CHAR_E;
4988       break;
4989
4990     case 0x161a:        // (blue steel)
4991       element = EL_STEEL_CHAR_F;
4992       break;
4993
4994     case 0x161b:        // (blue steel)
4995       element = EL_STEEL_CHAR_G;
4996       break;
4997
4998     case 0x161c:        // (blue steel)
4999       element = EL_STEEL_CHAR_H;
5000       break;
5001
5002     case 0x161d:        // (blue steel)
5003       element = EL_STEEL_CHAR_I;
5004       break;
5005
5006     case 0x161e:        // (blue steel)
5007       element = EL_STEEL_CHAR_J;
5008       break;
5009
5010     case 0x161f:        // (blue steel)
5011       element = EL_STEEL_CHAR_K;
5012       break;
5013
5014     case 0x1620:        // (blue steel)
5015       element = EL_STEEL_CHAR_L;
5016       break;
5017
5018     case 0x1621:        // (blue steel)
5019       element = EL_STEEL_CHAR_M;
5020       break;
5021
5022     case 0x1622:        // (blue steel)
5023       element = EL_STEEL_CHAR_N;
5024       break;
5025
5026     case 0x1623:        // (blue steel)
5027       element = EL_STEEL_CHAR_O;
5028       break;
5029
5030     case 0x1624:        // (blue steel)
5031       element = EL_STEEL_CHAR_P;
5032       break;
5033
5034     case 0x1625:        // (blue steel)
5035       element = EL_STEEL_CHAR_Q;
5036       break;
5037
5038     case 0x1626:        // (blue steel)
5039       element = EL_STEEL_CHAR_R;
5040       break;
5041
5042     case 0x1627:        // (blue steel)
5043       element = EL_STEEL_CHAR_S;
5044       break;
5045
5046     case 0x1628:        // (blue steel)
5047       element = EL_STEEL_CHAR_T;
5048       break;
5049
5050     case 0x1629:        // (blue steel)
5051       element = EL_STEEL_CHAR_U;
5052       break;
5053
5054     case 0x162a:        // (blue steel)
5055       element = EL_STEEL_CHAR_V;
5056       break;
5057
5058     case 0x162b:        // (blue steel)
5059       element = EL_STEEL_CHAR_W;
5060       break;
5061
5062     case 0x162c:        // (blue steel)
5063       element = EL_STEEL_CHAR_X;
5064       break;
5065
5066     case 0x162d:        // (blue steel)
5067       element = EL_STEEL_CHAR_Y;
5068       break;
5069
5070     case 0x162e:        // (blue steel)
5071       element = EL_STEEL_CHAR_Z;
5072       break;
5073
5074     case 0x162f:        // (blue steel)
5075       element = EL_STEEL_CHAR_AUMLAUT;
5076       break;
5077
5078     case 0x1630:        // (blue steel)
5079       element = EL_STEEL_CHAR_OUMLAUT;
5080       break;
5081
5082     case 0x1631:        // (blue steel)
5083       element = EL_STEEL_CHAR_UUMLAUT;
5084       break;
5085
5086     case 0x1632:        // (blue steel)
5087       element = EL_STEEL_CHAR_0;
5088       break;
5089
5090     case 0x1633:        // (blue steel)
5091       element = EL_STEEL_CHAR_1;
5092       break;
5093
5094     case 0x1634:        // (blue steel)
5095       element = EL_STEEL_CHAR_2;
5096       break;
5097
5098     case 0x1635:        // (blue steel)
5099       element = EL_STEEL_CHAR_3;
5100       break;
5101
5102     case 0x1636:        // (blue steel)
5103       element = EL_STEEL_CHAR_4;
5104       break;
5105
5106     case 0x1637:        // (blue steel)
5107       element = EL_STEEL_CHAR_5;
5108       break;
5109
5110     case 0x1638:        // (blue steel)
5111       element = EL_STEEL_CHAR_6;
5112       break;
5113
5114     case 0x1639:        // (blue steel)
5115       element = EL_STEEL_CHAR_7;
5116       break;
5117
5118     case 0x163a:        // (blue steel)
5119       element = EL_STEEL_CHAR_8;
5120       break;
5121
5122     case 0x163b:        // (blue steel)
5123       element = EL_STEEL_CHAR_9;
5124       break;
5125
5126     case 0x163c:        // (blue steel)
5127       element = EL_STEEL_CHAR_PERIOD;
5128       break;
5129
5130     case 0x163d:        // (blue steel)
5131       element = EL_STEEL_CHAR_EXCLAM;
5132       break;
5133
5134     case 0x163e:        // (blue steel)
5135       element = EL_STEEL_CHAR_COLON;
5136       break;
5137
5138     case 0x163f:        // (blue steel)
5139       element = EL_STEEL_CHAR_LESS;
5140       break;
5141
5142     case 0x1640:        // (blue steel)
5143       element = EL_STEEL_CHAR_GREATER;
5144       break;
5145
5146     case 0x1641:        // (blue steel)
5147       element = EL_STEEL_CHAR_QUESTION;
5148       break;
5149
5150     case 0x1642:        // (blue steel)
5151       element = EL_STEEL_CHAR_COPYRIGHT;
5152       break;
5153
5154     case 0x1643:        // (blue steel)
5155       element = EL_STEEL_CHAR_UP;
5156       break;
5157
5158     case 0x1644:        // (blue steel)
5159       element = EL_STEEL_CHAR_DOWN;
5160       break;
5161
5162     case 0x1645:        // (blue steel)
5163       element = EL_STEEL_CHAR_BUTTON;
5164       break;
5165
5166     case 0x1646:        // (blue steel)
5167       element = EL_STEEL_CHAR_PLUS;
5168       break;
5169
5170     case 0x1647:        // (blue steel)
5171       element = EL_STEEL_CHAR_MINUS;
5172       break;
5173
5174     case 0x1648:        // (blue steel)
5175       element = EL_STEEL_CHAR_APOSTROPHE;
5176       break;
5177
5178     case 0x1649:        // (blue steel)
5179       element = EL_STEEL_CHAR_PARENLEFT;
5180       break;
5181
5182     case 0x164a:        // (blue steel)
5183       element = EL_STEEL_CHAR_PARENRIGHT;
5184       break;
5185
5186     case 0x164b:        // (green steel)
5187       element = EL_STEEL_CHAR_A;
5188       break;
5189
5190     case 0x164c:        // (green steel)
5191       element = EL_STEEL_CHAR_B;
5192       break;
5193
5194     case 0x164d:        // (green steel)
5195       element = EL_STEEL_CHAR_C;
5196       break;
5197
5198     case 0x164e:        // (green steel)
5199       element = EL_STEEL_CHAR_D;
5200       break;
5201
5202     case 0x164f:        // (green steel)
5203       element = EL_STEEL_CHAR_E;
5204       break;
5205
5206     case 0x1650:        // (green steel)
5207       element = EL_STEEL_CHAR_F;
5208       break;
5209
5210     case 0x1651:        // (green steel)
5211       element = EL_STEEL_CHAR_G;
5212       break;
5213
5214     case 0x1652:        // (green steel)
5215       element = EL_STEEL_CHAR_H;
5216       break;
5217
5218     case 0x1653:        // (green steel)
5219       element = EL_STEEL_CHAR_I;
5220       break;
5221
5222     case 0x1654:        // (green steel)
5223       element = EL_STEEL_CHAR_J;
5224       break;
5225
5226     case 0x1655:        // (green steel)
5227       element = EL_STEEL_CHAR_K;
5228       break;
5229
5230     case 0x1656:        // (green steel)
5231       element = EL_STEEL_CHAR_L;
5232       break;
5233
5234     case 0x1657:        // (green steel)
5235       element = EL_STEEL_CHAR_M;
5236       break;
5237
5238     case 0x1658:        // (green steel)
5239       element = EL_STEEL_CHAR_N;
5240       break;
5241
5242     case 0x1659:        // (green steel)
5243       element = EL_STEEL_CHAR_O;
5244       break;
5245
5246     case 0x165a:        // (green steel)
5247       element = EL_STEEL_CHAR_P;
5248       break;
5249
5250     case 0x165b:        // (green steel)
5251       element = EL_STEEL_CHAR_Q;
5252       break;
5253
5254     case 0x165c:        // (green steel)
5255       element = EL_STEEL_CHAR_R;
5256       break;
5257
5258     case 0x165d:        // (green steel)
5259       element = EL_STEEL_CHAR_S;
5260       break;
5261
5262     case 0x165e:        // (green steel)
5263       element = EL_STEEL_CHAR_T;
5264       break;
5265
5266     case 0x165f:        // (green steel)
5267       element = EL_STEEL_CHAR_U;
5268       break;
5269
5270     case 0x1660:        // (green steel)
5271       element = EL_STEEL_CHAR_V;
5272       break;
5273
5274     case 0x1661:        // (green steel)
5275       element = EL_STEEL_CHAR_W;
5276       break;
5277
5278     case 0x1662:        // (green steel)
5279       element = EL_STEEL_CHAR_X;
5280       break;
5281
5282     case 0x1663:        // (green steel)
5283       element = EL_STEEL_CHAR_Y;
5284       break;
5285
5286     case 0x1664:        // (green steel)
5287       element = EL_STEEL_CHAR_Z;
5288       break;
5289
5290     case 0x1665:        // (green steel)
5291       element = EL_STEEL_CHAR_AUMLAUT;
5292       break;
5293
5294     case 0x1666:        // (green steel)
5295       element = EL_STEEL_CHAR_OUMLAUT;
5296       break;
5297
5298     case 0x1667:        // (green steel)
5299       element = EL_STEEL_CHAR_UUMLAUT;
5300       break;
5301
5302     case 0x1668:        // (green steel)
5303       element = EL_STEEL_CHAR_0;
5304       break;
5305
5306     case 0x1669:        // (green steel)
5307       element = EL_STEEL_CHAR_1;
5308       break;
5309
5310     case 0x166a:        // (green steel)
5311       element = EL_STEEL_CHAR_2;
5312       break;
5313
5314     case 0x166b:        // (green steel)
5315       element = EL_STEEL_CHAR_3;
5316       break;
5317
5318     case 0x166c:        // (green steel)
5319       element = EL_STEEL_CHAR_4;
5320       break;
5321
5322     case 0x166d:        // (green steel)
5323       element = EL_STEEL_CHAR_5;
5324       break;
5325
5326     case 0x166e:        // (green steel)
5327       element = EL_STEEL_CHAR_6;
5328       break;
5329
5330     case 0x166f:        // (green steel)
5331       element = EL_STEEL_CHAR_7;
5332       break;
5333
5334     case 0x1670:        // (green steel)
5335       element = EL_STEEL_CHAR_8;
5336       break;
5337
5338     case 0x1671:        // (green steel)
5339       element = EL_STEEL_CHAR_9;
5340       break;
5341
5342     case 0x1672:        // (green steel)
5343       element = EL_STEEL_CHAR_PERIOD;
5344       break;
5345
5346     case 0x1673:        // (green steel)
5347       element = EL_STEEL_CHAR_EXCLAM;
5348       break;
5349
5350     case 0x1674:        // (green steel)
5351       element = EL_STEEL_CHAR_COLON;
5352       break;
5353
5354     case 0x1675:        // (green steel)
5355       element = EL_STEEL_CHAR_LESS;
5356       break;
5357
5358     case 0x1676:        // (green steel)
5359       element = EL_STEEL_CHAR_GREATER;
5360       break;
5361
5362     case 0x1677:        // (green steel)
5363       element = EL_STEEL_CHAR_QUESTION;
5364       break;
5365
5366     case 0x1678:        // (green steel)
5367       element = EL_STEEL_CHAR_COPYRIGHT;
5368       break;
5369
5370     case 0x1679:        // (green steel)
5371       element = EL_STEEL_CHAR_UP;
5372       break;
5373
5374     case 0x167a:        // (green steel)
5375       element = EL_STEEL_CHAR_DOWN;
5376       break;
5377
5378     case 0x167b:        // (green steel)
5379       element = EL_STEEL_CHAR_BUTTON;
5380       break;
5381
5382     case 0x167c:        // (green steel)
5383       element = EL_STEEL_CHAR_PLUS;
5384       break;
5385
5386     case 0x167d:        // (green steel)
5387       element = EL_STEEL_CHAR_MINUS;
5388       break;
5389
5390     case 0x167e:        // (green steel)
5391       element = EL_STEEL_CHAR_APOSTROPHE;
5392       break;
5393
5394     case 0x167f:        // (green steel)
5395       element = EL_STEEL_CHAR_PARENLEFT;
5396       break;
5397
5398     case 0x1680:        // (green steel)
5399       element = EL_STEEL_CHAR_PARENRIGHT;
5400       break;
5401
5402     case 0x1681:        // gate (red)
5403       element = EL_EM_GATE_1;
5404       break;
5405
5406     case 0x1682:        // secret gate (red)
5407       element = EL_EM_GATE_1_GRAY;
5408       break;
5409
5410     case 0x1683:        // gate (yellow)
5411       element = EL_EM_GATE_2;
5412       break;
5413
5414     case 0x1684:        // secret gate (yellow)
5415       element = EL_EM_GATE_2_GRAY;
5416       break;
5417
5418     case 0x1685:        // gate (blue)
5419       element = EL_EM_GATE_4;
5420       break;
5421
5422     case 0x1686:        // secret gate (blue)
5423       element = EL_EM_GATE_4_GRAY;
5424       break;
5425
5426     case 0x1687:        // gate (green)
5427       element = EL_EM_GATE_3;
5428       break;
5429
5430     case 0x1688:        // secret gate (green)
5431       element = EL_EM_GATE_3_GRAY;
5432       break;
5433
5434     case 0x1689:        // gate (white)
5435       element = EL_DC_GATE_WHITE;
5436       break;
5437
5438     case 0x168a:        // secret gate (white)
5439       element = EL_DC_GATE_WHITE_GRAY;
5440       break;
5441
5442     case 0x168b:        // secret gate (no key)
5443       element = EL_DC_GATE_FAKE_GRAY;
5444       break;
5445
5446     case 0x168c:
5447       element = EL_ROBOT_WHEEL;
5448       break;
5449
5450     case 0x168d:
5451       element = EL_DC_TIMEGATE_SWITCH;
5452       break;
5453
5454     case 0x168e:
5455       element = EL_ACID_POOL_BOTTOM;
5456       break;
5457
5458     case 0x168f:
5459       element = EL_ACID_POOL_TOPLEFT;
5460       break;
5461
5462     case 0x1690:
5463       element = EL_ACID_POOL_TOPRIGHT;
5464       break;
5465
5466     case 0x1691:
5467       element = EL_ACID_POOL_BOTTOMLEFT;
5468       break;
5469
5470     case 0x1692:
5471       element = EL_ACID_POOL_BOTTOMRIGHT;
5472       break;
5473
5474     case 0x1693:
5475       element = EL_STEELWALL;
5476       break;
5477
5478     case 0x1694:
5479       element = EL_STEELWALL_SLIPPERY;
5480       break;
5481
5482     case 0x1695:        // steel wall (not round)
5483       element = EL_STEELWALL;
5484       break;
5485
5486     case 0x1696:        // steel wall (left)
5487       element = EL_DC_STEELWALL_1_LEFT;
5488       break;
5489
5490     case 0x1697:        // steel wall (bottom)
5491       element = EL_DC_STEELWALL_1_BOTTOM;
5492       break;
5493
5494     case 0x1698:        // steel wall (right)
5495       element = EL_DC_STEELWALL_1_RIGHT;
5496       break;
5497
5498     case 0x1699:        // steel wall (top)
5499       element = EL_DC_STEELWALL_1_TOP;
5500       break;
5501
5502     case 0x169a:        // steel wall (left/bottom)
5503       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5504       break;
5505
5506     case 0x169b:        // steel wall (right/bottom)
5507       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5508       break;
5509
5510     case 0x169c:        // steel wall (right/top)
5511       element = EL_DC_STEELWALL_1_TOPRIGHT;
5512       break;
5513
5514     case 0x169d:        // steel wall (left/top)
5515       element = EL_DC_STEELWALL_1_TOPLEFT;
5516       break;
5517
5518     case 0x169e:        // steel wall (right/bottom small)
5519       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5520       break;
5521
5522     case 0x169f:        // steel wall (left/bottom small)
5523       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5524       break;
5525
5526     case 0x16a0:        // steel wall (right/top small)
5527       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5528       break;
5529
5530     case 0x16a1:        // steel wall (left/top small)
5531       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5532       break;
5533
5534     case 0x16a2:        // steel wall (left/right)
5535       element = EL_DC_STEELWALL_1_VERTICAL;
5536       break;
5537
5538     case 0x16a3:        // steel wall (top/bottom)
5539       element = EL_DC_STEELWALL_1_HORIZONTAL;
5540       break;
5541
5542     case 0x16a4:        // steel wall 2 (left end)
5543       element = EL_DC_STEELWALL_2_LEFT;
5544       break;
5545
5546     case 0x16a5:        // steel wall 2 (right end)
5547       element = EL_DC_STEELWALL_2_RIGHT;
5548       break;
5549
5550     case 0x16a6:        // steel wall 2 (top end)
5551       element = EL_DC_STEELWALL_2_TOP;
5552       break;
5553
5554     case 0x16a7:        // steel wall 2 (bottom end)
5555       element = EL_DC_STEELWALL_2_BOTTOM;
5556       break;
5557
5558     case 0x16a8:        // steel wall 2 (left/right)
5559       element = EL_DC_STEELWALL_2_HORIZONTAL;
5560       break;
5561
5562     case 0x16a9:        // steel wall 2 (up/down)
5563       element = EL_DC_STEELWALL_2_VERTICAL;
5564       break;
5565
5566     case 0x16aa:        // steel wall 2 (mid)
5567       element = EL_DC_STEELWALL_2_MIDDLE;
5568       break;
5569
5570     case 0x16ab:
5571       element = EL_SIGN_EXCLAMATION;
5572       break;
5573
5574     case 0x16ac:
5575       element = EL_SIGN_RADIOACTIVITY;
5576       break;
5577
5578     case 0x16ad:
5579       element = EL_SIGN_STOP;
5580       break;
5581
5582     case 0x16ae:
5583       element = EL_SIGN_WHEELCHAIR;
5584       break;
5585
5586     case 0x16af:
5587       element = EL_SIGN_PARKING;
5588       break;
5589
5590     case 0x16b0:
5591       element = EL_SIGN_NO_ENTRY;
5592       break;
5593
5594     case 0x16b1:
5595       element = EL_SIGN_HEART;
5596       break;
5597
5598     case 0x16b2:
5599       element = EL_SIGN_GIVE_WAY;
5600       break;
5601
5602     case 0x16b3:
5603       element = EL_SIGN_ENTRY_FORBIDDEN;
5604       break;
5605
5606     case 0x16b4:
5607       element = EL_SIGN_EMERGENCY_EXIT;
5608       break;
5609
5610     case 0x16b5:
5611       element = EL_SIGN_YIN_YANG;
5612       break;
5613
5614     case 0x16b6:
5615       element = EL_WALL_EMERALD;
5616       break;
5617
5618     case 0x16b7:
5619       element = EL_WALL_DIAMOND;
5620       break;
5621
5622     case 0x16b8:
5623       element = EL_WALL_PEARL;
5624       break;
5625
5626     case 0x16b9:
5627       element = EL_WALL_CRYSTAL;
5628       break;
5629
5630     case 0x16ba:
5631       element = EL_INVISIBLE_WALL;
5632       break;
5633
5634     case 0x16bb:
5635       element = EL_INVISIBLE_STEELWALL;
5636       break;
5637
5638       // 0x16bc - 0x16cb:
5639       // EL_INVISIBLE_SAND
5640
5641     case 0x16cc:
5642       element = EL_LIGHT_SWITCH;
5643       break;
5644
5645     case 0x16cd:
5646       element = EL_ENVELOPE_1;
5647       break;
5648
5649     default:
5650       if (element >= 0x0117 && element <= 0x036e)       // (?)
5651         element = EL_DIAMOND;
5652       else if (element >= 0x042d && element <= 0x0684)  // (?)
5653         element = EL_EMERALD;
5654       else if (element >= 0x157c && element <= 0x158b)
5655         element = EL_SAND;
5656       else if (element >= 0x1590 && element <= 0x159f)
5657         element = EL_DC_LANDMINE;
5658       else if (element >= 0x16bc && element <= 0x16cb)
5659         element = EL_INVISIBLE_SAND;
5660       else
5661       {
5662         Warn("unknown Diamond Caves element 0x%04x", element);
5663
5664         element = EL_UNKNOWN;
5665       }
5666       break;
5667   }
5668
5669   return getMappedElement(element);
5670 }
5671
5672 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5673                                        int nr)
5674 {
5675   byte header[DC_LEVEL_HEADER_SIZE];
5676   int envelope_size;
5677   int envelope_header_pos = 62;
5678   int envelope_content_pos = 94;
5679   int level_name_pos = 251;
5680   int level_author_pos = 292;
5681   int envelope_header_len;
5682   int envelope_content_len;
5683   int level_name_len;
5684   int level_author_len;
5685   int fieldx, fieldy;
5686   int num_yamyam_contents;
5687   int i, x, y;
5688
5689   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5690
5691   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5692   {
5693     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5694
5695     header[i * 2 + 0] = header_word >> 8;
5696     header[i * 2 + 1] = header_word & 0xff;
5697   }
5698
5699   // read some values from level header to check level decoding integrity
5700   fieldx = header[6] | (header[7] << 8);
5701   fieldy = header[8] | (header[9] << 8);
5702   num_yamyam_contents = header[60] | (header[61] << 8);
5703
5704   // do some simple sanity checks to ensure that level was correctly decoded
5705   if (fieldx < 1 || fieldx > 256 ||
5706       fieldy < 1 || fieldy > 256 ||
5707       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5708   {
5709     level->no_valid_file = TRUE;
5710
5711     Warn("cannot decode level from stream -- using empty level");
5712
5713     return;
5714   }
5715
5716   // maximum envelope header size is 31 bytes
5717   envelope_header_len   = header[envelope_header_pos];
5718   // maximum envelope content size is 110 (156?) bytes
5719   envelope_content_len  = header[envelope_content_pos];
5720
5721   // maximum level title size is 40 bytes
5722   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5723   // maximum level author size is 30 (51?) bytes
5724   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5725
5726   envelope_size = 0;
5727
5728   for (i = 0; i < envelope_header_len; i++)
5729     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5730       level->envelope[0].text[envelope_size++] =
5731         header[envelope_header_pos + 1 + i];
5732
5733   if (envelope_header_len > 0 && envelope_content_len > 0)
5734   {
5735     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5736       level->envelope[0].text[envelope_size++] = '\n';
5737     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5738       level->envelope[0].text[envelope_size++] = '\n';
5739   }
5740
5741   for (i = 0; i < envelope_content_len; i++)
5742     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5743       level->envelope[0].text[envelope_size++] =
5744         header[envelope_content_pos + 1 + i];
5745
5746   level->envelope[0].text[envelope_size] = '\0';
5747
5748   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5749   level->envelope[0].ysize = 10;
5750   level->envelope[0].autowrap = TRUE;
5751   level->envelope[0].centered = TRUE;
5752
5753   for (i = 0; i < level_name_len; i++)
5754     level->name[i] = header[level_name_pos + 1 + i];
5755   level->name[level_name_len] = '\0';
5756
5757   for (i = 0; i < level_author_len; i++)
5758     level->author[i] = header[level_author_pos + 1 + i];
5759   level->author[level_author_len] = '\0';
5760
5761   num_yamyam_contents = header[60] | (header[61] << 8);
5762   level->num_yamyam_contents =
5763     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5764
5765   for (i = 0; i < num_yamyam_contents; i++)
5766   {
5767     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5768     {
5769       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5770       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5771
5772       if (i < MAX_ELEMENT_CONTENTS)
5773         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5774     }
5775   }
5776
5777   fieldx = header[6] | (header[7] << 8);
5778   fieldy = header[8] | (header[9] << 8);
5779   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5780   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5781
5782   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5783   {
5784     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5785     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5786
5787     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5788       level->field[x][y] = getMappedElement_DC(element_dc);
5789   }
5790
5791   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5792   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5793   level->field[x][y] = EL_PLAYER_1;
5794
5795   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5796   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5797   level->field[x][y] = EL_PLAYER_2;
5798
5799   level->gems_needed            = header[18] | (header[19] << 8);
5800
5801   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5802   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5803   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5804   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5805   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5806   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5807   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5808   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5809   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5810   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5811   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5812   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5813
5814   level->time                   = header[44] | (header[45] << 8);
5815
5816   level->amoeba_speed           = header[46] | (header[47] << 8);
5817   level->time_light             = header[48] | (header[49] << 8);
5818   level->time_timegate          = header[50] | (header[51] << 8);
5819   level->time_wheel             = header[52] | (header[53] << 8);
5820   level->time_magic_wall        = header[54] | (header[55] << 8);
5821   level->extra_time             = header[56] | (header[57] << 8);
5822   level->shield_normal_time     = header[58] | (header[59] << 8);
5823
5824   // shield and extra time elements do not have a score
5825   level->score[SC_SHIELD]       = 0;
5826   level->extra_time_score       = 0;
5827
5828   // set time for normal and deadly shields to the same value
5829   level->shield_deadly_time     = level->shield_normal_time;
5830
5831   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5832   // can slip down from flat walls, like normal walls and steel walls
5833   level->em_slippery_gems = TRUE;
5834
5835   // time score is counted for each 10 seconds left in Diamond Caves levels
5836   level->time_score_base = 10;
5837 }
5838
5839 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5840                                      struct LevelFileInfo *level_file_info,
5841                                      boolean level_info_only)
5842 {
5843   char *filename = level_file_info->filename;
5844   File *file;
5845   int num_magic_bytes = 8;
5846   char magic_bytes[num_magic_bytes + 1];
5847   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5848
5849   if (!(file = openFile(filename, MODE_READ)))
5850   {
5851     level->no_valid_file = TRUE;
5852
5853     if (!level_info_only)
5854       Warn("cannot read level '%s' -- using empty level", filename);
5855
5856     return;
5857   }
5858
5859   // fseek(file, 0x0000, SEEK_SET);
5860
5861   if (level_file_info->packed)
5862   {
5863     // read "magic bytes" from start of file
5864     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5865       magic_bytes[0] = '\0';
5866
5867     // check "magic bytes" for correct file format
5868     if (!strPrefix(magic_bytes, "DC2"))
5869     {
5870       level->no_valid_file = TRUE;
5871
5872       Warn("unknown DC level file '%s' -- using empty level", filename);
5873
5874       return;
5875     }
5876
5877     if (strPrefix(magic_bytes, "DC2Win95") ||
5878         strPrefix(magic_bytes, "DC2Win98"))
5879     {
5880       int position_first_level = 0x00fa;
5881       int extra_bytes = 4;
5882       int skip_bytes;
5883
5884       // advance file stream to first level inside the level package
5885       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5886
5887       // each block of level data is followed by block of non-level data
5888       num_levels_to_skip *= 2;
5889
5890       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5891       while (num_levels_to_skip >= 0)
5892       {
5893         // advance file stream to next level inside the level package
5894         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5895         {
5896           level->no_valid_file = TRUE;
5897
5898           Warn("cannot fseek in file '%s' -- using empty level", filename);
5899
5900           return;
5901         }
5902
5903         // skip apparently unused extra bytes following each level
5904         ReadUnusedBytesFromFile(file, extra_bytes);
5905
5906         // read size of next level in level package
5907         skip_bytes = getFile32BitLE(file);
5908
5909         num_levels_to_skip--;
5910       }
5911     }
5912     else
5913     {
5914       level->no_valid_file = TRUE;
5915
5916       Warn("unknown DC2 level file '%s' -- using empty level", filename);
5917
5918       return;
5919     }
5920   }
5921
5922   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5923
5924   closeFile(file);
5925 }
5926
5927
5928 // ----------------------------------------------------------------------------
5929 // functions for loading SB level
5930 // ----------------------------------------------------------------------------
5931
5932 int getMappedElement_SB(int element_ascii, boolean use_ces)
5933 {
5934   static struct
5935   {
5936     int ascii;
5937     int sb;
5938     int ce;
5939   }
5940   sb_element_mapping[] =
5941   {
5942     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
5943     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
5944     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
5945     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
5946     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
5947     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
5948     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
5949     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
5950
5951     { 0,   -1,                      -1          },
5952   };
5953
5954   int i;
5955
5956   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5957     if (element_ascii == sb_element_mapping[i].ascii)
5958       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5959
5960   return EL_UNDEFINED;
5961 }
5962
5963 static void SetLevelSettings_SB(struct LevelInfo *level)
5964 {
5965   // time settings
5966   level->time = 0;
5967   level->use_step_counter = TRUE;
5968
5969   // score settings
5970   level->score[SC_TIME_BONUS] = 0;
5971   level->time_score_base = 1;
5972   level->rate_time_over_score = TRUE;
5973
5974   // game settings
5975   level->auto_exit_sokoban = TRUE;
5976 }
5977
5978 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5979                                      struct LevelFileInfo *level_file_info,
5980                                      boolean level_info_only)
5981 {
5982   char *filename = level_file_info->filename;
5983   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5984   char last_comment[MAX_LINE_LEN];
5985   char level_name[MAX_LINE_LEN];
5986   char *line_ptr;
5987   File *file;
5988   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5989   boolean read_continued_line = FALSE;
5990   boolean reading_playfield = FALSE;
5991   boolean got_valid_playfield_line = FALSE;
5992   boolean invalid_playfield_char = FALSE;
5993   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5994   int file_level_nr = 0;
5995   int line_nr = 0;
5996   int x = 0, y = 0;             // initialized to make compilers happy
5997
5998   last_comment[0] = '\0';
5999   level_name[0] = '\0';
6000
6001   if (!(file = openFile(filename, MODE_READ)))
6002   {
6003     level->no_valid_file = TRUE;
6004
6005     if (!level_info_only)
6006       Warn("cannot read level '%s' -- using empty level", filename);
6007
6008     return;
6009   }
6010
6011   while (!checkEndOfFile(file))
6012   {
6013     // level successfully read, but next level may follow here
6014     if (!got_valid_playfield_line && reading_playfield)
6015     {
6016       // read playfield from single level file -- skip remaining file
6017       if (!level_file_info->packed)
6018         break;
6019
6020       if (file_level_nr >= num_levels_to_skip)
6021         break;
6022
6023       file_level_nr++;
6024
6025       last_comment[0] = '\0';
6026       level_name[0] = '\0';
6027
6028       reading_playfield = FALSE;
6029     }
6030
6031     got_valid_playfield_line = FALSE;
6032
6033     // read next line of input file
6034     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6035       break;
6036
6037     // check if line was completely read and is terminated by line break
6038     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6039       line_nr++;
6040
6041     // cut trailing line break (this can be newline and/or carriage return)
6042     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6043       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6044         *line_ptr = '\0';
6045
6046     // copy raw input line for later use (mainly debugging output)
6047     strcpy(line_raw, line);
6048
6049     if (read_continued_line)
6050     {
6051       // append new line to existing line, if there is enough space
6052       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6053         strcat(previous_line, line_ptr);
6054
6055       strcpy(line, previous_line);      // copy storage buffer to line
6056
6057       read_continued_line = FALSE;
6058     }
6059
6060     // if the last character is '\', continue at next line
6061     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6062     {
6063       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6064       strcpy(previous_line, line);      // copy line to storage buffer
6065
6066       read_continued_line = TRUE;
6067
6068       continue;
6069     }
6070
6071     // skip empty lines
6072     if (line[0] == '\0')
6073       continue;
6074
6075     // extract comment text from comment line
6076     if (line[0] == ';')
6077     {
6078       for (line_ptr = line; *line_ptr; line_ptr++)
6079         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6080           break;
6081
6082       strcpy(last_comment, line_ptr);
6083
6084       continue;
6085     }
6086
6087     // extract level title text from line containing level title
6088     if (line[0] == '\'')
6089     {
6090       strcpy(level_name, &line[1]);
6091
6092       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6093         level_name[strlen(level_name) - 1] = '\0';
6094
6095       continue;
6096     }
6097
6098     // skip lines containing only spaces (or empty lines)
6099     for (line_ptr = line; *line_ptr; line_ptr++)
6100       if (*line_ptr != ' ')
6101         break;
6102     if (*line_ptr == '\0')
6103       continue;
6104
6105     // at this point, we have found a line containing part of a playfield
6106
6107     got_valid_playfield_line = TRUE;
6108
6109     if (!reading_playfield)
6110     {
6111       reading_playfield = TRUE;
6112       invalid_playfield_char = FALSE;
6113
6114       for (x = 0; x < MAX_LEV_FIELDX; x++)
6115         for (y = 0; y < MAX_LEV_FIELDY; y++)
6116           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6117
6118       level->fieldx = 0;
6119       level->fieldy = 0;
6120
6121       // start with topmost tile row
6122       y = 0;
6123     }
6124
6125     // skip playfield line if larger row than allowed
6126     if (y >= MAX_LEV_FIELDY)
6127       continue;
6128
6129     // start with leftmost tile column
6130     x = 0;
6131
6132     // read playfield elements from line
6133     for (line_ptr = line; *line_ptr; line_ptr++)
6134     {
6135       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6136
6137       // stop parsing playfield line if larger column than allowed
6138       if (x >= MAX_LEV_FIELDX)
6139         break;
6140
6141       if (mapped_sb_element == EL_UNDEFINED)
6142       {
6143         invalid_playfield_char = TRUE;
6144
6145         break;
6146       }
6147
6148       level->field[x][y] = mapped_sb_element;
6149
6150       // continue with next tile column
6151       x++;
6152
6153       level->fieldx = MAX(x, level->fieldx);
6154     }
6155
6156     if (invalid_playfield_char)
6157     {
6158       // if first playfield line, treat invalid lines as comment lines
6159       if (y == 0)
6160         reading_playfield = FALSE;
6161
6162       continue;
6163     }
6164
6165     // continue with next tile row
6166     y++;
6167   }
6168
6169   closeFile(file);
6170
6171   level->fieldy = y;
6172
6173   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6174   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6175
6176   if (!reading_playfield)
6177   {
6178     level->no_valid_file = TRUE;
6179
6180     Warn("cannot read level '%s' -- using empty level", filename);
6181
6182     return;
6183   }
6184
6185   if (*level_name != '\0')
6186   {
6187     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6188     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6189   }
6190   else if (*last_comment != '\0')
6191   {
6192     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6193     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6194   }
6195   else
6196   {
6197     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6198   }
6199
6200   // set all empty fields beyond the border walls to invisible steel wall
6201   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6202   {
6203     if ((x == 0 || x == level->fieldx - 1 ||
6204          y == 0 || y == level->fieldy - 1) &&
6205         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6206       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6207                      level->field, level->fieldx, level->fieldy);
6208   }
6209
6210   // set special level settings for Sokoban levels
6211   SetLevelSettings_SB(level);
6212
6213   if (load_xsb_to_ces)
6214   {
6215     // special global settings can now be set in level template
6216     level->use_custom_template = TRUE;
6217   }
6218 }
6219
6220
6221 // -------------------------------------------------------------------------
6222 // functions for handling native levels
6223 // -------------------------------------------------------------------------
6224
6225 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6226                                      struct LevelFileInfo *level_file_info,
6227                                      boolean level_info_only)
6228 {
6229   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6230     level->no_valid_file = TRUE;
6231 }
6232
6233 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6234                                      struct LevelFileInfo *level_file_info,
6235                                      boolean level_info_only)
6236 {
6237   int pos = 0;
6238
6239   // determine position of requested level inside level package
6240   if (level_file_info->packed)
6241     pos = level_file_info->nr - leveldir_current->first_level;
6242
6243   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6244     level->no_valid_file = TRUE;
6245 }
6246
6247 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6248                                      struct LevelFileInfo *level_file_info,
6249                                      boolean level_info_only)
6250 {
6251   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6252     level->no_valid_file = TRUE;
6253 }
6254
6255 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6256 {
6257   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6258     CopyNativeLevel_RND_to_EM(level);
6259   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6260     CopyNativeLevel_RND_to_SP(level);
6261   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6262     CopyNativeLevel_RND_to_MM(level);
6263 }
6264
6265 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6266 {
6267   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6268     CopyNativeLevel_EM_to_RND(level);
6269   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6270     CopyNativeLevel_SP_to_RND(level);
6271   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6272     CopyNativeLevel_MM_to_RND(level);
6273 }
6274
6275 void SaveNativeLevel(struct LevelInfo *level)
6276 {
6277   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6278   {
6279     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6280     char *filename = getLevelFilenameFromBasename(basename);
6281
6282     CopyNativeLevel_RND_to_SP(level);
6283     CopyNativeTape_RND_to_SP(level);
6284
6285     SaveNativeLevel_SP(filename);
6286   }
6287 }
6288
6289
6290 // ----------------------------------------------------------------------------
6291 // functions for loading generic level
6292 // ----------------------------------------------------------------------------
6293
6294 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6295                                   struct LevelFileInfo *level_file_info,
6296                                   boolean level_info_only)
6297 {
6298   // always start with reliable default values
6299   setLevelInfoToDefaults(level, level_info_only, TRUE);
6300
6301   switch (level_file_info->type)
6302   {
6303     case LEVEL_FILE_TYPE_RND:
6304       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6305       break;
6306
6307     case LEVEL_FILE_TYPE_EM:
6308       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6309       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6310       break;
6311
6312     case LEVEL_FILE_TYPE_SP:
6313       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6314       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6315       break;
6316
6317     case LEVEL_FILE_TYPE_MM:
6318       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6319       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6320       break;
6321
6322     case LEVEL_FILE_TYPE_DC:
6323       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6324       break;
6325
6326     case LEVEL_FILE_TYPE_SB:
6327       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6328       break;
6329
6330     default:
6331       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6332       break;
6333   }
6334
6335   // if level file is invalid, restore level structure to default values
6336   if (level->no_valid_file)
6337     setLevelInfoToDefaults(level, level_info_only, FALSE);
6338
6339   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6340     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6341
6342   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6343     CopyNativeLevel_Native_to_RND(level);
6344 }
6345
6346 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6347 {
6348   static struct LevelFileInfo level_file_info;
6349
6350   // always start with reliable default values
6351   setFileInfoToDefaults(&level_file_info);
6352
6353   level_file_info.nr = 0;                       // unknown level number
6354   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6355
6356   setString(&level_file_info.filename, filename);
6357
6358   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6359 }
6360
6361 static void LoadLevel_InitVersion(struct LevelInfo *level)
6362 {
6363   int i, j;
6364
6365   if (leveldir_current == NULL)         // only when dumping level
6366     return;
6367
6368   // all engine modifications also valid for levels which use latest engine
6369   if (level->game_version < VERSION_IDENT(3,2,0,5))
6370   {
6371     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6372     level->time_score_base = 10;
6373   }
6374
6375   if (leveldir_current->latest_engine)
6376   {
6377     // ---------- use latest game engine --------------------------------------
6378
6379     /* For all levels which are forced to use the latest game engine version
6380        (normally all but user contributed, private and undefined levels), set
6381        the game engine version to the actual version; this allows for actual
6382        corrections in the game engine to take effect for existing, converted
6383        levels (from "classic" or other existing games) to make the emulation
6384        of the corresponding game more accurate, while (hopefully) not breaking
6385        existing levels created from other players. */
6386
6387     level->game_version = GAME_VERSION_ACTUAL;
6388
6389     /* Set special EM style gems behaviour: EM style gems slip down from
6390        normal, steel and growing wall. As this is a more fundamental change,
6391        it seems better to set the default behaviour to "off" (as it is more
6392        natural) and make it configurable in the level editor (as a property
6393        of gem style elements). Already existing converted levels (neither
6394        private nor contributed levels) are changed to the new behaviour. */
6395
6396     if (level->file_version < FILE_VERSION_2_0)
6397       level->em_slippery_gems = TRUE;
6398
6399     return;
6400   }
6401
6402   // ---------- use game engine the level was created with --------------------
6403
6404   /* For all levels which are not forced to use the latest game engine
6405      version (normally user contributed, private and undefined levels),
6406      use the version of the game engine the levels were created for.
6407
6408      Since 2.0.1, the game engine version is now directly stored
6409      in the level file (chunk "VERS"), so there is no need anymore
6410      to set the game version from the file version (except for old,
6411      pre-2.0 levels, where the game version is still taken from the
6412      file format version used to store the level -- see above). */
6413
6414   // player was faster than enemies in 1.0.0 and before
6415   if (level->file_version == FILE_VERSION_1_0)
6416     for (i = 0; i < MAX_PLAYERS; i++)
6417       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6418
6419   // default behaviour for EM style gems was "slippery" only in 2.0.1
6420   if (level->game_version == VERSION_IDENT(2,0,1,0))
6421     level->em_slippery_gems = TRUE;
6422
6423   // springs could be pushed over pits before (pre-release version) 2.2.0
6424   if (level->game_version < VERSION_IDENT(2,2,0,0))
6425     level->use_spring_bug = TRUE;
6426
6427   if (level->game_version < VERSION_IDENT(3,2,0,5))
6428   {
6429     // time orb caused limited time in endless time levels before 3.2.0-5
6430     level->use_time_orb_bug = TRUE;
6431
6432     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6433     level->block_snap_field = FALSE;
6434
6435     // extra time score was same value as time left score before 3.2.0-5
6436     level->extra_time_score = level->score[SC_TIME_BONUS];
6437   }
6438
6439   if (level->game_version < VERSION_IDENT(3,2,0,7))
6440   {
6441     // default behaviour for snapping was "not continuous" before 3.2.0-7
6442     level->continuous_snapping = FALSE;
6443   }
6444
6445   // only few elements were able to actively move into acid before 3.1.0
6446   // trigger settings did not exist before 3.1.0; set to default "any"
6447   if (level->game_version < VERSION_IDENT(3,1,0,0))
6448   {
6449     // correct "can move into acid" settings (all zero in old levels)
6450
6451     level->can_move_into_acid_bits = 0; // nothing can move into acid
6452     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6453
6454     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6455     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6456     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6457     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6458
6459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6460       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6461
6462     // correct trigger settings (stored as zero == "none" in old levels)
6463
6464     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6465     {
6466       int element = EL_CUSTOM_START + i;
6467       struct ElementInfo *ei = &element_info[element];
6468
6469       for (j = 0; j < ei->num_change_pages; j++)
6470       {
6471         struct ElementChangeInfo *change = &ei->change_page[j];
6472
6473         change->trigger_player = CH_PLAYER_ANY;
6474         change->trigger_page = CH_PAGE_ANY;
6475       }
6476     }
6477   }
6478
6479   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6480   {
6481     int element = EL_CUSTOM_256;
6482     struct ElementInfo *ei = &element_info[element];
6483     struct ElementChangeInfo *change = &ei->change_page[0];
6484
6485     /* This is needed to fix a problem that was caused by a bugfix in function
6486        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6487        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6488        not replace walkable elements, but instead just placed the player on it,
6489        without placing the Sokoban field under the player). Unfortunately, this
6490        breaks "Snake Bite" style levels when the snake is halfway through a door
6491        that just closes (the snake head is still alive and can be moved in this
6492        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6493        player (without Sokoban element) which then gets killed as designed). */
6494
6495     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6496          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6497         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6498       change->target_element = EL_PLAYER_1;
6499   }
6500
6501   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6502   if (level->game_version < VERSION_IDENT(3,2,5,0))
6503   {
6504     /* This is needed to fix a problem that was caused by a bugfix in function
6505        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6506        corrects the behaviour when a custom element changes to another custom
6507        element with a higher element number that has change actions defined.
6508        Normally, only one change per frame is allowed for custom elements.
6509        Therefore, it is checked if a custom element already changed in the
6510        current frame; if it did, subsequent changes are suppressed.
6511        Unfortunately, this is only checked for element changes, but not for
6512        change actions, which are still executed. As the function above loops
6513        through all custom elements from lower to higher, an element change
6514        resulting in a lower CE number won't be checked again, while a target
6515        element with a higher number will also be checked, and potential change
6516        actions will get executed for this CE, too (which is wrong), while
6517        further changes are ignored (which is correct). As this bugfix breaks
6518        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6519        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6520        behaviour for existing levels and tapes that make use of this bug */
6521
6522     level->use_action_after_change_bug = TRUE;
6523   }
6524
6525   // not centering level after relocating player was default only in 3.2.3
6526   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6527     level->shifted_relocation = TRUE;
6528
6529   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6530   if (level->game_version < VERSION_IDENT(3,2,6,0))
6531     level->em_explodes_by_fire = TRUE;
6532
6533   // levels were solved by the first player entering an exit up to 4.1.0.0
6534   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6535     level->solved_by_one_player = TRUE;
6536
6537   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6538   if (level->game_version < VERSION_IDENT(4,1,1,1))
6539     level->use_life_bugs = TRUE;
6540
6541   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6542   if (level->game_version < VERSION_IDENT(4,1,1,1))
6543     level->sb_objects_needed = FALSE;
6544
6545   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6546   if (level->game_version <= VERSION_IDENT(4,2,2,0))
6547     level->finish_dig_collect = FALSE;
6548
6549   // CE changing to player was kept under the player if walkable up to 4.2.3.1
6550   if (level->game_version <= VERSION_IDENT(4,2,3,1))
6551     level->keep_walkable_ce = TRUE;
6552 }
6553
6554 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6555 {
6556   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
6557   int x, y;
6558
6559   // check if this level is (not) a Sokoban level
6560   for (y = 0; y < level->fieldy; y++)
6561     for (x = 0; x < level->fieldx; x++)
6562       if (!IS_SB_ELEMENT(Tile[x][y]))
6563         is_sokoban_level = FALSE;
6564
6565   if (is_sokoban_level)
6566   {
6567     // set special level settings for Sokoban levels
6568     SetLevelSettings_SB(level);
6569   }
6570 }
6571
6572 static void LoadLevel_InitSettings(struct LevelInfo *level)
6573 {
6574   // adjust level settings for (non-native) Sokoban-style levels
6575   LoadLevel_InitSettings_SB(level);
6576
6577   // rename levels with title "nameless level" or if renaming is forced
6578   if (leveldir_current->empty_level_name != NULL &&
6579       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6580        leveldir_current->force_level_name))
6581     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6582              leveldir_current->empty_level_name, level_nr);
6583 }
6584
6585 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6586 {
6587   int i, x, y;
6588
6589   // map elements that have changed in newer versions
6590   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6591                                                     level->game_version);
6592   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6593     for (x = 0; x < 3; x++)
6594       for (y = 0; y < 3; y++)
6595         level->yamyam_content[i].e[x][y] =
6596           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6597                                     level->game_version);
6598
6599 }
6600
6601 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6602 {
6603   int i, j;
6604
6605   // map custom element change events that have changed in newer versions
6606   // (these following values were accidentally changed in version 3.0.1)
6607   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6608   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6609   {
6610     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6611     {
6612       int element = EL_CUSTOM_START + i;
6613
6614       // order of checking and copying events to be mapped is important
6615       // (do not change the start and end value -- they are constant)
6616       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6617       {
6618         if (HAS_CHANGE_EVENT(element, j - 2))
6619         {
6620           SET_CHANGE_EVENT(element, j - 2, FALSE);
6621           SET_CHANGE_EVENT(element, j, TRUE);
6622         }
6623       }
6624
6625       // order of checking and copying events to be mapped is important
6626       // (do not change the start and end value -- they are constant)
6627       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6628       {
6629         if (HAS_CHANGE_EVENT(element, j - 1))
6630         {
6631           SET_CHANGE_EVENT(element, j - 1, FALSE);
6632           SET_CHANGE_EVENT(element, j, TRUE);
6633         }
6634       }
6635     }
6636   }
6637
6638   // initialize "can_change" field for old levels with only one change page
6639   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6640   {
6641     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6642     {
6643       int element = EL_CUSTOM_START + i;
6644
6645       if (CAN_CHANGE(element))
6646         element_info[element].change->can_change = TRUE;
6647     }
6648   }
6649
6650   // correct custom element values (for old levels without these options)
6651   if (level->game_version < VERSION_IDENT(3,1,1,0))
6652   {
6653     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6654     {
6655       int element = EL_CUSTOM_START + i;
6656       struct ElementInfo *ei = &element_info[element];
6657
6658       if (ei->access_direction == MV_NO_DIRECTION)
6659         ei->access_direction = MV_ALL_DIRECTIONS;
6660     }
6661   }
6662
6663   // correct custom element values (fix invalid values for all versions)
6664   if (1)
6665   {
6666     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6667     {
6668       int element = EL_CUSTOM_START + i;
6669       struct ElementInfo *ei = &element_info[element];
6670
6671       for (j = 0; j < ei->num_change_pages; j++)
6672       {
6673         struct ElementChangeInfo *change = &ei->change_page[j];
6674
6675         if (change->trigger_player == CH_PLAYER_NONE)
6676           change->trigger_player = CH_PLAYER_ANY;
6677
6678         if (change->trigger_side == CH_SIDE_NONE)
6679           change->trigger_side = CH_SIDE_ANY;
6680       }
6681     }
6682   }
6683
6684   // initialize "can_explode" field for old levels which did not store this
6685   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6686   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6687   {
6688     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6689     {
6690       int element = EL_CUSTOM_START + i;
6691
6692       if (EXPLODES_1X1_OLD(element))
6693         element_info[element].explosion_type = EXPLODES_1X1;
6694
6695       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6696                                              EXPLODES_SMASHED(element) ||
6697                                              EXPLODES_IMPACT(element)));
6698     }
6699   }
6700
6701   // correct previously hard-coded move delay values for maze runner style
6702   if (level->game_version < VERSION_IDENT(3,1,1,0))
6703   {
6704     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6705     {
6706       int element = EL_CUSTOM_START + i;
6707
6708       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6709       {
6710         // previously hard-coded and therefore ignored
6711         element_info[element].move_delay_fixed = 9;
6712         element_info[element].move_delay_random = 0;
6713       }
6714     }
6715   }
6716
6717   // set some other uninitialized values of custom elements in older levels
6718   if (level->game_version < VERSION_IDENT(3,1,0,0))
6719   {
6720     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6721     {
6722       int element = EL_CUSTOM_START + i;
6723
6724       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6725
6726       element_info[element].explosion_delay = 17;
6727       element_info[element].ignition_delay = 8;
6728     }
6729   }
6730
6731   // set mouse click change events to work for left/middle/right mouse button
6732   if (level->game_version < VERSION_IDENT(4,2,3,0))
6733   {
6734     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6735     {
6736       int element = EL_CUSTOM_START + i;
6737       struct ElementInfo *ei = &element_info[element];
6738
6739       for (j = 0; j < ei->num_change_pages; j++)
6740       {
6741         struct ElementChangeInfo *change = &ei->change_page[j];
6742
6743         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6744             change->has_event[CE_PRESSED_BY_MOUSE] ||
6745             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6746             change->has_event[CE_MOUSE_PRESSED_ON_X])
6747           change->trigger_side = CH_SIDE_ANY;
6748       }
6749     }
6750   }
6751 }
6752
6753 static void LoadLevel_InitElements(struct LevelInfo *level)
6754 {
6755   LoadLevel_InitStandardElements(level);
6756
6757   if (level->file_has_custom_elements)
6758     LoadLevel_InitCustomElements(level);
6759
6760   // initialize element properties for level editor etc.
6761   InitElementPropertiesEngine(level->game_version);
6762   InitElementPropertiesGfxElement();
6763 }
6764
6765 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6766 {
6767   int x, y;
6768
6769   // map elements that have changed in newer versions
6770   for (y = 0; y < level->fieldy; y++)
6771     for (x = 0; x < level->fieldx; x++)
6772       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6773                                                      level->game_version);
6774
6775   // clear unused playfield data (nicer if level gets resized in editor)
6776   for (x = 0; x < MAX_LEV_FIELDX; x++)
6777     for (y = 0; y < MAX_LEV_FIELDY; y++)
6778       if (x >= level->fieldx || y >= level->fieldy)
6779         level->field[x][y] = EL_EMPTY;
6780
6781   // copy elements to runtime playfield array
6782   for (x = 0; x < MAX_LEV_FIELDX; x++)
6783     for (y = 0; y < MAX_LEV_FIELDY; y++)
6784       Tile[x][y] = level->field[x][y];
6785
6786   // initialize level size variables for faster access
6787   lev_fieldx = level->fieldx;
6788   lev_fieldy = level->fieldy;
6789
6790   // determine border element for this level
6791   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6792     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
6793   else
6794     SetBorderElement();
6795 }
6796
6797 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6798 {
6799   struct LevelFileInfo *level_file_info = &level->file_info;
6800
6801   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6802     CopyNativeLevel_RND_to_Native(level);
6803 }
6804
6805 static void LoadLevelTemplate_LoadAndInit(void)
6806 {
6807   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6808
6809   LoadLevel_InitVersion(&level_template);
6810   LoadLevel_InitElements(&level_template);
6811   LoadLevel_InitSettings(&level_template);
6812
6813   ActivateLevelTemplate();
6814 }
6815
6816 void LoadLevelTemplate(int nr)
6817 {
6818   if (!fileExists(getGlobalLevelTemplateFilename()))
6819   {
6820     Warn("no level template found for this level");
6821
6822     return;
6823   }
6824
6825   setLevelFileInfo(&level_template.file_info, nr);
6826
6827   LoadLevelTemplate_LoadAndInit();
6828 }
6829
6830 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6831 {
6832   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6833
6834   LoadLevelTemplate_LoadAndInit();
6835 }
6836
6837 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6838 {
6839   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6840
6841   if (level.use_custom_template)
6842   {
6843     if (network_level != NULL)
6844       LoadNetworkLevelTemplate(network_level);
6845     else
6846       LoadLevelTemplate(-1);
6847   }
6848
6849   LoadLevel_InitVersion(&level);
6850   LoadLevel_InitElements(&level);
6851   LoadLevel_InitPlayfield(&level);
6852   LoadLevel_InitSettings(&level);
6853
6854   LoadLevel_InitNativeEngines(&level);
6855 }
6856
6857 void LoadLevel(int nr)
6858 {
6859   SetLevelSetInfo(leveldir_current->identifier, nr);
6860
6861   setLevelFileInfo(&level.file_info, nr);
6862
6863   LoadLevel_LoadAndInit(NULL);
6864 }
6865
6866 void LoadLevelInfoOnly(int nr)
6867 {
6868   setLevelFileInfo(&level.file_info, nr);
6869
6870   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6871 }
6872
6873 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6874 {
6875   SetLevelSetInfo(network_level->leveldir_identifier,
6876                   network_level->file_info.nr);
6877
6878   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6879
6880   LoadLevel_LoadAndInit(network_level);
6881 }
6882
6883 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6884 {
6885   int chunk_size = 0;
6886
6887   chunk_size += putFileVersion(file, level->file_version);
6888   chunk_size += putFileVersion(file, level->game_version);
6889
6890   return chunk_size;
6891 }
6892
6893 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6894 {
6895   int chunk_size = 0;
6896
6897   chunk_size += putFile16BitBE(file, level->creation_date.year);
6898   chunk_size += putFile8Bit(file,    level->creation_date.month);
6899   chunk_size += putFile8Bit(file,    level->creation_date.day);
6900
6901   return chunk_size;
6902 }
6903
6904 #if ENABLE_HISTORIC_CHUNKS
6905 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6906 {
6907   int i, x, y;
6908
6909   putFile8Bit(file, level->fieldx);
6910   putFile8Bit(file, level->fieldy);
6911
6912   putFile16BitBE(file, level->time);
6913   putFile16BitBE(file, level->gems_needed);
6914
6915   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6916     putFile8Bit(file, level->name[i]);
6917
6918   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6919     putFile8Bit(file, level->score[i]);
6920
6921   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6922     for (y = 0; y < 3; y++)
6923       for (x = 0; x < 3; x++)
6924         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6925                            level->yamyam_content[i].e[x][y]));
6926   putFile8Bit(file, level->amoeba_speed);
6927   putFile8Bit(file, level->time_magic_wall);
6928   putFile8Bit(file, level->time_wheel);
6929   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6930                      level->amoeba_content));
6931   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6932   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6933   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6934   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6935
6936   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6937
6938   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6939   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6940   putFile32BitBE(file, level->can_move_into_acid_bits);
6941   putFile8Bit(file, level->dont_collide_with_bits);
6942
6943   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6944   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6945
6946   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6947   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6948   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6949
6950   putFile8Bit(file, level->game_engine_type);
6951
6952   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6953 }
6954 #endif
6955
6956 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6957 {
6958   int chunk_size = 0;
6959   int i;
6960
6961   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6962     chunk_size += putFile8Bit(file, level->name[i]);
6963
6964   return chunk_size;
6965 }
6966
6967 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6968 {
6969   int chunk_size = 0;
6970   int i;
6971
6972   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6973     chunk_size += putFile8Bit(file, level->author[i]);
6974
6975   return chunk_size;
6976 }
6977
6978 #if ENABLE_HISTORIC_CHUNKS
6979 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6980 {
6981   int chunk_size = 0;
6982   int x, y;
6983
6984   for (y = 0; y < level->fieldy; y++)
6985     for (x = 0; x < level->fieldx; x++)
6986       if (level->encoding_16bit_field)
6987         chunk_size += putFile16BitBE(file, level->field[x][y]);
6988       else
6989         chunk_size += putFile8Bit(file, level->field[x][y]);
6990
6991   return chunk_size;
6992 }
6993 #endif
6994
6995 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6996 {
6997   int chunk_size = 0;
6998   int x, y;
6999
7000   for (y = 0; y < level->fieldy; y++) 
7001     for (x = 0; x < level->fieldx; x++) 
7002       chunk_size += putFile16BitBE(file, level->field[x][y]);
7003
7004   return chunk_size;
7005 }
7006
7007 #if ENABLE_HISTORIC_CHUNKS
7008 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7009 {
7010   int i, x, y;
7011
7012   putFile8Bit(file, EL_YAMYAM);
7013   putFile8Bit(file, level->num_yamyam_contents);
7014   putFile8Bit(file, 0);
7015   putFile8Bit(file, 0);
7016
7017   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7018     for (y = 0; y < 3; y++)
7019       for (x = 0; x < 3; x++)
7020         if (level->encoding_16bit_field)
7021           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7022         else
7023           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7024 }
7025 #endif
7026
7027 #if ENABLE_HISTORIC_CHUNKS
7028 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7029 {
7030   int i, x, y;
7031   int num_contents, content_xsize, content_ysize;
7032   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7033
7034   if (element == EL_YAMYAM)
7035   {
7036     num_contents = level->num_yamyam_contents;
7037     content_xsize = 3;
7038     content_ysize = 3;
7039
7040     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7041       for (y = 0; y < 3; y++)
7042         for (x = 0; x < 3; x++)
7043           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7044   }
7045   else if (element == EL_BD_AMOEBA)
7046   {
7047     num_contents = 1;
7048     content_xsize = 1;
7049     content_ysize = 1;
7050
7051     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7052       for (y = 0; y < 3; y++)
7053         for (x = 0; x < 3; x++)
7054           content_array[i][x][y] = EL_EMPTY;
7055     content_array[0][0][0] = level->amoeba_content;
7056   }
7057   else
7058   {
7059     // chunk header already written -- write empty chunk data
7060     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7061
7062     Warn("cannot save content for element '%d'", element);
7063
7064     return;
7065   }
7066
7067   putFile16BitBE(file, element);
7068   putFile8Bit(file, num_contents);
7069   putFile8Bit(file, content_xsize);
7070   putFile8Bit(file, content_ysize);
7071
7072   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7073
7074   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7075     for (y = 0; y < 3; y++)
7076       for (x = 0; x < 3; x++)
7077         putFile16BitBE(file, content_array[i][x][y]);
7078 }
7079 #endif
7080
7081 #if ENABLE_HISTORIC_CHUNKS
7082 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7083 {
7084   int envelope_nr = element - EL_ENVELOPE_1;
7085   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7086   int chunk_size = 0;
7087   int i;
7088
7089   chunk_size += putFile16BitBE(file, element);
7090   chunk_size += putFile16BitBE(file, envelope_len);
7091   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7092   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7093
7094   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7095   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7096
7097   for (i = 0; i < envelope_len; i++)
7098     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7099
7100   return chunk_size;
7101 }
7102 #endif
7103
7104 #if ENABLE_HISTORIC_CHUNKS
7105 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7106                            int num_changed_custom_elements)
7107 {
7108   int i, check = 0;
7109
7110   putFile16BitBE(file, num_changed_custom_elements);
7111
7112   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7113   {
7114     int element = EL_CUSTOM_START + i;
7115
7116     struct ElementInfo *ei = &element_info[element];
7117
7118     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7119     {
7120       if (check < num_changed_custom_elements)
7121       {
7122         putFile16BitBE(file, element);
7123         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7124       }
7125
7126       check++;
7127     }
7128   }
7129
7130   if (check != num_changed_custom_elements)     // should not happen
7131     Warn("inconsistent number of custom element properties");
7132 }
7133 #endif
7134
7135 #if ENABLE_HISTORIC_CHUNKS
7136 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7137                            int num_changed_custom_elements)
7138 {
7139   int i, check = 0;
7140
7141   putFile16BitBE(file, num_changed_custom_elements);
7142
7143   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7144   {
7145     int element = EL_CUSTOM_START + i;
7146
7147     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7148     {
7149       if (check < num_changed_custom_elements)
7150       {
7151         putFile16BitBE(file, element);
7152         putFile16BitBE(file, element_info[element].change->target_element);
7153       }
7154
7155       check++;
7156     }
7157   }
7158
7159   if (check != num_changed_custom_elements)     // should not happen
7160     Warn("inconsistent number of custom target elements");
7161 }
7162 #endif
7163
7164 #if ENABLE_HISTORIC_CHUNKS
7165 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7166                            int num_changed_custom_elements)
7167 {
7168   int i, j, x, y, check = 0;
7169
7170   putFile16BitBE(file, num_changed_custom_elements);
7171
7172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7173   {
7174     int element = EL_CUSTOM_START + i;
7175     struct ElementInfo *ei = &element_info[element];
7176
7177     if (ei->modified_settings)
7178     {
7179       if (check < num_changed_custom_elements)
7180       {
7181         putFile16BitBE(file, element);
7182
7183         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7184           putFile8Bit(file, ei->description[j]);
7185
7186         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7187
7188         // some free bytes for future properties and padding
7189         WriteUnusedBytesToFile(file, 7);
7190
7191         putFile8Bit(file, ei->use_gfx_element);
7192         putFile16BitBE(file, ei->gfx_element_initial);
7193
7194         putFile8Bit(file, ei->collect_score_initial);
7195         putFile8Bit(file, ei->collect_count_initial);
7196
7197         putFile16BitBE(file, ei->push_delay_fixed);
7198         putFile16BitBE(file, ei->push_delay_random);
7199         putFile16BitBE(file, ei->move_delay_fixed);
7200         putFile16BitBE(file, ei->move_delay_random);
7201
7202         putFile16BitBE(file, ei->move_pattern);
7203         putFile8Bit(file, ei->move_direction_initial);
7204         putFile8Bit(file, ei->move_stepsize);
7205
7206         for (y = 0; y < 3; y++)
7207           for (x = 0; x < 3; x++)
7208             putFile16BitBE(file, ei->content.e[x][y]);
7209
7210         putFile32BitBE(file, ei->change->events);
7211
7212         putFile16BitBE(file, ei->change->target_element);
7213
7214         putFile16BitBE(file, ei->change->delay_fixed);
7215         putFile16BitBE(file, ei->change->delay_random);
7216         putFile16BitBE(file, ei->change->delay_frames);
7217
7218         putFile16BitBE(file, ei->change->initial_trigger_element);
7219
7220         putFile8Bit(file, ei->change->explode);
7221         putFile8Bit(file, ei->change->use_target_content);
7222         putFile8Bit(file, ei->change->only_if_complete);
7223         putFile8Bit(file, ei->change->use_random_replace);
7224
7225         putFile8Bit(file, ei->change->random_percentage);
7226         putFile8Bit(file, ei->change->replace_when);
7227
7228         for (y = 0; y < 3; y++)
7229           for (x = 0; x < 3; x++)
7230             putFile16BitBE(file, ei->change->content.e[x][y]);
7231
7232         putFile8Bit(file, ei->slippery_type);
7233
7234         // some free bytes for future properties and padding
7235         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7236       }
7237
7238       check++;
7239     }
7240   }
7241
7242   if (check != num_changed_custom_elements)     // should not happen
7243     Warn("inconsistent number of custom element properties");
7244 }
7245 #endif
7246
7247 #if ENABLE_HISTORIC_CHUNKS
7248 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7249 {
7250   struct ElementInfo *ei = &element_info[element];
7251   int i, j, x, y;
7252
7253   // ---------- custom element base property values (96 bytes) ----------------
7254
7255   putFile16BitBE(file, element);
7256
7257   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7258     putFile8Bit(file, ei->description[i]);
7259
7260   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7261
7262   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7263
7264   putFile8Bit(file, ei->num_change_pages);
7265
7266   putFile16BitBE(file, ei->ce_value_fixed_initial);
7267   putFile16BitBE(file, ei->ce_value_random_initial);
7268   putFile8Bit(file, ei->use_last_ce_value);
7269
7270   putFile8Bit(file, ei->use_gfx_element);
7271   putFile16BitBE(file, ei->gfx_element_initial);
7272
7273   putFile8Bit(file, ei->collect_score_initial);
7274   putFile8Bit(file, ei->collect_count_initial);
7275
7276   putFile8Bit(file, ei->drop_delay_fixed);
7277   putFile8Bit(file, ei->push_delay_fixed);
7278   putFile8Bit(file, ei->drop_delay_random);
7279   putFile8Bit(file, ei->push_delay_random);
7280   putFile16BitBE(file, ei->move_delay_fixed);
7281   putFile16BitBE(file, ei->move_delay_random);
7282
7283   // bits 0 - 15 of "move_pattern" ...
7284   putFile16BitBE(file, ei->move_pattern & 0xffff);
7285   putFile8Bit(file, ei->move_direction_initial);
7286   putFile8Bit(file, ei->move_stepsize);
7287
7288   putFile8Bit(file, ei->slippery_type);
7289
7290   for (y = 0; y < 3; y++)
7291     for (x = 0; x < 3; x++)
7292       putFile16BitBE(file, ei->content.e[x][y]);
7293
7294   putFile16BitBE(file, ei->move_enter_element);
7295   putFile16BitBE(file, ei->move_leave_element);
7296   putFile8Bit(file, ei->move_leave_type);
7297
7298   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7299   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7300
7301   putFile8Bit(file, ei->access_direction);
7302
7303   putFile8Bit(file, ei->explosion_delay);
7304   putFile8Bit(file, ei->ignition_delay);
7305   putFile8Bit(file, ei->explosion_type);
7306
7307   // some free bytes for future custom property values and padding
7308   WriteUnusedBytesToFile(file, 1);
7309
7310   // ---------- change page property values (48 bytes) ------------------------
7311
7312   for (i = 0; i < ei->num_change_pages; i++)
7313   {
7314     struct ElementChangeInfo *change = &ei->change_page[i];
7315     unsigned int event_bits;
7316
7317     // bits 0 - 31 of "has_event[]" ...
7318     event_bits = 0;
7319     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7320       if (change->has_event[j])
7321         event_bits |= (1 << j);
7322     putFile32BitBE(file, event_bits);
7323
7324     putFile16BitBE(file, change->target_element);
7325
7326     putFile16BitBE(file, change->delay_fixed);
7327     putFile16BitBE(file, change->delay_random);
7328     putFile16BitBE(file, change->delay_frames);
7329
7330     putFile16BitBE(file, change->initial_trigger_element);
7331
7332     putFile8Bit(file, change->explode);
7333     putFile8Bit(file, change->use_target_content);
7334     putFile8Bit(file, change->only_if_complete);
7335     putFile8Bit(file, change->use_random_replace);
7336
7337     putFile8Bit(file, change->random_percentage);
7338     putFile8Bit(file, change->replace_when);
7339
7340     for (y = 0; y < 3; y++)
7341       for (x = 0; x < 3; x++)
7342         putFile16BitBE(file, change->target_content.e[x][y]);
7343
7344     putFile8Bit(file, change->can_change);
7345
7346     putFile8Bit(file, change->trigger_side);
7347
7348     putFile8Bit(file, change->trigger_player);
7349     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7350                        log_2(change->trigger_page)));
7351
7352     putFile8Bit(file, change->has_action);
7353     putFile8Bit(file, change->action_type);
7354     putFile8Bit(file, change->action_mode);
7355     putFile16BitBE(file, change->action_arg);
7356
7357     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7358     event_bits = 0;
7359     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7360       if (change->has_event[j])
7361         event_bits |= (1 << (j - 32));
7362     putFile8Bit(file, event_bits);
7363   }
7364 }
7365 #endif
7366
7367 #if ENABLE_HISTORIC_CHUNKS
7368 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7369 {
7370   struct ElementInfo *ei = &element_info[element];
7371   struct ElementGroupInfo *group = ei->group;
7372   int i;
7373
7374   putFile16BitBE(file, element);
7375
7376   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7377     putFile8Bit(file, ei->description[i]);
7378
7379   putFile8Bit(file, group->num_elements);
7380
7381   putFile8Bit(file, ei->use_gfx_element);
7382   putFile16BitBE(file, ei->gfx_element_initial);
7383
7384   putFile8Bit(file, group->choice_mode);
7385
7386   // some free bytes for future values and padding
7387   WriteUnusedBytesToFile(file, 3);
7388
7389   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7390     putFile16BitBE(file, group->element[i]);
7391 }
7392 #endif
7393
7394 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7395                                 boolean write_element)
7396 {
7397   int save_type = entry->save_type;
7398   int data_type = entry->data_type;
7399   int conf_type = entry->conf_type;
7400   int byte_mask = conf_type & CONF_MASK_BYTES;
7401   int element = entry->element;
7402   int default_value = entry->default_value;
7403   int num_bytes = 0;
7404   boolean modified = FALSE;
7405
7406   if (byte_mask != CONF_MASK_MULTI_BYTES)
7407   {
7408     void *value_ptr = entry->value;
7409     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7410                  *(int *)value_ptr);
7411
7412     // check if any settings have been modified before saving them
7413     if (value != default_value)
7414       modified = TRUE;
7415
7416     // do not save if explicitly told or if unmodified default settings
7417     if ((save_type == SAVE_CONF_NEVER) ||
7418         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7419       return 0;
7420
7421     if (write_element)
7422       num_bytes += putFile16BitBE(file, element);
7423
7424     num_bytes += putFile8Bit(file, conf_type);
7425     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7426                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7427                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7428                   0);
7429   }
7430   else if (data_type == TYPE_STRING)
7431   {
7432     char *default_string = entry->default_string;
7433     char *string = (char *)(entry->value);
7434     int string_length = strlen(string);
7435     int i;
7436
7437     // check if any settings have been modified before saving them
7438     if (!strEqual(string, default_string))
7439       modified = TRUE;
7440
7441     // do not save if explicitly told or if unmodified default settings
7442     if ((save_type == SAVE_CONF_NEVER) ||
7443         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7444       return 0;
7445
7446     if (write_element)
7447       num_bytes += putFile16BitBE(file, element);
7448
7449     num_bytes += putFile8Bit(file, conf_type);
7450     num_bytes += putFile16BitBE(file, string_length);
7451
7452     for (i = 0; i < string_length; i++)
7453       num_bytes += putFile8Bit(file, string[i]);
7454   }
7455   else if (data_type == TYPE_ELEMENT_LIST)
7456   {
7457     int *element_array = (int *)(entry->value);
7458     int num_elements = *(int *)(entry->num_entities);
7459     int i;
7460
7461     // check if any settings have been modified before saving them
7462     for (i = 0; i < num_elements; i++)
7463       if (element_array[i] != default_value)
7464         modified = TRUE;
7465
7466     // do not save if explicitly told or if unmodified default settings
7467     if ((save_type == SAVE_CONF_NEVER) ||
7468         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7469       return 0;
7470
7471     if (write_element)
7472       num_bytes += putFile16BitBE(file, element);
7473
7474     num_bytes += putFile8Bit(file, conf_type);
7475     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7476
7477     for (i = 0; i < num_elements; i++)
7478       num_bytes += putFile16BitBE(file, element_array[i]);
7479   }
7480   else if (data_type == TYPE_CONTENT_LIST)
7481   {
7482     struct Content *content = (struct Content *)(entry->value);
7483     int num_contents = *(int *)(entry->num_entities);
7484     int i, x, y;
7485
7486     // check if any settings have been modified before saving them
7487     for (i = 0; i < num_contents; i++)
7488       for (y = 0; y < 3; y++)
7489         for (x = 0; x < 3; x++)
7490           if (content[i].e[x][y] != default_value)
7491             modified = TRUE;
7492
7493     // do not save if explicitly told or if unmodified default settings
7494     if ((save_type == SAVE_CONF_NEVER) ||
7495         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7496       return 0;
7497
7498     if (write_element)
7499       num_bytes += putFile16BitBE(file, element);
7500
7501     num_bytes += putFile8Bit(file, conf_type);
7502     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7503
7504     for (i = 0; i < num_contents; i++)
7505       for (y = 0; y < 3; y++)
7506         for (x = 0; x < 3; x++)
7507           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7508   }
7509
7510   return num_bytes;
7511 }
7512
7513 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7514 {
7515   int chunk_size = 0;
7516   int i;
7517
7518   li = *level;          // copy level data into temporary buffer
7519
7520   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7521     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7522
7523   return chunk_size;
7524 }
7525
7526 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7527 {
7528   int chunk_size = 0;
7529   int i;
7530
7531   li = *level;          // copy level data into temporary buffer
7532
7533   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7534     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7535
7536   return chunk_size;
7537 }
7538
7539 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7540 {
7541   int envelope_nr = element - EL_ENVELOPE_1;
7542   int chunk_size = 0;
7543   int i;
7544
7545   chunk_size += putFile16BitBE(file, element);
7546
7547   // copy envelope data into temporary buffer
7548   xx_envelope = level->envelope[envelope_nr];
7549
7550   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7551     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7552
7553   return chunk_size;
7554 }
7555
7556 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7557 {
7558   struct ElementInfo *ei = &element_info[element];
7559   int chunk_size = 0;
7560   int i, j;
7561
7562   chunk_size += putFile16BitBE(file, element);
7563
7564   xx_ei = *ei;          // copy element data into temporary buffer
7565
7566   // set default description string for this specific element
7567   strcpy(xx_default_description, getDefaultElementDescription(ei));
7568
7569   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7570     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7571
7572   for (i = 0; i < ei->num_change_pages; i++)
7573   {
7574     struct ElementChangeInfo *change = &ei->change_page[i];
7575
7576     xx_current_change_page = i;
7577
7578     xx_change = *change;        // copy change data into temporary buffer
7579
7580     resetEventBits();
7581     setEventBitsFromEventFlags(change);
7582
7583     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7584       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7585                                          FALSE);
7586   }
7587
7588   return chunk_size;
7589 }
7590
7591 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7592 {
7593   struct ElementInfo *ei = &element_info[element];
7594   struct ElementGroupInfo *group = ei->group;
7595   int chunk_size = 0;
7596   int i;
7597
7598   chunk_size += putFile16BitBE(file, element);
7599
7600   xx_ei = *ei;          // copy element data into temporary buffer
7601   xx_group = *group;    // copy group data into temporary buffer
7602
7603   // set default description string for this specific element
7604   strcpy(xx_default_description, getDefaultElementDescription(ei));
7605
7606   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7607     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7608
7609   return chunk_size;
7610 }
7611
7612 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7613 {
7614   struct ElementInfo *ei = &element_info[element];
7615   int chunk_size = 0;
7616   int i;
7617
7618   chunk_size += putFile16BitBE(file, element);
7619
7620   xx_ei = *ei;          // copy element data into temporary buffer
7621
7622   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7623     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7624
7625   return chunk_size;
7626 }
7627
7628 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7629                                   boolean save_as_template)
7630 {
7631   int chunk_size;
7632   int i;
7633   FILE *file;
7634
7635   if (!(file = fopen(filename, MODE_WRITE)))
7636   {
7637     Warn("cannot save level file '%s'", filename);
7638
7639     return;
7640   }
7641
7642   level->file_version = FILE_VERSION_ACTUAL;
7643   level->game_version = GAME_VERSION_ACTUAL;
7644
7645   level->creation_date = getCurrentDate();
7646
7647   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7648   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7649
7650   chunk_size = SaveLevel_VERS(NULL, level);
7651   putFileChunkBE(file, "VERS", chunk_size);
7652   SaveLevel_VERS(file, level);
7653
7654   chunk_size = SaveLevel_DATE(NULL, level);
7655   putFileChunkBE(file, "DATE", chunk_size);
7656   SaveLevel_DATE(file, level);
7657
7658   chunk_size = SaveLevel_NAME(NULL, level);
7659   putFileChunkBE(file, "NAME", chunk_size);
7660   SaveLevel_NAME(file, level);
7661
7662   chunk_size = SaveLevel_AUTH(NULL, level);
7663   putFileChunkBE(file, "AUTH", chunk_size);
7664   SaveLevel_AUTH(file, level);
7665
7666   chunk_size = SaveLevel_INFO(NULL, level);
7667   putFileChunkBE(file, "INFO", chunk_size);
7668   SaveLevel_INFO(file, level);
7669
7670   chunk_size = SaveLevel_BODY(NULL, level);
7671   putFileChunkBE(file, "BODY", chunk_size);
7672   SaveLevel_BODY(file, level);
7673
7674   chunk_size = SaveLevel_ELEM(NULL, level);
7675   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7676   {
7677     putFileChunkBE(file, "ELEM", chunk_size);
7678     SaveLevel_ELEM(file, level);
7679   }
7680
7681   for (i = 0; i < NUM_ENVELOPES; i++)
7682   {
7683     int element = EL_ENVELOPE_1 + i;
7684
7685     chunk_size = SaveLevel_NOTE(NULL, level, element);
7686     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7687     {
7688       putFileChunkBE(file, "NOTE", chunk_size);
7689       SaveLevel_NOTE(file, level, element);
7690     }
7691   }
7692
7693   // if not using template level, check for non-default custom/group elements
7694   if (!level->use_custom_template || save_as_template)
7695   {
7696     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7697     {
7698       int element = EL_CUSTOM_START + i;
7699
7700       chunk_size = SaveLevel_CUSX(NULL, level, element);
7701       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
7702       {
7703         putFileChunkBE(file, "CUSX", chunk_size);
7704         SaveLevel_CUSX(file, level, element);
7705       }
7706     }
7707
7708     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7709     {
7710       int element = EL_GROUP_START + i;
7711
7712       chunk_size = SaveLevel_GRPX(NULL, level, element);
7713       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
7714       {
7715         putFileChunkBE(file, "GRPX", chunk_size);
7716         SaveLevel_GRPX(file, level, element);
7717       }
7718     }
7719
7720     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7721     {
7722       int element = GET_EMPTY_ELEMENT(i);
7723
7724       chunk_size = SaveLevel_EMPX(NULL, level, element);
7725       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
7726       {
7727         putFileChunkBE(file, "EMPX", chunk_size);
7728         SaveLevel_EMPX(file, level, element);
7729       }
7730     }
7731   }
7732
7733   fclose(file);
7734
7735   SetFilePermissions(filename, PERMS_PRIVATE);
7736 }
7737
7738 void SaveLevel(int nr)
7739 {
7740   char *filename = getDefaultLevelFilename(nr);
7741
7742   SaveLevelFromFilename(&level, filename, FALSE);
7743 }
7744
7745 void SaveLevelTemplate(void)
7746 {
7747   char *filename = getLocalLevelTemplateFilename();
7748
7749   SaveLevelFromFilename(&level, filename, TRUE);
7750 }
7751
7752 boolean SaveLevelChecked(int nr)
7753 {
7754   char *filename = getDefaultLevelFilename(nr);
7755   boolean new_level = !fileExists(filename);
7756   boolean level_saved = FALSE;
7757
7758   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7759   {
7760     SaveLevel(nr);
7761
7762     if (new_level)
7763       Request("Level saved!", REQ_CONFIRM);
7764
7765     level_saved = TRUE;
7766   }
7767
7768   return level_saved;
7769 }
7770
7771 void DumpLevel(struct LevelInfo *level)
7772 {
7773   if (level->no_level_file || level->no_valid_file)
7774   {
7775     Warn("cannot dump -- no valid level file found");
7776
7777     return;
7778   }
7779
7780   PrintLine("-", 79);
7781   Print("Level xxx (file version %08d, game version %08d)\n",
7782         level->file_version, level->game_version);
7783   PrintLine("-", 79);
7784
7785   Print("Level author: '%s'\n", level->author);
7786   Print("Level title:  '%s'\n", level->name);
7787   Print("\n");
7788   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7789   Print("\n");
7790   Print("Level time:  %d seconds\n", level->time);
7791   Print("Gems needed: %d\n", level->gems_needed);
7792   Print("\n");
7793   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7794   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7795   Print("Time for light:      %d seconds\n", level->time_light);
7796   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7797   Print("\n");
7798   Print("Amoeba speed: %d\n", level->amoeba_speed);
7799   Print("\n");
7800
7801   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7802   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7803   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7804   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7805   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7806   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7807
7808   if (options.debug)
7809   {
7810     int i, j;
7811
7812     for (i = 0; i < NUM_ENVELOPES; i++)
7813     {
7814       char *text = level->envelope[i].text;
7815       int text_len = strlen(text);
7816       boolean has_text = FALSE;
7817
7818       for (j = 0; j < text_len; j++)
7819         if (text[j] != ' ' && text[j] != '\n')
7820           has_text = TRUE;
7821
7822       if (has_text)
7823       {
7824         Print("\n");
7825         Print("Envelope %d:\n'%s'\n", i + 1, text);
7826       }
7827     }
7828   }
7829
7830   PrintLine("-", 79);
7831 }
7832
7833 void DumpLevels(void)
7834 {
7835   static LevelDirTree *dumplevel_leveldir = NULL;
7836
7837   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7838                                                  global.dumplevel_leveldir);
7839
7840   if (dumplevel_leveldir == NULL)
7841     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7842
7843   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7844       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7845     Fail("no such level number: %d", global.dumplevel_level_nr);
7846
7847   leveldir_current = dumplevel_leveldir;
7848
7849   LoadLevel(global.dumplevel_level_nr);
7850   DumpLevel(&level);
7851
7852   CloseAllAndExit(0);
7853 }
7854
7855
7856 // ============================================================================
7857 // tape file functions
7858 // ============================================================================
7859
7860 static void setTapeInfoToDefaults(void)
7861 {
7862   int i;
7863
7864   // always start with reliable default values (empty tape)
7865   TapeErase();
7866
7867   // default values (also for pre-1.2 tapes) with only the first player
7868   tape.player_participates[0] = TRUE;
7869   for (i = 1; i < MAX_PLAYERS; i++)
7870     tape.player_participates[i] = FALSE;
7871
7872   // at least one (default: the first) player participates in every tape
7873   tape.num_participating_players = 1;
7874
7875   tape.property_bits = TAPE_PROPERTY_NONE;
7876
7877   tape.level_nr = level_nr;
7878   tape.counter = 0;
7879   tape.changed = FALSE;
7880
7881   tape.recording = FALSE;
7882   tape.playing = FALSE;
7883   tape.pausing = FALSE;
7884
7885   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7886   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7887
7888   tape.no_info_chunk = TRUE;
7889   tape.no_valid_file = FALSE;
7890 }
7891
7892 static int getTapePosSize(struct TapeInfo *tape)
7893 {
7894   int tape_pos_size = 0;
7895
7896   if (tape->use_key_actions)
7897     tape_pos_size += tape->num_participating_players;
7898
7899   if (tape->use_mouse_actions)
7900     tape_pos_size += 3;         // x and y position and mouse button mask
7901
7902   tape_pos_size += 1;           // tape action delay value
7903
7904   return tape_pos_size;
7905 }
7906
7907 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7908 {
7909   tape->use_key_actions = FALSE;
7910   tape->use_mouse_actions = FALSE;
7911
7912   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7913     tape->use_key_actions = TRUE;
7914
7915   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7916     tape->use_mouse_actions = TRUE;
7917 }
7918
7919 static int getTapeActionValue(struct TapeInfo *tape)
7920 {
7921   return (tape->use_key_actions &&
7922           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7923           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7924           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7925           TAPE_ACTIONS_DEFAULT);
7926 }
7927
7928 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7929 {
7930   tape->file_version = getFileVersion(file);
7931   tape->game_version = getFileVersion(file);
7932
7933   return chunk_size;
7934 }
7935
7936 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7937 {
7938   int i;
7939
7940   tape->random_seed = getFile32BitBE(file);
7941   tape->date        = getFile32BitBE(file);
7942   tape->length      = getFile32BitBE(file);
7943
7944   // read header fields that are new since version 1.2
7945   if (tape->file_version >= FILE_VERSION_1_2)
7946   {
7947     byte store_participating_players = getFile8Bit(file);
7948     int engine_version;
7949
7950     // since version 1.2, tapes store which players participate in the tape
7951     tape->num_participating_players = 0;
7952     for (i = 0; i < MAX_PLAYERS; i++)
7953     {
7954       tape->player_participates[i] = FALSE;
7955
7956       if (store_participating_players & (1 << i))
7957       {
7958         tape->player_participates[i] = TRUE;
7959         tape->num_participating_players++;
7960       }
7961     }
7962
7963     setTapeActionFlags(tape, getFile8Bit(file));
7964
7965     tape->property_bits = getFile8Bit(file);
7966
7967     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7968
7969     engine_version = getFileVersion(file);
7970     if (engine_version > 0)
7971       tape->engine_version = engine_version;
7972     else
7973       tape->engine_version = tape->game_version;
7974   }
7975
7976   return chunk_size;
7977 }
7978
7979 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7980 {
7981   tape->scr_fieldx = getFile8Bit(file);
7982   tape->scr_fieldy = getFile8Bit(file);
7983
7984   return chunk_size;
7985 }
7986
7987 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7988 {
7989   char *level_identifier = NULL;
7990   int level_identifier_size;
7991   int i;
7992
7993   tape->no_info_chunk = FALSE;
7994
7995   level_identifier_size = getFile16BitBE(file);
7996
7997   level_identifier = checked_malloc(level_identifier_size);
7998
7999   for (i = 0; i < level_identifier_size; i++)
8000     level_identifier[i] = getFile8Bit(file);
8001
8002   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8003   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8004
8005   checked_free(level_identifier);
8006
8007   tape->level_nr = getFile16BitBE(file);
8008
8009   chunk_size = 2 + level_identifier_size + 2;
8010
8011   return chunk_size;
8012 }
8013
8014 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8015 {
8016   int i, j;
8017   int tape_pos_size = getTapePosSize(tape);
8018   int chunk_size_expected = tape_pos_size * tape->length;
8019
8020   if (chunk_size_expected != chunk_size)
8021   {
8022     ReadUnusedBytesFromFile(file, chunk_size);
8023     return chunk_size_expected;
8024   }
8025
8026   for (i = 0; i < tape->length; i++)
8027   {
8028     if (i >= MAX_TAPE_LEN)
8029     {
8030       Warn("tape truncated -- size exceeds maximum tape size %d",
8031             MAX_TAPE_LEN);
8032
8033       // tape too large; read and ignore remaining tape data from this chunk
8034       for (;i < tape->length; i++)
8035         ReadUnusedBytesFromFile(file, tape_pos_size);
8036
8037       break;
8038     }
8039
8040     if (tape->use_key_actions)
8041     {
8042       for (j = 0; j < MAX_PLAYERS; j++)
8043       {
8044         tape->pos[i].action[j] = MV_NONE;
8045
8046         if (tape->player_participates[j])
8047           tape->pos[i].action[j] = getFile8Bit(file);
8048       }
8049     }
8050
8051     if (tape->use_mouse_actions)
8052     {
8053       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8054       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8055       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8056     }
8057
8058     tape->pos[i].delay = getFile8Bit(file);
8059
8060     if (tape->file_version == FILE_VERSION_1_0)
8061     {
8062       // eliminate possible diagonal moves in old tapes
8063       // this is only for backward compatibility
8064
8065       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8066       byte action = tape->pos[i].action[0];
8067       int k, num_moves = 0;
8068
8069       for (k = 0; k<4; k++)
8070       {
8071         if (action & joy_dir[k])
8072         {
8073           tape->pos[i + num_moves].action[0] = joy_dir[k];
8074           if (num_moves > 0)
8075             tape->pos[i + num_moves].delay = 0;
8076           num_moves++;
8077         }
8078       }
8079
8080       if (num_moves > 1)
8081       {
8082         num_moves--;
8083         i += num_moves;
8084         tape->length += num_moves;
8085       }
8086     }
8087     else if (tape->file_version < FILE_VERSION_2_0)
8088     {
8089       // convert pre-2.0 tapes to new tape format
8090
8091       if (tape->pos[i].delay > 1)
8092       {
8093         // action part
8094         tape->pos[i + 1] = tape->pos[i];
8095         tape->pos[i + 1].delay = 1;
8096
8097         // delay part
8098         for (j = 0; j < MAX_PLAYERS; j++)
8099           tape->pos[i].action[j] = MV_NONE;
8100         tape->pos[i].delay--;
8101
8102         i++;
8103         tape->length++;
8104       }
8105     }
8106
8107     if (checkEndOfFile(file))
8108       break;
8109   }
8110
8111   if (i != tape->length)
8112     chunk_size = tape_pos_size * i;
8113
8114   return chunk_size;
8115 }
8116
8117 static void LoadTape_SokobanSolution(char *filename)
8118 {
8119   File *file;
8120   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8121
8122   if (!(file = openFile(filename, MODE_READ)))
8123   {
8124     tape.no_valid_file = TRUE;
8125
8126     return;
8127   }
8128
8129   while (!checkEndOfFile(file))
8130   {
8131     unsigned char c = getByteFromFile(file);
8132
8133     if (checkEndOfFile(file))
8134       break;
8135
8136     switch (c)
8137     {
8138       case 'u':
8139       case 'U':
8140         tape.pos[tape.length].action[0] = MV_UP;
8141         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8142         tape.length++;
8143         break;
8144
8145       case 'd':
8146       case 'D':
8147         tape.pos[tape.length].action[0] = MV_DOWN;
8148         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8149         tape.length++;
8150         break;
8151
8152       case 'l':
8153       case 'L':
8154         tape.pos[tape.length].action[0] = MV_LEFT;
8155         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8156         tape.length++;
8157         break;
8158
8159       case 'r':
8160       case 'R':
8161         tape.pos[tape.length].action[0] = MV_RIGHT;
8162         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8163         tape.length++;
8164         break;
8165
8166       case '\n':
8167       case '\r':
8168       case '\t':
8169       case ' ':
8170         // ignore white-space characters
8171         break;
8172
8173       default:
8174         tape.no_valid_file = TRUE;
8175
8176         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8177
8178         break;
8179     }
8180   }
8181
8182   closeFile(file);
8183
8184   if (tape.no_valid_file)
8185     return;
8186
8187   tape.length_frames  = GetTapeLengthFrames();
8188   tape.length_seconds = GetTapeLengthSeconds();
8189 }
8190
8191 void LoadTapeFromFilename(char *filename)
8192 {
8193   char cookie[MAX_LINE_LEN];
8194   char chunk_name[CHUNK_ID_LEN + 1];
8195   File *file;
8196   int chunk_size;
8197
8198   // always start with reliable default values
8199   setTapeInfoToDefaults();
8200
8201   if (strSuffix(filename, ".sln"))
8202   {
8203     LoadTape_SokobanSolution(filename);
8204
8205     return;
8206   }
8207
8208   if (!(file = openFile(filename, MODE_READ)))
8209   {
8210     tape.no_valid_file = TRUE;
8211
8212     return;
8213   }
8214
8215   getFileChunkBE(file, chunk_name, NULL);
8216   if (strEqual(chunk_name, "RND1"))
8217   {
8218     getFile32BitBE(file);               // not used
8219
8220     getFileChunkBE(file, chunk_name, NULL);
8221     if (!strEqual(chunk_name, "TAPE"))
8222     {
8223       tape.no_valid_file = TRUE;
8224
8225       Warn("unknown format of tape file '%s'", filename);
8226
8227       closeFile(file);
8228
8229       return;
8230     }
8231   }
8232   else  // check for pre-2.0 file format with cookie string
8233   {
8234     strcpy(cookie, chunk_name);
8235     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8236       cookie[4] = '\0';
8237     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8238       cookie[strlen(cookie) - 1] = '\0';
8239
8240     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8241     {
8242       tape.no_valid_file = TRUE;
8243
8244       Warn("unknown format of tape file '%s'", filename);
8245
8246       closeFile(file);
8247
8248       return;
8249     }
8250
8251     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8252     {
8253       tape.no_valid_file = TRUE;
8254
8255       Warn("unsupported version of tape file '%s'", filename);
8256
8257       closeFile(file);
8258
8259       return;
8260     }
8261
8262     // pre-2.0 tape files have no game version, so use file version here
8263     tape.game_version = tape.file_version;
8264   }
8265
8266   if (tape.file_version < FILE_VERSION_1_2)
8267   {
8268     // tape files from versions before 1.2.0 without chunk structure
8269     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8270     LoadTape_BODY(file, 2 * tape.length,      &tape);
8271   }
8272   else
8273   {
8274     static struct
8275     {
8276       char *name;
8277       int size;
8278       int (*loader)(File *, int, struct TapeInfo *);
8279     }
8280     chunk_info[] =
8281     {
8282       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8283       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8284       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8285       { "INFO", -1,                     LoadTape_INFO },
8286       { "BODY", -1,                     LoadTape_BODY },
8287       {  NULL,  0,                      NULL }
8288     };
8289
8290     while (getFileChunkBE(file, chunk_name, &chunk_size))
8291     {
8292       int i = 0;
8293
8294       while (chunk_info[i].name != NULL &&
8295              !strEqual(chunk_name, chunk_info[i].name))
8296         i++;
8297
8298       if (chunk_info[i].name == NULL)
8299       {
8300         Warn("unknown chunk '%s' in tape file '%s'",
8301               chunk_name, filename);
8302
8303         ReadUnusedBytesFromFile(file, chunk_size);
8304       }
8305       else if (chunk_info[i].size != -1 &&
8306                chunk_info[i].size != chunk_size)
8307       {
8308         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8309               chunk_size, chunk_name, filename);
8310
8311         ReadUnusedBytesFromFile(file, chunk_size);
8312       }
8313       else
8314       {
8315         // call function to load this tape chunk
8316         int chunk_size_expected =
8317           (chunk_info[i].loader)(file, chunk_size, &tape);
8318
8319         // the size of some chunks cannot be checked before reading other
8320         // chunks first (like "HEAD" and "BODY") that contain some header
8321         // information, so check them here
8322         if (chunk_size_expected != chunk_size)
8323         {
8324           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8325                 chunk_size, chunk_name, filename);
8326         }
8327       }
8328     }
8329   }
8330
8331   closeFile(file);
8332
8333   tape.length_frames  = GetTapeLengthFrames();
8334   tape.length_seconds = GetTapeLengthSeconds();
8335
8336 #if 0
8337   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8338         tape.file_version);
8339   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8340         tape.game_version);
8341   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8342         tape.engine_version);
8343 #endif
8344 }
8345
8346 void LoadTape(int nr)
8347 {
8348   char *filename = getTapeFilename(nr);
8349
8350   LoadTapeFromFilename(filename);
8351 }
8352
8353 void LoadSolutionTape(int nr)
8354 {
8355   char *filename = getSolutionTapeFilename(nr);
8356
8357   LoadTapeFromFilename(filename);
8358
8359   if (TAPE_IS_EMPTY(tape) &&
8360       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8361       level.native_sp_level->demo.is_available)
8362     CopyNativeTape_SP_to_RND(&level);
8363 }
8364
8365 void LoadScoreTape(char *score_tape_basename, int nr)
8366 {
8367   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8368
8369   LoadTapeFromFilename(filename);
8370 }
8371
8372 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8373 {
8374   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8375
8376   LoadTapeFromFilename(filename);
8377 }
8378
8379 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8380 {
8381   // chunk required for team mode tapes with non-default screen size
8382   return (tape->num_participating_players > 1 &&
8383           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8384            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8385 }
8386
8387 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8388 {
8389   putFileVersion(file, tape->file_version);
8390   putFileVersion(file, tape->game_version);
8391 }
8392
8393 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8394 {
8395   int i;
8396   byte store_participating_players = 0;
8397
8398   // set bits for participating players for compact storage
8399   for (i = 0; i < MAX_PLAYERS; i++)
8400     if (tape->player_participates[i])
8401       store_participating_players |= (1 << i);
8402
8403   putFile32BitBE(file, tape->random_seed);
8404   putFile32BitBE(file, tape->date);
8405   putFile32BitBE(file, tape->length);
8406
8407   putFile8Bit(file, store_participating_players);
8408
8409   putFile8Bit(file, getTapeActionValue(tape));
8410
8411   putFile8Bit(file, tape->property_bits);
8412
8413   // unused bytes not at the end here for 4-byte alignment of engine_version
8414   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8415
8416   putFileVersion(file, tape->engine_version);
8417 }
8418
8419 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8420 {
8421   putFile8Bit(file, tape->scr_fieldx);
8422   putFile8Bit(file, tape->scr_fieldy);
8423 }
8424
8425 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8426 {
8427   int level_identifier_size = strlen(tape->level_identifier) + 1;
8428   int i;
8429
8430   putFile16BitBE(file, level_identifier_size);
8431
8432   for (i = 0; i < level_identifier_size; i++)
8433     putFile8Bit(file, tape->level_identifier[i]);
8434
8435   putFile16BitBE(file, tape->level_nr);
8436 }
8437
8438 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8439 {
8440   int i, j;
8441
8442   for (i = 0; i < tape->length; i++)
8443   {
8444     if (tape->use_key_actions)
8445     {
8446       for (j = 0; j < MAX_PLAYERS; j++)
8447         if (tape->player_participates[j])
8448           putFile8Bit(file, tape->pos[i].action[j]);
8449     }
8450
8451     if (tape->use_mouse_actions)
8452     {
8453       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8454       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8455       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8456     }
8457
8458     putFile8Bit(file, tape->pos[i].delay);
8459   }
8460 }
8461
8462 void SaveTapeToFilename(char *filename)
8463 {
8464   FILE *file;
8465   int tape_pos_size;
8466   int info_chunk_size;
8467   int body_chunk_size;
8468
8469   if (!(file = fopen(filename, MODE_WRITE)))
8470   {
8471     Warn("cannot save level recording file '%s'", filename);
8472
8473     return;
8474   }
8475
8476   tape_pos_size = getTapePosSize(&tape);
8477
8478   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8479   body_chunk_size = tape_pos_size * tape.length;
8480
8481   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8482   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8483
8484   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8485   SaveTape_VERS(file, &tape);
8486
8487   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8488   SaveTape_HEAD(file, &tape);
8489
8490   if (checkSaveTape_SCRN(&tape))
8491   {
8492     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8493     SaveTape_SCRN(file, &tape);
8494   }
8495
8496   putFileChunkBE(file, "INFO", info_chunk_size);
8497   SaveTape_INFO(file, &tape);
8498
8499   putFileChunkBE(file, "BODY", body_chunk_size);
8500   SaveTape_BODY(file, &tape);
8501
8502   fclose(file);
8503
8504   SetFilePermissions(filename, PERMS_PRIVATE);
8505 }
8506
8507 static void SaveTapeExt(char *filename)
8508 {
8509   int i;
8510
8511   tape.file_version = FILE_VERSION_ACTUAL;
8512   tape.game_version = GAME_VERSION_ACTUAL;
8513
8514   tape.num_participating_players = 0;
8515
8516   // count number of participating players
8517   for (i = 0; i < MAX_PLAYERS; i++)
8518     if (tape.player_participates[i])
8519       tape.num_participating_players++;
8520
8521   SaveTapeToFilename(filename);
8522
8523   tape.changed = FALSE;
8524 }
8525
8526 void SaveTape(int nr)
8527 {
8528   char *filename = getTapeFilename(nr);
8529
8530   InitTapeDirectory(leveldir_current->subdir);
8531
8532   SaveTapeExt(filename);
8533 }
8534
8535 void SaveScoreTape(int nr)
8536 {
8537   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8538
8539   // used instead of "leveldir_current->subdir" (for network games)
8540   InitScoreTapeDirectory(levelset.identifier, nr);
8541
8542   SaveTapeExt(filename);
8543 }
8544
8545 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8546                                   unsigned int req_state_added)
8547 {
8548   char *filename = getTapeFilename(nr);
8549   boolean new_tape = !fileExists(filename);
8550   boolean tape_saved = FALSE;
8551
8552   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8553   {
8554     SaveTape(nr);
8555
8556     if (new_tape)
8557       Request(msg_saved, REQ_CONFIRM | req_state_added);
8558
8559     tape_saved = TRUE;
8560   }
8561
8562   return tape_saved;
8563 }
8564
8565 boolean SaveTapeChecked(int nr)
8566 {
8567   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8568 }
8569
8570 boolean SaveTapeChecked_LevelSolved(int nr)
8571 {
8572   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8573                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8574 }
8575
8576 void DumpTape(struct TapeInfo *tape)
8577 {
8578   int tape_frame_counter;
8579   int i, j;
8580
8581   if (tape->no_valid_file)
8582   {
8583     Warn("cannot dump -- no valid tape file found");
8584
8585     return;
8586   }
8587
8588   PrintLine("-", 79);
8589
8590   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8591         tape->level_nr, tape->file_version, tape->game_version);
8592   Print("                  (effective engine version %08d)\n",
8593         tape->engine_version);
8594   Print("Level series identifier: '%s'\n", tape->level_identifier);
8595
8596   Print("Special tape properties: ");
8597   if (tape->property_bits == TAPE_PROPERTY_NONE)
8598     Print("[none]");
8599   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8600     Print("[em_random_bug]");
8601   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8602     Print("[game_speed]");
8603   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8604     Print("[pause]");
8605   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8606     Print("[single_step]");
8607   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8608     Print("[snapshot]");
8609   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8610     Print("[replayed]");
8611   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8612     Print("[tas_keys]");
8613   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8614     Print("[small_graphics]");
8615   Print("\n");
8616
8617   int year2 = tape->date / 10000;
8618   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8619   int month_index_raw = (tape->date / 100) % 100;
8620   int month_index = month_index_raw % 12;       // prevent invalid index
8621   int month = month_index + 1;
8622   int day = tape->date % 100;
8623
8624   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8625
8626   PrintLine("-", 79);
8627
8628   tape_frame_counter = 0;
8629
8630   for (i = 0; i < tape->length; i++)
8631   {
8632     if (i >= MAX_TAPE_LEN)
8633       break;
8634
8635     Print("%04d: ", i);
8636
8637     for (j = 0; j < MAX_PLAYERS; j++)
8638     {
8639       if (tape->player_participates[j])
8640       {
8641         int action = tape->pos[i].action[j];
8642
8643         Print("%d:%02x ", j, action);
8644         Print("[%c%c%c%c|%c%c] - ",
8645               (action & JOY_LEFT ? '<' : ' '),
8646               (action & JOY_RIGHT ? '>' : ' '),
8647               (action & JOY_UP ? '^' : ' '),
8648               (action & JOY_DOWN ? 'v' : ' '),
8649               (action & JOY_BUTTON_1 ? '1' : ' '),
8650               (action & JOY_BUTTON_2 ? '2' : ' '));
8651       }
8652     }
8653
8654     Print("(%03d) ", tape->pos[i].delay);
8655     Print("[%05d]\n", tape_frame_counter);
8656
8657     tape_frame_counter += tape->pos[i].delay;
8658   }
8659
8660   PrintLine("-", 79);
8661 }
8662
8663 void DumpTapes(void)
8664 {
8665   static LevelDirTree *dumptape_leveldir = NULL;
8666
8667   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8668                                                 global.dumptape_leveldir);
8669
8670   if (dumptape_leveldir == NULL)
8671     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8672
8673   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8674       global.dumptape_level_nr > dumptape_leveldir->last_level)
8675     Fail("no such level number: %d", global.dumptape_level_nr);
8676
8677   leveldir_current = dumptape_leveldir;
8678
8679   if (options.mytapes)
8680     LoadTape(global.dumptape_level_nr);
8681   else
8682     LoadSolutionTape(global.dumptape_level_nr);
8683
8684   DumpTape(&tape);
8685
8686   CloseAllAndExit(0);
8687 }
8688
8689
8690 // ============================================================================
8691 // score file functions
8692 // ============================================================================
8693
8694 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8695 {
8696   int i;
8697
8698   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8699   {
8700     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8701     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8702     scores->entry[i].score = 0;
8703     scores->entry[i].time = 0;
8704
8705     scores->entry[i].id = -1;
8706     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
8707     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
8708     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
8709     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8710     strcpy(scores->entry[i].country_code, "??");
8711   }
8712
8713   scores->num_entries = 0;
8714   scores->last_added = -1;
8715   scores->last_added_local = -1;
8716
8717   scores->updated = FALSE;
8718   scores->uploaded = FALSE;
8719   scores->tape_downloaded = FALSE;
8720   scores->force_last_added = FALSE;
8721
8722   // The following values are intentionally not reset here:
8723   // - last_level_nr
8724   // - last_entry_nr
8725   // - next_level_nr
8726   // - continue_playing
8727   // - continue_on_return
8728 }
8729
8730 static void setScoreInfoToDefaults(void)
8731 {
8732   setScoreInfoToDefaultsExt(&scores);
8733 }
8734
8735 static void setServerScoreInfoToDefaults(void)
8736 {
8737   setScoreInfoToDefaultsExt(&server_scores);
8738 }
8739
8740 static void LoadScore_OLD(int nr)
8741 {
8742   int i;
8743   char *filename = getScoreFilename(nr);
8744   char cookie[MAX_LINE_LEN];
8745   char line[MAX_LINE_LEN];
8746   char *line_ptr;
8747   FILE *file;
8748
8749   if (!(file = fopen(filename, MODE_READ)))
8750     return;
8751
8752   // check file identifier
8753   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8754     cookie[0] = '\0';
8755   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8756     cookie[strlen(cookie) - 1] = '\0';
8757
8758   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8759   {
8760     Warn("unknown format of score file '%s'", filename);
8761
8762     fclose(file);
8763
8764     return;
8765   }
8766
8767   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8768   {
8769     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8770       Warn("fscanf() failed; %s", strerror(errno));
8771
8772     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8773       line[0] = '\0';
8774
8775     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8776       line[strlen(line) - 1] = '\0';
8777
8778     for (line_ptr = line; *line_ptr; line_ptr++)
8779     {
8780       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8781       {
8782         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8783         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8784         break;
8785       }
8786     }
8787   }
8788
8789   fclose(file);
8790 }
8791
8792 static void ConvertScore_OLD(void)
8793 {
8794   // only convert score to time for levels that rate playing time over score
8795   if (!level.rate_time_over_score)
8796     return;
8797
8798   // convert old score to playing time for score-less levels (like Supaplex)
8799   int time_final_max = 999;
8800   int i;
8801
8802   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8803   {
8804     int score = scores.entry[i].score;
8805
8806     if (score > 0 && score < time_final_max)
8807       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8808   }
8809 }
8810
8811 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8812 {
8813   scores->file_version = getFileVersion(file);
8814   scores->game_version = getFileVersion(file);
8815
8816   return chunk_size;
8817 }
8818
8819 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8820 {
8821   char *level_identifier = NULL;
8822   int level_identifier_size;
8823   int i;
8824
8825   level_identifier_size = getFile16BitBE(file);
8826
8827   level_identifier = checked_malloc(level_identifier_size);
8828
8829   for (i = 0; i < level_identifier_size; i++)
8830     level_identifier[i] = getFile8Bit(file);
8831
8832   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8833   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8834
8835   checked_free(level_identifier);
8836
8837   scores->level_nr = getFile16BitBE(file);
8838   scores->num_entries = getFile16BitBE(file);
8839
8840   chunk_size = 2 + level_identifier_size + 2 + 2;
8841
8842   return chunk_size;
8843 }
8844
8845 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8846 {
8847   int i, j;
8848
8849   for (i = 0; i < scores->num_entries; i++)
8850   {
8851     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8852       scores->entry[i].name[j] = getFile8Bit(file);
8853
8854     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8855   }
8856
8857   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8858
8859   return chunk_size;
8860 }
8861
8862 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8863 {
8864   int i;
8865
8866   for (i = 0; i < scores->num_entries; i++)
8867     scores->entry[i].score = getFile16BitBE(file);
8868
8869   chunk_size = scores->num_entries * 2;
8870
8871   return chunk_size;
8872 }
8873
8874 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8875 {
8876   int i;
8877
8878   for (i = 0; i < scores->num_entries; i++)
8879     scores->entry[i].score = getFile32BitBE(file);
8880
8881   chunk_size = scores->num_entries * 4;
8882
8883   return chunk_size;
8884 }
8885
8886 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8887 {
8888   int i;
8889
8890   for (i = 0; i < scores->num_entries; i++)
8891     scores->entry[i].time = getFile32BitBE(file);
8892
8893   chunk_size = scores->num_entries * 4;
8894
8895   return chunk_size;
8896 }
8897
8898 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8899 {
8900   int i, j;
8901
8902   for (i = 0; i < scores->num_entries; i++)
8903   {
8904     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8905       scores->entry[i].tape_basename[j] = getFile8Bit(file);
8906
8907     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8908   }
8909
8910   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8911
8912   return chunk_size;
8913 }
8914
8915 void LoadScore(int nr)
8916 {
8917   char *filename = getScoreFilename(nr);
8918   char cookie[MAX_LINE_LEN];
8919   char chunk_name[CHUNK_ID_LEN + 1];
8920   int chunk_size;
8921   boolean old_score_file_format = FALSE;
8922   File *file;
8923
8924   // always start with reliable default values
8925   setScoreInfoToDefaults();
8926
8927   if (!(file = openFile(filename, MODE_READ)))
8928     return;
8929
8930   getFileChunkBE(file, chunk_name, NULL);
8931   if (strEqual(chunk_name, "RND1"))
8932   {
8933     getFile32BitBE(file);               // not used
8934
8935     getFileChunkBE(file, chunk_name, NULL);
8936     if (!strEqual(chunk_name, "SCOR"))
8937     {
8938       Warn("unknown format of score file '%s'", filename);
8939
8940       closeFile(file);
8941
8942       return;
8943     }
8944   }
8945   else  // check for old file format with cookie string
8946   {
8947     strcpy(cookie, chunk_name);
8948     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8949       cookie[4] = '\0';
8950     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8951       cookie[strlen(cookie) - 1] = '\0';
8952
8953     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8954     {
8955       Warn("unknown format of score file '%s'", filename);
8956
8957       closeFile(file);
8958
8959       return;
8960     }
8961
8962     old_score_file_format = TRUE;
8963   }
8964
8965   if (old_score_file_format)
8966   {
8967     // score files from versions before 4.2.4.0 without chunk structure
8968     LoadScore_OLD(nr);
8969
8970     // convert score to time, if possible (mainly for Supaplex levels)
8971     ConvertScore_OLD();
8972   }
8973   else
8974   {
8975     static struct
8976     {
8977       char *name;
8978       int size;
8979       int (*loader)(File *, int, struct ScoreInfo *);
8980     }
8981     chunk_info[] =
8982     {
8983       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
8984       { "INFO", -1,                     LoadScore_INFO },
8985       { "NAME", -1,                     LoadScore_NAME },
8986       { "SCOR", -1,                     LoadScore_SCOR },
8987       { "SC4R", -1,                     LoadScore_SC4R },
8988       { "TIME", -1,                     LoadScore_TIME },
8989       { "TAPE", -1,                     LoadScore_TAPE },
8990
8991       {  NULL,  0,                      NULL }
8992     };
8993
8994     while (getFileChunkBE(file, chunk_name, &chunk_size))
8995     {
8996       int i = 0;
8997
8998       while (chunk_info[i].name != NULL &&
8999              !strEqual(chunk_name, chunk_info[i].name))
9000         i++;
9001
9002       if (chunk_info[i].name == NULL)
9003       {
9004         Warn("unknown chunk '%s' in score file '%s'",
9005               chunk_name, filename);
9006
9007         ReadUnusedBytesFromFile(file, chunk_size);
9008       }
9009       else if (chunk_info[i].size != -1 &&
9010                chunk_info[i].size != chunk_size)
9011       {
9012         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9013               chunk_size, chunk_name, filename);
9014
9015         ReadUnusedBytesFromFile(file, chunk_size);
9016       }
9017       else
9018       {
9019         // call function to load this score chunk
9020         int chunk_size_expected =
9021           (chunk_info[i].loader)(file, chunk_size, &scores);
9022
9023         // the size of some chunks cannot be checked before reading other
9024         // chunks first (like "HEAD" and "BODY") that contain some header
9025         // information, so check them here
9026         if (chunk_size_expected != chunk_size)
9027         {
9028           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9029                 chunk_size, chunk_name, filename);
9030         }
9031       }
9032     }
9033   }
9034
9035   closeFile(file);
9036 }
9037
9038 #if ENABLE_HISTORIC_CHUNKS
9039 void SaveScore_OLD(int nr)
9040 {
9041   int i;
9042   char *filename = getScoreFilename(nr);
9043   FILE *file;
9044
9045   // used instead of "leveldir_current->subdir" (for network games)
9046   InitScoreDirectory(levelset.identifier);
9047
9048   if (!(file = fopen(filename, MODE_WRITE)))
9049   {
9050     Warn("cannot save score for level %d", nr);
9051
9052     return;
9053   }
9054
9055   fprintf(file, "%s\n\n", SCORE_COOKIE);
9056
9057   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9058     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9059
9060   fclose(file);
9061
9062   SetFilePermissions(filename, PERMS_PRIVATE);
9063 }
9064 #endif
9065
9066 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9067 {
9068   putFileVersion(file, scores->file_version);
9069   putFileVersion(file, scores->game_version);
9070 }
9071
9072 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9073 {
9074   int level_identifier_size = strlen(scores->level_identifier) + 1;
9075   int i;
9076
9077   putFile16BitBE(file, level_identifier_size);
9078
9079   for (i = 0; i < level_identifier_size; i++)
9080     putFile8Bit(file, scores->level_identifier[i]);
9081
9082   putFile16BitBE(file, scores->level_nr);
9083   putFile16BitBE(file, scores->num_entries);
9084 }
9085
9086 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9087 {
9088   int i, j;
9089
9090   for (i = 0; i < scores->num_entries; i++)
9091   {
9092     int name_size = strlen(scores->entry[i].name);
9093
9094     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9095       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9096   }
9097 }
9098
9099 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9100 {
9101   int i;
9102
9103   for (i = 0; i < scores->num_entries; i++)
9104     putFile16BitBE(file, scores->entry[i].score);
9105 }
9106
9107 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9108 {
9109   int i;
9110
9111   for (i = 0; i < scores->num_entries; i++)
9112     putFile32BitBE(file, scores->entry[i].score);
9113 }
9114
9115 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9116 {
9117   int i;
9118
9119   for (i = 0; i < scores->num_entries; i++)
9120     putFile32BitBE(file, scores->entry[i].time);
9121 }
9122
9123 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9124 {
9125   int i, j;
9126
9127   for (i = 0; i < scores->num_entries; i++)
9128   {
9129     int size = strlen(scores->entry[i].tape_basename);
9130
9131     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9132       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9133   }
9134 }
9135
9136 static void SaveScoreToFilename(char *filename)
9137 {
9138   FILE *file;
9139   int info_chunk_size;
9140   int name_chunk_size;
9141   int scor_chunk_size;
9142   int sc4r_chunk_size;
9143   int time_chunk_size;
9144   int tape_chunk_size;
9145   boolean has_large_score_values;
9146   int i;
9147
9148   if (!(file = fopen(filename, MODE_WRITE)))
9149   {
9150     Warn("cannot save score file '%s'", filename);
9151
9152     return;
9153   }
9154
9155   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9156   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9157   scor_chunk_size = scores.num_entries * 2;
9158   sc4r_chunk_size = scores.num_entries * 4;
9159   time_chunk_size = scores.num_entries * 4;
9160   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9161
9162   has_large_score_values = FALSE;
9163   for (i = 0; i < scores.num_entries; i++)
9164     if (scores.entry[i].score > 0xffff)
9165       has_large_score_values = TRUE;
9166
9167   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9168   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9169
9170   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9171   SaveScore_VERS(file, &scores);
9172
9173   putFileChunkBE(file, "INFO", info_chunk_size);
9174   SaveScore_INFO(file, &scores);
9175
9176   putFileChunkBE(file, "NAME", name_chunk_size);
9177   SaveScore_NAME(file, &scores);
9178
9179   if (has_large_score_values)
9180   {
9181     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9182     SaveScore_SC4R(file, &scores);
9183   }
9184   else
9185   {
9186     putFileChunkBE(file, "SCOR", scor_chunk_size);
9187     SaveScore_SCOR(file, &scores);
9188   }
9189
9190   putFileChunkBE(file, "TIME", time_chunk_size);
9191   SaveScore_TIME(file, &scores);
9192
9193   putFileChunkBE(file, "TAPE", tape_chunk_size);
9194   SaveScore_TAPE(file, &scores);
9195
9196   fclose(file);
9197
9198   SetFilePermissions(filename, PERMS_PRIVATE);
9199 }
9200
9201 void SaveScore(int nr)
9202 {
9203   char *filename = getScoreFilename(nr);
9204   int i;
9205
9206   // used instead of "leveldir_current->subdir" (for network games)
9207   InitScoreDirectory(levelset.identifier);
9208
9209   scores.file_version = FILE_VERSION_ACTUAL;
9210   scores.game_version = GAME_VERSION_ACTUAL;
9211
9212   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9213   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9214   scores.level_nr = level_nr;
9215
9216   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9217     if (scores.entry[i].score == 0 &&
9218         scores.entry[i].time == 0 &&
9219         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9220       break;
9221
9222   scores.num_entries = i;
9223
9224   if (scores.num_entries == 0)
9225     return;
9226
9227   SaveScoreToFilename(filename);
9228 }
9229
9230 static void LoadServerScoreFromCache(int nr)
9231 {
9232   struct ScoreEntry score_entry;
9233   struct
9234   {
9235     void *value;
9236     boolean is_string;
9237     int string_size;
9238   }
9239   score_mapping[] =
9240   {
9241     { &score_entry.score,               FALSE,  0                       },
9242     { &score_entry.time,                FALSE,  0                       },
9243     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9244     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9245     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9246     { &score_entry.id,                  FALSE,  0                       },
9247     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9248     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9249     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9250     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9251
9252     { NULL,                             FALSE,  0                       }
9253   };
9254   char *filename = getScoreCacheFilename(nr);
9255   SetupFileHash *score_hash = loadSetupFileHash(filename);
9256   int i, j;
9257
9258   server_scores.num_entries = 0;
9259
9260   if (score_hash == NULL)
9261     return;
9262
9263   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9264   {
9265     score_entry = server_scores.entry[i];
9266
9267     for (j = 0; score_mapping[j].value != NULL; j++)
9268     {
9269       char token[10];
9270
9271       sprintf(token, "%02d.%d", i, j);
9272
9273       char *value = getHashEntry(score_hash, token);
9274
9275       if (value == NULL)
9276         continue;
9277
9278       if (score_mapping[j].is_string)
9279       {
9280         char *score_value = (char *)score_mapping[j].value;
9281         int value_size = score_mapping[j].string_size;
9282
9283         strncpy(score_value, value, value_size);
9284         score_value[value_size] = '\0';
9285       }
9286       else
9287       {
9288         int *score_value = (int *)score_mapping[j].value;
9289
9290         *score_value = atoi(value);
9291       }
9292
9293       server_scores.num_entries = i + 1;
9294     }
9295
9296     server_scores.entry[i] = score_entry;
9297   }
9298
9299   freeSetupFileHash(score_hash);
9300 }
9301
9302 void LoadServerScore(int nr, boolean download_score)
9303 {
9304   if (!setup.use_api_server)
9305     return;
9306
9307   // always start with reliable default values
9308   setServerScoreInfoToDefaults();
9309
9310   // 1st step: load server scores from cache file (which may not exist)
9311   // (this should prevent reading it while the thread is writing to it)
9312   LoadServerScoreFromCache(nr);
9313
9314   if (download_score && runtime.use_api_server)
9315   {
9316     // 2nd step: download server scores from score server to cache file
9317     // (as thread, as it might time out if the server is not reachable)
9318     ApiGetScoreAsThread(nr);
9319   }
9320 }
9321
9322 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9323 {
9324   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9325
9326   // if score tape not uploaded, ask for uploading missing tapes later
9327   if (!setup.has_remaining_tapes)
9328     setup.ask_for_remaining_tapes = TRUE;
9329
9330   setup.provide_uploading_tapes = TRUE;
9331   setup.has_remaining_tapes = TRUE;
9332
9333   SaveSetup_ServerSetup();
9334 }
9335
9336 void SaveServerScore(int nr, boolean tape_saved)
9337 {
9338   if (!runtime.use_api_server)
9339   {
9340     PrepareScoreTapesForUpload(leveldir_current->subdir);
9341
9342     return;
9343   }
9344
9345   ApiAddScoreAsThread(nr, tape_saved, NULL);
9346 }
9347
9348 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9349                              char *score_tape_filename)
9350 {
9351   if (!runtime.use_api_server)
9352     return;
9353
9354   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9355 }
9356
9357 void LoadLocalAndServerScore(int nr, boolean download_score)
9358 {
9359   int last_added_local = scores.last_added_local;
9360   boolean force_last_added = scores.force_last_added;
9361
9362   // needed if only showing server scores
9363   setScoreInfoToDefaults();
9364
9365   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9366     LoadScore(nr);
9367
9368   // restore last added local score entry (before merging server scores)
9369   scores.last_added = scores.last_added_local = last_added_local;
9370
9371   if (setup.use_api_server &&
9372       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9373   {
9374     // load server scores from cache file and trigger update from server
9375     LoadServerScore(nr, download_score);
9376
9377     // merge local scores with scores from server
9378     MergeServerScore();
9379   }
9380
9381   if (force_last_added)
9382     scores.force_last_added = force_last_added;
9383 }
9384
9385
9386 // ============================================================================
9387 // setup file functions
9388 // ============================================================================
9389
9390 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9391
9392
9393 static struct TokenInfo global_setup_tokens[] =
9394 {
9395   {
9396     TYPE_STRING,
9397     &setup.player_name,                         "player_name"
9398   },
9399   {
9400     TYPE_SWITCH,
9401     &setup.multiple_users,                      "multiple_users"
9402   },
9403   {
9404     TYPE_SWITCH,
9405     &setup.sound,                               "sound"
9406   },
9407   {
9408     TYPE_SWITCH,
9409     &setup.sound_loops,                         "repeating_sound_loops"
9410   },
9411   {
9412     TYPE_SWITCH,
9413     &setup.sound_music,                         "background_music"
9414   },
9415   {
9416     TYPE_SWITCH,
9417     &setup.sound_simple,                        "simple_sound_effects"
9418   },
9419   {
9420     TYPE_SWITCH,
9421     &setup.toons,                               "toons"
9422   },
9423   {
9424     TYPE_SWITCH,
9425     &setup.scroll_delay,                        "scroll_delay"
9426   },
9427   {
9428     TYPE_SWITCH,
9429     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9430   },
9431   {
9432     TYPE_INTEGER,
9433     &setup.scroll_delay_value,                  "scroll_delay_value"
9434   },
9435   {
9436     TYPE_STRING,
9437     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9438   },
9439   {
9440     TYPE_INTEGER,
9441     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9442   },
9443   {
9444     TYPE_SWITCH,
9445     &setup.fade_screens,                        "fade_screens"
9446   },
9447   {
9448     TYPE_SWITCH,
9449     &setup.autorecord,                          "automatic_tape_recording"
9450   },
9451   {
9452     TYPE_SWITCH,
9453     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9454   },
9455   {
9456     TYPE_SWITCH,
9457     &setup.show_titlescreen,                    "show_titlescreen"
9458   },
9459   {
9460     TYPE_SWITCH,
9461     &setup.quick_doors,                         "quick_doors"
9462   },
9463   {
9464     TYPE_SWITCH,
9465     &setup.team_mode,                           "team_mode"
9466   },
9467   {
9468     TYPE_SWITCH,
9469     &setup.handicap,                            "handicap"
9470   },
9471   {
9472     TYPE_SWITCH,
9473     &setup.skip_levels,                         "skip_levels"
9474   },
9475   {
9476     TYPE_SWITCH,
9477     &setup.increment_levels,                    "increment_levels"
9478   },
9479   {
9480     TYPE_SWITCH,
9481     &setup.auto_play_next_level,                "auto_play_next_level"
9482   },
9483   {
9484     TYPE_SWITCH,
9485     &setup.count_score_after_game,              "count_score_after_game"
9486   },
9487   {
9488     TYPE_SWITCH,
9489     &setup.show_scores_after_game,              "show_scores_after_game"
9490   },
9491   {
9492     TYPE_SWITCH,
9493     &setup.time_limit,                          "time_limit"
9494   },
9495   {
9496     TYPE_SWITCH,
9497     &setup.fullscreen,                          "fullscreen"
9498   },
9499   {
9500     TYPE_INTEGER,
9501     &setup.window_scaling_percent,              "window_scaling_percent"
9502   },
9503   {
9504     TYPE_STRING,
9505     &setup.window_scaling_quality,              "window_scaling_quality"
9506   },
9507   {
9508     TYPE_STRING,
9509     &setup.screen_rendering_mode,               "screen_rendering_mode"
9510   },
9511   {
9512     TYPE_STRING,
9513     &setup.vsync_mode,                          "vsync_mode"
9514   },
9515   {
9516     TYPE_SWITCH,
9517     &setup.ask_on_escape,                       "ask_on_escape"
9518   },
9519   {
9520     TYPE_SWITCH,
9521     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
9522   },
9523   {
9524     TYPE_SWITCH,
9525     &setup.ask_on_game_over,                    "ask_on_game_over"
9526   },
9527   {
9528     TYPE_SWITCH,
9529     &setup.ask_on_quit_game,                    "ask_on_quit_game"
9530   },
9531   {
9532     TYPE_SWITCH,
9533     &setup.ask_on_quit_program,                 "ask_on_quit_program"
9534   },
9535   {
9536     TYPE_SWITCH,
9537     &setup.quick_switch,                        "quick_player_switch"
9538   },
9539   {
9540     TYPE_SWITCH,
9541     &setup.input_on_focus,                      "input_on_focus"
9542   },
9543   {
9544     TYPE_SWITCH,
9545     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
9546   },
9547   {
9548     TYPE_SWITCH,
9549     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
9550   },
9551   {
9552     TYPE_SWITCH,
9553     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
9554   },
9555   {
9556     TYPE_SWITCH,
9557     &setup.game_speed_extended,                 "game_speed_extended"
9558   },
9559   {
9560     TYPE_INTEGER,
9561     &setup.game_frame_delay,                    "game_frame_delay"
9562   },
9563   {
9564     TYPE_SWITCH,
9565     &setup.sp_show_border_elements,             "sp_show_border_elements"
9566   },
9567   {
9568     TYPE_SWITCH,
9569     &setup.small_game_graphics,                 "small_game_graphics"
9570   },
9571   {
9572     TYPE_SWITCH,
9573     &setup.show_load_save_buttons,              "show_load_save_buttons"
9574   },
9575   {
9576     TYPE_SWITCH,
9577     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
9578   },
9579   {
9580     TYPE_STRING,
9581     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
9582   },
9583   {
9584     TYPE_STRING,
9585     &setup.graphics_set,                        "graphics_set"
9586   },
9587   {
9588     TYPE_STRING,
9589     &setup.sounds_set,                          "sounds_set"
9590   },
9591   {
9592     TYPE_STRING,
9593     &setup.music_set,                           "music_set"
9594   },
9595   {
9596     TYPE_SWITCH3,
9597     &setup.override_level_graphics,             "override_level_graphics"
9598   },
9599   {
9600     TYPE_SWITCH3,
9601     &setup.override_level_sounds,               "override_level_sounds"
9602   },
9603   {
9604     TYPE_SWITCH3,
9605     &setup.override_level_music,                "override_level_music"
9606   },
9607   {
9608     TYPE_INTEGER,
9609     &setup.volume_simple,                       "volume_simple"
9610   },
9611   {
9612     TYPE_INTEGER,
9613     &setup.volume_loops,                        "volume_loops"
9614   },
9615   {
9616     TYPE_INTEGER,
9617     &setup.volume_music,                        "volume_music"
9618   },
9619   {
9620     TYPE_SWITCH,
9621     &setup.network_mode,                        "network_mode"
9622   },
9623   {
9624     TYPE_PLAYER,
9625     &setup.network_player_nr,                   "network_player"
9626   },
9627   {
9628     TYPE_STRING,
9629     &setup.network_server_hostname,             "network_server_hostname"
9630   },
9631   {
9632     TYPE_STRING,
9633     &setup.touch.control_type,                  "touch.control_type"
9634   },
9635   {
9636     TYPE_INTEGER,
9637     &setup.touch.move_distance,                 "touch.move_distance"
9638   },
9639   {
9640     TYPE_INTEGER,
9641     &setup.touch.drop_distance,                 "touch.drop_distance"
9642   },
9643   {
9644     TYPE_INTEGER,
9645     &setup.touch.transparency,                  "touch.transparency"
9646   },
9647   {
9648     TYPE_INTEGER,
9649     &setup.touch.draw_outlined,                 "touch.draw_outlined"
9650   },
9651   {
9652     TYPE_INTEGER,
9653     &setup.touch.draw_pressed,                  "touch.draw_pressed"
9654   },
9655   {
9656     TYPE_INTEGER,
9657     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
9658   },
9659   {
9660     TYPE_INTEGER,
9661     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
9662   },
9663   {
9664     TYPE_INTEGER,
9665     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
9666   },
9667   {
9668     TYPE_INTEGER,
9669     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
9670   },
9671   {
9672     TYPE_SWITCH,
9673     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
9674   },
9675 };
9676
9677 static struct TokenInfo auto_setup_tokens[] =
9678 {
9679   {
9680     TYPE_INTEGER,
9681     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
9682   },
9683 };
9684
9685 static struct TokenInfo server_setup_tokens[] =
9686 {
9687   {
9688     TYPE_STRING,
9689     &setup.player_uuid,                         "player_uuid"
9690   },
9691   {
9692     TYPE_INTEGER,
9693     &setup.player_version,                      "player_version"
9694   },
9695   {
9696     TYPE_SWITCH,
9697     &setup.use_api_server,          TEST_PREFIX "use_api_server"
9698   },
9699   {
9700     TYPE_STRING,
9701     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
9702   },
9703   {
9704     TYPE_STRING,
9705     &setup.api_server_password,     TEST_PREFIX "api_server_password"
9706   },
9707   {
9708     TYPE_SWITCH,
9709     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9710   },
9711   {
9712     TYPE_SWITCH,
9713     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9714   },
9715   {
9716     TYPE_SWITCH,
9717     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9718   },
9719   {
9720     TYPE_SWITCH,
9721     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9722   },
9723   {
9724     TYPE_SWITCH,
9725     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
9726   },
9727 };
9728
9729 static struct TokenInfo editor_setup_tokens[] =
9730 {
9731   {
9732     TYPE_SWITCH,
9733     &setup.editor.el_classic,                   "editor.el_classic"
9734   },
9735   {
9736     TYPE_SWITCH,
9737     &setup.editor.el_custom,                    "editor.el_custom"
9738   },
9739   {
9740     TYPE_SWITCH,
9741     &setup.editor.el_user_defined,              "editor.el_user_defined"
9742   },
9743   {
9744     TYPE_SWITCH,
9745     &setup.editor.el_dynamic,                   "editor.el_dynamic"
9746   },
9747   {
9748     TYPE_SWITCH,
9749     &setup.editor.el_headlines,                 "editor.el_headlines"
9750   },
9751   {
9752     TYPE_SWITCH,
9753     &setup.editor.show_element_token,           "editor.show_element_token"
9754   },
9755   {
9756     TYPE_SWITCH,
9757     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
9758   },
9759 };
9760
9761 static struct TokenInfo editor_cascade_setup_tokens[] =
9762 {
9763   {
9764     TYPE_SWITCH,
9765     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
9766   },
9767   {
9768     TYPE_SWITCH,
9769     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
9770   },
9771   {
9772     TYPE_SWITCH,
9773     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
9774   },
9775   {
9776     TYPE_SWITCH,
9777     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
9778   },
9779   {
9780     TYPE_SWITCH,
9781     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
9782   },
9783   {
9784     TYPE_SWITCH,
9785     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
9786   },
9787   {
9788     TYPE_SWITCH,
9789     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
9790   },
9791   {
9792     TYPE_SWITCH,
9793     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
9794   },
9795   {
9796     TYPE_SWITCH,
9797     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
9798   },
9799   {
9800     TYPE_SWITCH,
9801     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
9802   },
9803   {
9804     TYPE_SWITCH,
9805     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
9806   },
9807   {
9808     TYPE_SWITCH,
9809     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
9810   },
9811   {
9812     TYPE_SWITCH,
9813     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
9814   },
9815   {
9816     TYPE_SWITCH,
9817     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
9818   },
9819   {
9820     TYPE_SWITCH,
9821     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
9822   },
9823   {
9824     TYPE_SWITCH,
9825     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
9826   },
9827   {
9828     TYPE_SWITCH,
9829     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
9830   },
9831   {
9832     TYPE_SWITCH,
9833     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
9834   },
9835 };
9836
9837 static struct TokenInfo shortcut_setup_tokens[] =
9838 {
9839   {
9840     TYPE_KEY_X11,
9841     &setup.shortcut.save_game,                  "shortcut.save_game"
9842   },
9843   {
9844     TYPE_KEY_X11,
9845     &setup.shortcut.load_game,                  "shortcut.load_game"
9846   },
9847   {
9848     TYPE_KEY_X11,
9849     &setup.shortcut.restart_game,               "shortcut.restart_game"
9850   },
9851   {
9852     TYPE_KEY_X11,
9853     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
9854   },
9855   {
9856     TYPE_KEY_X11,
9857     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
9858   },
9859   {
9860     TYPE_KEY_X11,
9861     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
9862   },
9863   {
9864     TYPE_KEY_X11,
9865     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
9866   },
9867   {
9868     TYPE_KEY_X11,
9869     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
9870   },
9871   {
9872     TYPE_KEY_X11,
9873     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
9874   },
9875   {
9876     TYPE_KEY_X11,
9877     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
9878   },
9879   {
9880     TYPE_KEY_X11,
9881     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
9882   },
9883   {
9884     TYPE_KEY_X11,
9885     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
9886   },
9887   {
9888     TYPE_KEY_X11,
9889     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
9890   },
9891   {
9892     TYPE_KEY_X11,
9893     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
9894   },
9895   {
9896     TYPE_KEY_X11,
9897     &setup.shortcut.tape_record,                "shortcut.tape_record"
9898   },
9899   {
9900     TYPE_KEY_X11,
9901     &setup.shortcut.tape_play,                  "shortcut.tape_play"
9902   },
9903   {
9904     TYPE_KEY_X11,
9905     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
9906   },
9907   {
9908     TYPE_KEY_X11,
9909     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
9910   },
9911   {
9912     TYPE_KEY_X11,
9913     &setup.shortcut.sound_music,                "shortcut.sound_music"
9914   },
9915   {
9916     TYPE_KEY_X11,
9917     &setup.shortcut.snap_left,                  "shortcut.snap_left"
9918   },
9919   {
9920     TYPE_KEY_X11,
9921     &setup.shortcut.snap_right,                 "shortcut.snap_right"
9922   },
9923   {
9924     TYPE_KEY_X11,
9925     &setup.shortcut.snap_up,                    "shortcut.snap_up"
9926   },
9927   {
9928     TYPE_KEY_X11,
9929     &setup.shortcut.snap_down,                  "shortcut.snap_down"
9930   },
9931 };
9932
9933 static struct SetupInputInfo setup_input;
9934 static struct TokenInfo player_setup_tokens[] =
9935 {
9936   {
9937     TYPE_BOOLEAN,
9938     &setup_input.use_joystick,                  ".use_joystick"
9939   },
9940   {
9941     TYPE_STRING,
9942     &setup_input.joy.device_name,               ".joy.device_name"
9943   },
9944   {
9945     TYPE_INTEGER,
9946     &setup_input.joy.xleft,                     ".joy.xleft"
9947   },
9948   {
9949     TYPE_INTEGER,
9950     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
9951   },
9952   {
9953     TYPE_INTEGER,
9954     &setup_input.joy.xright,                    ".joy.xright"
9955   },
9956   {
9957     TYPE_INTEGER,
9958     &setup_input.joy.yupper,                    ".joy.yupper"
9959   },
9960   {
9961     TYPE_INTEGER,
9962     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
9963   },
9964   {
9965     TYPE_INTEGER,
9966     &setup_input.joy.ylower,                    ".joy.ylower"
9967   },
9968   {
9969     TYPE_INTEGER,
9970     &setup_input.joy.snap,                      ".joy.snap_field"
9971   },
9972   {
9973     TYPE_INTEGER,
9974     &setup_input.joy.drop,                      ".joy.place_bomb"
9975   },
9976   {
9977     TYPE_KEY_X11,
9978     &setup_input.key.left,                      ".key.move_left"
9979   },
9980   {
9981     TYPE_KEY_X11,
9982     &setup_input.key.right,                     ".key.move_right"
9983   },
9984   {
9985     TYPE_KEY_X11,
9986     &setup_input.key.up,                        ".key.move_up"
9987   },
9988   {
9989     TYPE_KEY_X11,
9990     &setup_input.key.down,                      ".key.move_down"
9991   },
9992   {
9993     TYPE_KEY_X11,
9994     &setup_input.key.snap,                      ".key.snap_field"
9995   },
9996   {
9997     TYPE_KEY_X11,
9998     &setup_input.key.drop,                      ".key.place_bomb"
9999   },
10000 };
10001
10002 static struct TokenInfo system_setup_tokens[] =
10003 {
10004   {
10005     TYPE_STRING,
10006     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10007   },
10008   {
10009     TYPE_STRING,
10010     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10011   },
10012   {
10013     TYPE_STRING,
10014     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10015   },
10016   {
10017     TYPE_INTEGER,
10018     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10019   },
10020 };
10021
10022 static struct TokenInfo internal_setup_tokens[] =
10023 {
10024   {
10025     TYPE_STRING,
10026     &setup.internal.program_title,              "program_title"
10027   },
10028   {
10029     TYPE_STRING,
10030     &setup.internal.program_version,            "program_version"
10031   },
10032   {
10033     TYPE_STRING,
10034     &setup.internal.program_author,             "program_author"
10035   },
10036   {
10037     TYPE_STRING,
10038     &setup.internal.program_email,              "program_email"
10039   },
10040   {
10041     TYPE_STRING,
10042     &setup.internal.program_website,            "program_website"
10043   },
10044   {
10045     TYPE_STRING,
10046     &setup.internal.program_copyright,          "program_copyright"
10047   },
10048   {
10049     TYPE_STRING,
10050     &setup.internal.program_company,            "program_company"
10051   },
10052   {
10053     TYPE_STRING,
10054     &setup.internal.program_icon_file,          "program_icon_file"
10055   },
10056   {
10057     TYPE_STRING,
10058     &setup.internal.default_graphics_set,       "default_graphics_set"
10059   },
10060   {
10061     TYPE_STRING,
10062     &setup.internal.default_sounds_set,         "default_sounds_set"
10063   },
10064   {
10065     TYPE_STRING,
10066     &setup.internal.default_music_set,          "default_music_set"
10067   },
10068   {
10069     TYPE_STRING,
10070     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10071   },
10072   {
10073     TYPE_STRING,
10074     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10075   },
10076   {
10077     TYPE_STRING,
10078     &setup.internal.fallback_music_file,        "fallback_music_file"
10079   },
10080   {
10081     TYPE_STRING,
10082     &setup.internal.default_level_series,       "default_level_series"
10083   },
10084   {
10085     TYPE_INTEGER,
10086     &setup.internal.default_window_width,       "default_window_width"
10087   },
10088   {
10089     TYPE_INTEGER,
10090     &setup.internal.default_window_height,      "default_window_height"
10091   },
10092   {
10093     TYPE_BOOLEAN,
10094     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10095   },
10096   {
10097     TYPE_BOOLEAN,
10098     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10099   },
10100   {
10101     TYPE_BOOLEAN,
10102     &setup.internal.create_user_levelset,       "create_user_levelset"
10103   },
10104   {
10105     TYPE_BOOLEAN,
10106     &setup.internal.menu_game,                  "menu_game"
10107   },
10108   {
10109     TYPE_BOOLEAN,
10110     &setup.internal.menu_editor,                "menu_editor"
10111   },
10112   {
10113     TYPE_BOOLEAN,
10114     &setup.internal.menu_graphics,              "menu_graphics"
10115   },
10116   {
10117     TYPE_BOOLEAN,
10118     &setup.internal.menu_sound,                 "menu_sound"
10119   },
10120   {
10121     TYPE_BOOLEAN,
10122     &setup.internal.menu_artwork,               "menu_artwork"
10123   },
10124   {
10125     TYPE_BOOLEAN,
10126     &setup.internal.menu_input,                 "menu_input"
10127   },
10128   {
10129     TYPE_BOOLEAN,
10130     &setup.internal.menu_touch,                 "menu_touch"
10131   },
10132   {
10133     TYPE_BOOLEAN,
10134     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10135   },
10136   {
10137     TYPE_BOOLEAN,
10138     &setup.internal.menu_exit,                  "menu_exit"
10139   },
10140   {
10141     TYPE_BOOLEAN,
10142     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10143   },
10144   {
10145     TYPE_BOOLEAN,
10146     &setup.internal.info_title,                 "info_title"
10147   },
10148   {
10149     TYPE_BOOLEAN,
10150     &setup.internal.info_elements,              "info_elements"
10151   },
10152   {
10153     TYPE_BOOLEAN,
10154     &setup.internal.info_music,                 "info_music"
10155   },
10156   {
10157     TYPE_BOOLEAN,
10158     &setup.internal.info_credits,               "info_credits"
10159   },
10160   {
10161     TYPE_BOOLEAN,
10162     &setup.internal.info_program,               "info_program"
10163   },
10164   {
10165     TYPE_BOOLEAN,
10166     &setup.internal.info_version,               "info_version"
10167   },
10168   {
10169     TYPE_BOOLEAN,
10170     &setup.internal.info_levelset,              "info_levelset"
10171   },
10172   {
10173     TYPE_BOOLEAN,
10174     &setup.internal.info_exit,                  "info_exit"
10175   },
10176 };
10177
10178 static struct TokenInfo debug_setup_tokens[] =
10179 {
10180   {
10181     TYPE_INTEGER,
10182     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10183   },
10184   {
10185     TYPE_INTEGER,
10186     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10187   },
10188   {
10189     TYPE_INTEGER,
10190     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10191   },
10192   {
10193     TYPE_INTEGER,
10194     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10195   },
10196   {
10197     TYPE_INTEGER,
10198     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10199   },
10200   {
10201     TYPE_INTEGER,
10202     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10203   },
10204   {
10205     TYPE_INTEGER,
10206     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10207   },
10208   {
10209     TYPE_INTEGER,
10210     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10211   },
10212   {
10213     TYPE_INTEGER,
10214     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10215   },
10216   {
10217     TYPE_INTEGER,
10218     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10219   },
10220   {
10221     TYPE_KEY_X11,
10222     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10223   },
10224   {
10225     TYPE_KEY_X11,
10226     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10227   },
10228   {
10229     TYPE_KEY_X11,
10230     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10231   },
10232   {
10233     TYPE_KEY_X11,
10234     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10235   },
10236   {
10237     TYPE_KEY_X11,
10238     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10239   },
10240   {
10241     TYPE_KEY_X11,
10242     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10243   },
10244   {
10245     TYPE_KEY_X11,
10246     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10247   },
10248   {
10249     TYPE_KEY_X11,
10250     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10251   },
10252   {
10253     TYPE_KEY_X11,
10254     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10255   },
10256   {
10257     TYPE_KEY_X11,
10258     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10259   },
10260   {
10261     TYPE_BOOLEAN,
10262     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10263   {
10264     TYPE_BOOLEAN,
10265     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10266   },
10267   {
10268     TYPE_BOOLEAN,
10269     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10270   },
10271   {
10272     TYPE_SWITCH3,
10273     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10274   },
10275   {
10276     TYPE_INTEGER,
10277     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10278   },
10279 };
10280
10281 static struct TokenInfo options_setup_tokens[] =
10282 {
10283   {
10284     TYPE_BOOLEAN,
10285     &setup.options.verbose,                     "options.verbose"
10286   },
10287 };
10288
10289 static void setSetupInfoToDefaults(struct SetupInfo *si)
10290 {
10291   int i;
10292
10293   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10294
10295   si->multiple_users = TRUE;
10296
10297   si->sound = TRUE;
10298   si->sound_loops = TRUE;
10299   si->sound_music = TRUE;
10300   si->sound_simple = TRUE;
10301   si->toons = TRUE;
10302   si->scroll_delay = TRUE;
10303   si->forced_scroll_delay = FALSE;
10304   si->scroll_delay_value = STD_SCROLL_DELAY;
10305   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10306   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10307   si->fade_screens = TRUE;
10308   si->autorecord = TRUE;
10309   si->auto_pause_on_start = FALSE;
10310   si->show_titlescreen = TRUE;
10311   si->quick_doors = FALSE;
10312   si->team_mode = FALSE;
10313   si->handicap = TRUE;
10314   si->skip_levels = TRUE;
10315   si->increment_levels = TRUE;
10316   si->auto_play_next_level = TRUE;
10317   si->count_score_after_game = TRUE;
10318   si->show_scores_after_game = TRUE;
10319   si->time_limit = TRUE;
10320   si->fullscreen = FALSE;
10321   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10322   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10323   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10324   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10325   si->ask_on_escape = TRUE;
10326   si->ask_on_escape_editor = TRUE;
10327   si->ask_on_game_over = TRUE;
10328   si->ask_on_quit_game = TRUE;
10329   si->ask_on_quit_program = TRUE;
10330   si->quick_switch = FALSE;
10331   si->input_on_focus = FALSE;
10332   si->prefer_aga_graphics = TRUE;
10333   si->prefer_lowpass_sounds = FALSE;
10334   si->prefer_extra_panel_items = TRUE;
10335   si->game_speed_extended = FALSE;
10336   si->game_frame_delay = GAME_FRAME_DELAY;
10337   si->sp_show_border_elements = FALSE;
10338   si->small_game_graphics = FALSE;
10339   si->show_load_save_buttons = FALSE;
10340   si->show_undo_redo_buttons = FALSE;
10341   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10342
10343   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10344   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10345   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10346
10347   si->override_level_graphics = FALSE;
10348   si->override_level_sounds = FALSE;
10349   si->override_level_music = FALSE;
10350
10351   si->volume_simple = 100;              // percent
10352   si->volume_loops = 100;               // percent
10353   si->volume_music = 100;               // percent
10354
10355   si->network_mode = FALSE;
10356   si->network_player_nr = 0;            // first player
10357   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10358
10359   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10360   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10361   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10362   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10363   si->touch.draw_outlined = TRUE;
10364   si->touch.draw_pressed = TRUE;
10365
10366   for (i = 0; i < 2; i++)
10367   {
10368     char *default_grid_button[6][2] =
10369     {
10370       { "      ", "  ^^  " },
10371       { "      ", "  ^^  " },
10372       { "      ", "<<  >>" },
10373       { "      ", "<<  >>" },
10374       { "111222", "  vv  " },
10375       { "111222", "  vv  " }
10376     };
10377     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10378     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10379     int min_xsize = MIN(6, grid_xsize);
10380     int min_ysize = MIN(6, grid_ysize);
10381     int startx = grid_xsize - min_xsize;
10382     int starty = grid_ysize - min_ysize;
10383     int x, y;
10384
10385     // virtual buttons grid can only be set to defaults if video is initialized
10386     // (this will be repeated if virtual buttons are not loaded from setup file)
10387     if (video.initialized)
10388     {
10389       si->touch.grid_xsize[i] = grid_xsize;
10390       si->touch.grid_ysize[i] = grid_ysize;
10391     }
10392     else
10393     {
10394       si->touch.grid_xsize[i] = -1;
10395       si->touch.grid_ysize[i] = -1;
10396     }
10397
10398     for (x = 0; x < MAX_GRID_XSIZE; x++)
10399       for (y = 0; y < MAX_GRID_YSIZE; y++)
10400         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10401
10402     for (x = 0; x < min_xsize; x++)
10403       for (y = 0; y < min_ysize; y++)
10404         si->touch.grid_button[i][x][starty + y] =
10405           default_grid_button[y][0][x];
10406
10407     for (x = 0; x < min_xsize; x++)
10408       for (y = 0; y < min_ysize; y++)
10409         si->touch.grid_button[i][startx + x][starty + y] =
10410           default_grid_button[y][1][x];
10411   }
10412
10413   si->touch.grid_initialized            = video.initialized;
10414
10415   si->touch.overlay_buttons             = FALSE;
10416
10417   si->editor.el_boulderdash             = TRUE;
10418   si->editor.el_emerald_mine            = TRUE;
10419   si->editor.el_emerald_mine_club       = TRUE;
10420   si->editor.el_more                    = TRUE;
10421   si->editor.el_sokoban                 = TRUE;
10422   si->editor.el_supaplex                = TRUE;
10423   si->editor.el_diamond_caves           = TRUE;
10424   si->editor.el_dx_boulderdash          = TRUE;
10425
10426   si->editor.el_mirror_magic            = TRUE;
10427   si->editor.el_deflektor               = TRUE;
10428
10429   si->editor.el_chars                   = TRUE;
10430   si->editor.el_steel_chars             = TRUE;
10431
10432   si->editor.el_classic                 = TRUE;
10433   si->editor.el_custom                  = TRUE;
10434
10435   si->editor.el_user_defined            = FALSE;
10436   si->editor.el_dynamic                 = TRUE;
10437
10438   si->editor.el_headlines               = TRUE;
10439
10440   si->editor.show_element_token         = FALSE;
10441
10442   si->editor.show_read_only_warning     = TRUE;
10443
10444   si->editor.use_template_for_new_levels = TRUE;
10445
10446   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10447   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10448   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10449   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10450   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10451
10452   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10453   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10454   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10455   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10456   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10457
10458   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10459   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10460   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10461   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10462   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10463   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10464
10465   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10466   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10467   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10468
10469   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10470   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10471   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10472   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10473
10474   for (i = 0; i < MAX_PLAYERS; i++)
10475   {
10476     si->input[i].use_joystick = FALSE;
10477     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10478     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10479     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10480     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10481     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10482     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10483     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10484     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10485     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10486     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10487     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10488     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10489     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10490     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10491     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10492   }
10493
10494   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10495   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10496   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10497   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10498
10499   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10500   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10501   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10502   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10503   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
10504   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10505   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
10506
10507   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10508
10509   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10510   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10511   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10512
10513   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10514   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
10515   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
10516
10517   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10518   si->internal.choose_from_top_leveldir = FALSE;
10519   si->internal.show_scaling_in_title = TRUE;
10520   si->internal.create_user_levelset = TRUE;
10521
10522   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
10523   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10524
10525   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10526   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10527   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10528   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10529   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10530   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10531   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10532   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10533   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10534   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10535
10536   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10537   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10538   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10539   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10540   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10541   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10542   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10543   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10544   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10545   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10546
10547   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10548   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
10549
10550   si->debug.show_frames_per_second = FALSE;
10551
10552   si->debug.xsn_mode = AUTO;
10553   si->debug.xsn_percent = 0;
10554
10555   si->options.verbose = FALSE;
10556
10557 #if defined(PLATFORM_ANDROID)
10558   si->fullscreen = TRUE;
10559   si->touch.overlay_buttons = TRUE;
10560 #endif
10561
10562   setHideSetupEntry(&setup.debug.xsn_mode);
10563 }
10564
10565 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10566 {
10567   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10568 }
10569
10570 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10571 {
10572   si->player_uuid = NULL;       // (will be set later)
10573   si->player_version = 1;       // (will be set later)
10574
10575   si->use_api_server = TRUE;
10576   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10577   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10578   si->ask_for_uploading_tapes = TRUE;
10579   si->ask_for_remaining_tapes = FALSE;
10580   si->provide_uploading_tapes = TRUE;
10581   si->ask_for_using_api_server = TRUE;
10582   si->has_remaining_tapes = FALSE;
10583 }
10584
10585 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10586 {
10587   si->editor_cascade.el_bd              = TRUE;
10588   si->editor_cascade.el_em              = TRUE;
10589   si->editor_cascade.el_emc             = TRUE;
10590   si->editor_cascade.el_rnd             = TRUE;
10591   si->editor_cascade.el_sb              = TRUE;
10592   si->editor_cascade.el_sp              = TRUE;
10593   si->editor_cascade.el_dc              = TRUE;
10594   si->editor_cascade.el_dx              = TRUE;
10595
10596   si->editor_cascade.el_mm              = TRUE;
10597   si->editor_cascade.el_df              = TRUE;
10598
10599   si->editor_cascade.el_chars           = FALSE;
10600   si->editor_cascade.el_steel_chars     = FALSE;
10601   si->editor_cascade.el_ce              = FALSE;
10602   si->editor_cascade.el_ge              = FALSE;
10603   si->editor_cascade.el_es              = FALSE;
10604   si->editor_cascade.el_ref             = FALSE;
10605   si->editor_cascade.el_user            = FALSE;
10606   si->editor_cascade.el_dynamic         = FALSE;
10607 }
10608
10609 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
10610
10611 static char *getHideSetupToken(void *setup_value)
10612 {
10613   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10614
10615   if (setup_value != NULL)
10616     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10617
10618   return hide_setup_token;
10619 }
10620
10621 void setHideSetupEntry(void *setup_value)
10622 {
10623   char *hide_setup_token = getHideSetupToken(setup_value);
10624
10625   if (hide_setup_hash == NULL)
10626     hide_setup_hash = newSetupFileHash();
10627
10628   if (setup_value != NULL)
10629     setHashEntry(hide_setup_hash, hide_setup_token, "");
10630 }
10631
10632 void removeHideSetupEntry(void *setup_value)
10633 {
10634   char *hide_setup_token = getHideSetupToken(setup_value);
10635
10636   if (setup_value != NULL)
10637     removeHashEntry(hide_setup_hash, hide_setup_token);
10638 }
10639
10640 boolean hideSetupEntry(void *setup_value)
10641 {
10642   char *hide_setup_token = getHideSetupToken(setup_value);
10643
10644   return (setup_value != NULL &&
10645           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10646 }
10647
10648 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10649                                       struct TokenInfo *token_info,
10650                                       int token_nr, char *token_text)
10651 {
10652   char *token_hide_text = getStringCat2(token_text, ".hide");
10653   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10654
10655   // set the value of this setup option in the setup option structure
10656   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10657
10658   // check if this setup option should be hidden in the setup menu
10659   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10660     setHideSetupEntry(token_info[token_nr].value);
10661
10662   free(token_hide_text);
10663 }
10664
10665 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10666                                       struct TokenInfo *token_info,
10667                                       int token_nr)
10668 {
10669   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10670                             token_info[token_nr].text);
10671 }
10672
10673 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10674 {
10675   int i, pnr;
10676
10677   if (!setup_file_hash)
10678     return;
10679
10680   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10681     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10682
10683   setup.touch.grid_initialized = TRUE;
10684   for (i = 0; i < 2; i++)
10685   {
10686     int grid_xsize = setup.touch.grid_xsize[i];
10687     int grid_ysize = setup.touch.grid_ysize[i];
10688     int x, y;
10689
10690     // if virtual buttons are not loaded from setup file, repeat initializing
10691     // virtual buttons grid with default values later when video is initialized
10692     if (grid_xsize == -1 ||
10693         grid_ysize == -1)
10694     {
10695       setup.touch.grid_initialized = FALSE;
10696
10697       continue;
10698     }
10699
10700     for (y = 0; y < grid_ysize; y++)
10701     {
10702       char token_string[MAX_LINE_LEN];
10703
10704       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10705
10706       char *value_string = getHashEntry(setup_file_hash, token_string);
10707
10708       if (value_string == NULL)
10709         continue;
10710
10711       for (x = 0; x < grid_xsize; x++)
10712       {
10713         char c = value_string[x];
10714
10715         setup.touch.grid_button[i][x][y] =
10716           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10717       }
10718     }
10719   }
10720
10721   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10722     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10723
10724   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10725     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10726
10727   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10728   {
10729     char prefix[30];
10730
10731     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10732
10733     setup_input = setup.input[pnr];
10734     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10735     {
10736       char full_token[100];
10737
10738       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10739       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10740                                 full_token);
10741     }
10742     setup.input[pnr] = setup_input;
10743   }
10744
10745   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10746     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10747
10748   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10749     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10750
10751   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10752     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10753
10754   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10755     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10756
10757   setHideRelatedSetupEntries();
10758 }
10759
10760 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10761 {
10762   int i;
10763
10764   if (!setup_file_hash)
10765     return;
10766
10767   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10768     setSetupInfo(auto_setup_tokens, i,
10769                  getHashEntry(setup_file_hash,
10770                               auto_setup_tokens[i].text));
10771 }
10772
10773 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10774 {
10775   int i;
10776
10777   if (!setup_file_hash)
10778     return;
10779
10780   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10781     setSetupInfo(server_setup_tokens, i,
10782                  getHashEntry(setup_file_hash,
10783                               server_setup_tokens[i].text));
10784 }
10785
10786 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10787 {
10788   int i;
10789
10790   if (!setup_file_hash)
10791     return;
10792
10793   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10794     setSetupInfo(editor_cascade_setup_tokens, i,
10795                  getHashEntry(setup_file_hash,
10796                               editor_cascade_setup_tokens[i].text));
10797 }
10798
10799 void LoadUserNames(void)
10800 {
10801   int last_user_nr = user.nr;
10802   int i;
10803
10804   if (global.user_names != NULL)
10805   {
10806     for (i = 0; i < MAX_PLAYER_NAMES; i++)
10807       checked_free(global.user_names[i]);
10808
10809     checked_free(global.user_names);
10810   }
10811
10812   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10813
10814   for (i = 0; i < MAX_PLAYER_NAMES; i++)
10815   {
10816     user.nr = i;
10817
10818     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10819
10820     if (setup_file_hash)
10821     {
10822       char *player_name = getHashEntry(setup_file_hash, "player_name");
10823
10824       global.user_names[i] = getFixedUserName(player_name);
10825
10826       freeSetupFileHash(setup_file_hash);
10827     }
10828
10829     if (global.user_names[i] == NULL)
10830       global.user_names[i] = getStringCopy(getDefaultUserName(i));
10831   }
10832
10833   user.nr = last_user_nr;
10834 }
10835
10836 void LoadSetupFromFilename(char *filename)
10837 {
10838   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10839
10840   if (setup_file_hash)
10841   {
10842     decodeSetupFileHash_Default(setup_file_hash);
10843
10844     freeSetupFileHash(setup_file_hash);
10845   }
10846   else
10847   {
10848     Debug("setup", "using default setup values");
10849   }
10850 }
10851
10852 static void LoadSetup_SpecialPostProcessing(void)
10853 {
10854   char *player_name_new;
10855
10856   // needed to work around problems with fixed length strings
10857   player_name_new = getFixedUserName(setup.player_name);
10858   free(setup.player_name);
10859   setup.player_name = player_name_new;
10860
10861   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10862   if (setup.scroll_delay == FALSE)
10863   {
10864     setup.scroll_delay_value = MIN_SCROLL_DELAY;
10865     setup.scroll_delay = TRUE;                  // now always "on"
10866   }
10867
10868   // make sure that scroll delay value stays inside valid range
10869   setup.scroll_delay_value =
10870     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10871 }
10872
10873 void LoadSetup_Default(void)
10874 {
10875   char *filename;
10876
10877   // always start with reliable default values
10878   setSetupInfoToDefaults(&setup);
10879
10880   // try to load setup values from default setup file
10881   filename = getDefaultSetupFilename();
10882
10883   if (fileExists(filename))
10884     LoadSetupFromFilename(filename);
10885
10886   // try to load setup values from platform setup file
10887   filename = getPlatformSetupFilename();
10888
10889   if (fileExists(filename))
10890     LoadSetupFromFilename(filename);
10891
10892   // try to load setup values from user setup file
10893   filename = getSetupFilename();
10894
10895   LoadSetupFromFilename(filename);
10896
10897   LoadSetup_SpecialPostProcessing();
10898 }
10899
10900 void LoadSetup_AutoSetup(void)
10901 {
10902   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10903   SetupFileHash *setup_file_hash = NULL;
10904
10905   // always start with reliable default values
10906   setSetupInfoToDefaults_AutoSetup(&setup);
10907
10908   setup_file_hash = loadSetupFileHash(filename);
10909
10910   if (setup_file_hash)
10911   {
10912     decodeSetupFileHash_AutoSetup(setup_file_hash);
10913
10914     freeSetupFileHash(setup_file_hash);
10915   }
10916
10917   free(filename);
10918 }
10919
10920 void LoadSetup_ServerSetup(void)
10921 {
10922   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10923   SetupFileHash *setup_file_hash = NULL;
10924
10925   // always start with reliable default values
10926   setSetupInfoToDefaults_ServerSetup(&setup);
10927
10928   setup_file_hash = loadSetupFileHash(filename);
10929
10930   if (setup_file_hash)
10931   {
10932     decodeSetupFileHash_ServerSetup(setup_file_hash);
10933
10934     freeSetupFileHash(setup_file_hash);
10935   }
10936
10937   free(filename);
10938
10939   if (setup.player_uuid == NULL)
10940   {
10941     // player UUID does not yet exist in setup file
10942     setup.player_uuid = getStringCopy(getUUID());
10943     setup.player_version = 2;
10944
10945     SaveSetup_ServerSetup();
10946   }
10947 }
10948
10949 void LoadSetup_EditorCascade(void)
10950 {
10951   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10952   SetupFileHash *setup_file_hash = NULL;
10953
10954   // always start with reliable default values
10955   setSetupInfoToDefaults_EditorCascade(&setup);
10956
10957   setup_file_hash = loadSetupFileHash(filename);
10958
10959   if (setup_file_hash)
10960   {
10961     decodeSetupFileHash_EditorCascade(setup_file_hash);
10962
10963     freeSetupFileHash(setup_file_hash);
10964   }
10965
10966   free(filename);
10967 }
10968
10969 void LoadSetup(void)
10970 {
10971   LoadSetup_Default();
10972   LoadSetup_AutoSetup();
10973   LoadSetup_ServerSetup();
10974   LoadSetup_EditorCascade();
10975 }
10976
10977 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10978                                            char *mapping_line)
10979 {
10980   char mapping_guid[MAX_LINE_LEN];
10981   char *mapping_start, *mapping_end;
10982
10983   // get GUID from game controller mapping line: copy complete line
10984   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10985   mapping_guid[MAX_LINE_LEN - 1] = '\0';
10986
10987   // get GUID from game controller mapping line: cut after GUID part
10988   mapping_start = strchr(mapping_guid, ',');
10989   if (mapping_start != NULL)
10990     *mapping_start = '\0';
10991
10992   // cut newline from game controller mapping line
10993   mapping_end = strchr(mapping_line, '\n');
10994   if (mapping_end != NULL)
10995     *mapping_end = '\0';
10996
10997   // add mapping entry to game controller mappings hash
10998   setHashEntry(mappings_hash, mapping_guid, mapping_line);
10999 }
11000
11001 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11002                                                  char *filename)
11003 {
11004   FILE *file;
11005
11006   if (!(file = fopen(filename, MODE_READ)))
11007   {
11008     Warn("cannot read game controller mappings file '%s'", filename);
11009
11010     return;
11011   }
11012
11013   while (!feof(file))
11014   {
11015     char line[MAX_LINE_LEN];
11016
11017     if (!fgets(line, MAX_LINE_LEN, file))
11018       break;
11019
11020     addGameControllerMappingToHash(mappings_hash, line);
11021   }
11022
11023   fclose(file);
11024 }
11025
11026 void SaveSetup_Default(void)
11027 {
11028   char *filename = getSetupFilename();
11029   FILE *file;
11030   int i, pnr;
11031
11032   InitUserDataDirectory();
11033
11034   if (!(file = fopen(filename, MODE_WRITE)))
11035   {
11036     Warn("cannot write setup file '%s'", filename);
11037
11038     return;
11039   }
11040
11041   fprintFileHeader(file, SETUP_FILENAME);
11042
11043   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11044   {
11045     // just to make things nicer :)
11046     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11047         global_setup_tokens[i].value == &setup.sound                    ||
11048         global_setup_tokens[i].value == &setup.graphics_set             ||
11049         global_setup_tokens[i].value == &setup.volume_simple            ||
11050         global_setup_tokens[i].value == &setup.network_mode             ||
11051         global_setup_tokens[i].value == &setup.touch.control_type       ||
11052         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11053         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11054       fprintf(file, "\n");
11055
11056     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11057   }
11058
11059   for (i = 0; i < 2; i++)
11060   {
11061     int grid_xsize = setup.touch.grid_xsize[i];
11062     int grid_ysize = setup.touch.grid_ysize[i];
11063     int x, y;
11064
11065     fprintf(file, "\n");
11066
11067     for (y = 0; y < grid_ysize; y++)
11068     {
11069       char token_string[MAX_LINE_LEN];
11070       char value_string[MAX_LINE_LEN];
11071
11072       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11073
11074       for (x = 0; x < grid_xsize; x++)
11075       {
11076         char c = setup.touch.grid_button[i][x][y];
11077
11078         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11079       }
11080
11081       value_string[grid_xsize] = '\0';
11082
11083       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11084     }
11085   }
11086
11087   fprintf(file, "\n");
11088   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11089     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11090
11091   fprintf(file, "\n");
11092   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11093     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11094
11095   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11096   {
11097     char prefix[30];
11098
11099     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11100     fprintf(file, "\n");
11101
11102     setup_input = setup.input[pnr];
11103     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11104       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11105   }
11106
11107   fprintf(file, "\n");
11108   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11109     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11110
11111   // (internal setup values not saved to user setup file)
11112
11113   fprintf(file, "\n");
11114   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11115     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11116         setup.debug.xsn_mode != AUTO)
11117       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11118
11119   fprintf(file, "\n");
11120   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11121     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11122
11123   fclose(file);
11124
11125   SetFilePermissions(filename, PERMS_PRIVATE);
11126 }
11127
11128 void SaveSetup_AutoSetup(void)
11129 {
11130   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11131   FILE *file;
11132   int i;
11133
11134   InitUserDataDirectory();
11135
11136   if (!(file = fopen(filename, MODE_WRITE)))
11137   {
11138     Warn("cannot write auto setup file '%s'", filename);
11139
11140     free(filename);
11141
11142     return;
11143   }
11144
11145   fprintFileHeader(file, AUTOSETUP_FILENAME);
11146
11147   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11148     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11149
11150   fclose(file);
11151
11152   SetFilePermissions(filename, PERMS_PRIVATE);
11153
11154   free(filename);
11155 }
11156
11157 void SaveSetup_ServerSetup(void)
11158 {
11159   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11160   FILE *file;
11161   int i;
11162
11163   InitUserDataDirectory();
11164
11165   if (!(file = fopen(filename, MODE_WRITE)))
11166   {
11167     Warn("cannot write server setup file '%s'", filename);
11168
11169     free(filename);
11170
11171     return;
11172   }
11173
11174   fprintFileHeader(file, SERVERSETUP_FILENAME);
11175
11176   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11177   {
11178     // just to make things nicer :)
11179     if (server_setup_tokens[i].value == &setup.use_api_server)
11180       fprintf(file, "\n");
11181
11182     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11183   }
11184
11185   fclose(file);
11186
11187   SetFilePermissions(filename, PERMS_PRIVATE);
11188
11189   free(filename);
11190 }
11191
11192 void SaveSetup_EditorCascade(void)
11193 {
11194   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11195   FILE *file;
11196   int i;
11197
11198   InitUserDataDirectory();
11199
11200   if (!(file = fopen(filename, MODE_WRITE)))
11201   {
11202     Warn("cannot write editor cascade state file '%s'", filename);
11203
11204     free(filename);
11205
11206     return;
11207   }
11208
11209   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11210
11211   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11212     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11213
11214   fclose(file);
11215
11216   SetFilePermissions(filename, PERMS_PRIVATE);
11217
11218   free(filename);
11219 }
11220
11221 void SaveSetup(void)
11222 {
11223   SaveSetup_Default();
11224   SaveSetup_AutoSetup();
11225   SaveSetup_ServerSetup();
11226   SaveSetup_EditorCascade();
11227 }
11228
11229 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11230                                                   char *filename)
11231 {
11232   FILE *file;
11233
11234   if (!(file = fopen(filename, MODE_WRITE)))
11235   {
11236     Warn("cannot write game controller mappings file '%s'", filename);
11237
11238     return;
11239   }
11240
11241   BEGIN_HASH_ITERATION(mappings_hash, itr)
11242   {
11243     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11244   }
11245   END_HASH_ITERATION(mappings_hash, itr)
11246
11247   fclose(file);
11248 }
11249
11250 void SaveSetup_AddGameControllerMapping(char *mapping)
11251 {
11252   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11253   SetupFileHash *mappings_hash = newSetupFileHash();
11254
11255   InitUserDataDirectory();
11256
11257   // load existing personal game controller mappings
11258   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11259
11260   // add new mapping to personal game controller mappings
11261   addGameControllerMappingToHash(mappings_hash, mapping);
11262
11263   // save updated personal game controller mappings
11264   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11265
11266   freeSetupFileHash(mappings_hash);
11267   free(filename);
11268 }
11269
11270 void LoadCustomElementDescriptions(void)
11271 {
11272   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11273   SetupFileHash *setup_file_hash;
11274   int i;
11275
11276   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11277   {
11278     if (element_info[i].custom_description != NULL)
11279     {
11280       free(element_info[i].custom_description);
11281       element_info[i].custom_description = NULL;
11282     }
11283   }
11284
11285   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11286     return;
11287
11288   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11289   {
11290     char *token = getStringCat2(element_info[i].token_name, ".name");
11291     char *value = getHashEntry(setup_file_hash, token);
11292
11293     if (value != NULL)
11294       element_info[i].custom_description = getStringCopy(value);
11295
11296     free(token);
11297   }
11298
11299   freeSetupFileHash(setup_file_hash);
11300 }
11301
11302 static int getElementFromToken(char *token)
11303 {
11304   char *value = getHashEntry(element_token_hash, token);
11305
11306   if (value != NULL)
11307     return atoi(value);
11308
11309   Warn("unknown element token '%s'", token);
11310
11311   return EL_UNDEFINED;
11312 }
11313
11314 void FreeGlobalAnimEventInfo(void)
11315 {
11316   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11317
11318   if (gaei->event_list == NULL)
11319     return;
11320
11321   int i;
11322
11323   for (i = 0; i < gaei->num_event_lists; i++)
11324   {
11325     checked_free(gaei->event_list[i]->event_value);
11326     checked_free(gaei->event_list[i]);
11327   }
11328
11329   checked_free(gaei->event_list);
11330
11331   gaei->event_list = NULL;
11332   gaei->num_event_lists = 0;
11333 }
11334
11335 static int AddGlobalAnimEventList(void)
11336 {
11337   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11338   int list_pos = gaei->num_event_lists++;
11339
11340   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11341                                      sizeof(struct GlobalAnimEventListInfo *));
11342
11343   gaei->event_list[list_pos] =
11344     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11345
11346   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11347
11348   gaeli->event_value = NULL;
11349   gaeli->num_event_values = 0;
11350
11351   return list_pos;
11352 }
11353
11354 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11355 {
11356   // do not add empty global animation events
11357   if (event_value == ANIM_EVENT_NONE)
11358     return list_pos;
11359
11360   // if list position is undefined, create new list
11361   if (list_pos == ANIM_EVENT_UNDEFINED)
11362     list_pos = AddGlobalAnimEventList();
11363
11364   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11365   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11366   int value_pos = gaeli->num_event_values++;
11367
11368   gaeli->event_value = checked_realloc(gaeli->event_value,
11369                                        gaeli->num_event_values * sizeof(int *));
11370
11371   gaeli->event_value[value_pos] = event_value;
11372
11373   return list_pos;
11374 }
11375
11376 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11377 {
11378   if (list_pos == ANIM_EVENT_UNDEFINED)
11379     return ANIM_EVENT_NONE;
11380
11381   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11382   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11383
11384   return gaeli->event_value[value_pos];
11385 }
11386
11387 int GetGlobalAnimEventValueCount(int list_pos)
11388 {
11389   if (list_pos == ANIM_EVENT_UNDEFINED)
11390     return 0;
11391
11392   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11393   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11394
11395   return gaeli->num_event_values;
11396 }
11397
11398 // This function checks if a string <s> of the format "string1, string2, ..."
11399 // exactly contains a string <s_contained>.
11400
11401 static boolean string_has_parameter(char *s, char *s_contained)
11402 {
11403   char *substring;
11404
11405   if (s == NULL || s_contained == NULL)
11406     return FALSE;
11407
11408   if (strlen(s_contained) > strlen(s))
11409     return FALSE;
11410
11411   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11412   {
11413     char next_char = s[strlen(s_contained)];
11414
11415     // check if next character is delimiter or whitespace
11416     return (next_char == ',' || next_char == '\0' ||
11417             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11418   }
11419
11420   // check if string contains another parameter string after a comma
11421   substring = strchr(s, ',');
11422   if (substring == NULL)        // string does not contain a comma
11423     return FALSE;
11424
11425   // advance string pointer to next character after the comma
11426   substring++;
11427
11428   // skip potential whitespaces after the comma
11429   while (*substring == ' ' || *substring == '\t')
11430     substring++;
11431
11432   return string_has_parameter(substring, s_contained);
11433 }
11434
11435 static int get_anim_parameter_value(char *s)
11436 {
11437   int event_value[] =
11438   {
11439     ANIM_EVENT_CLICK,
11440     ANIM_EVENT_INIT,
11441     ANIM_EVENT_START,
11442     ANIM_EVENT_END,
11443     ANIM_EVENT_POST
11444   };
11445   char *pattern_1[] =
11446   {
11447     "click:anim_",
11448     "init:anim_",
11449     "start:anim_",
11450     "end:anim_",
11451     "post:anim_"
11452   };
11453   char *pattern_2 = ".part_";
11454   char *matching_char = NULL;
11455   char *s_ptr = s;
11456   int pattern_1_len = 0;
11457   int result = ANIM_EVENT_NONE;
11458   int i;
11459
11460   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11461   {
11462     matching_char = strstr(s_ptr, pattern_1[i]);
11463     pattern_1_len = strlen(pattern_1[i]);
11464     result = event_value[i];
11465
11466     if (matching_char != NULL)
11467       break;
11468   }
11469
11470   if (matching_char == NULL)
11471     return ANIM_EVENT_NONE;
11472
11473   s_ptr = matching_char + pattern_1_len;
11474
11475   // check for main animation number ("anim_X" or "anim_XX")
11476   if (*s_ptr >= '0' && *s_ptr <= '9')
11477   {
11478     int gic_anim_nr = (*s_ptr++ - '0');
11479
11480     if (*s_ptr >= '0' && *s_ptr <= '9')
11481       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11482
11483     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11484       return ANIM_EVENT_NONE;
11485
11486     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11487   }
11488   else
11489   {
11490     // invalid main animation number specified
11491
11492     return ANIM_EVENT_NONE;
11493   }
11494
11495   // check for animation part number ("part_X" or "part_XX") (optional)
11496   if (strPrefix(s_ptr, pattern_2))
11497   {
11498     s_ptr += strlen(pattern_2);
11499
11500     if (*s_ptr >= '0' && *s_ptr <= '9')
11501     {
11502       int gic_part_nr = (*s_ptr++ - '0');
11503
11504       if (*s_ptr >= '0' && *s_ptr <= '9')
11505         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11506
11507       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11508         return ANIM_EVENT_NONE;
11509
11510       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11511     }
11512     else
11513     {
11514       // invalid animation part number specified
11515
11516       return ANIM_EVENT_NONE;
11517     }
11518   }
11519
11520   // discard result if next character is neither delimiter nor whitespace
11521   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11522         *s_ptr == ' ' || *s_ptr == '\t'))
11523     return ANIM_EVENT_NONE;
11524
11525   return result;
11526 }
11527
11528 static int get_anim_parameter_values(char *s)
11529 {
11530   int list_pos = ANIM_EVENT_UNDEFINED;
11531   int event_value = ANIM_EVENT_DEFAULT;
11532
11533   if (string_has_parameter(s, "any"))
11534     event_value |= ANIM_EVENT_ANY;
11535
11536   if (string_has_parameter(s, "click:self") ||
11537       string_has_parameter(s, "click") ||
11538       string_has_parameter(s, "self"))
11539     event_value |= ANIM_EVENT_SELF;
11540
11541   if (string_has_parameter(s, "unclick:any"))
11542     event_value |= ANIM_EVENT_UNCLICK_ANY;
11543
11544   // if animation event found, add it to global animation event list
11545   if (event_value != ANIM_EVENT_NONE)
11546     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11547
11548   while (s != NULL)
11549   {
11550     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11551     event_value = get_anim_parameter_value(s);
11552
11553     // if animation event found, add it to global animation event list
11554     if (event_value != ANIM_EVENT_NONE)
11555       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11556
11557     // continue with next part of the string, starting with next comma
11558     s = strchr(s + 1, ',');
11559   }
11560
11561   return list_pos;
11562 }
11563
11564 static int get_anim_action_parameter_value(char *token)
11565 {
11566   // check most common default case first to massively speed things up
11567   if (strEqual(token, ARG_UNDEFINED))
11568     return ANIM_EVENT_ACTION_NONE;
11569
11570   int result = getImageIDFromToken(token);
11571
11572   if (result == -1)
11573   {
11574     char *gfx_token = getStringCat2("gfx.", token);
11575
11576     result = getImageIDFromToken(gfx_token);
11577
11578     checked_free(gfx_token);
11579   }
11580
11581   if (result == -1)
11582   {
11583     Key key = getKeyFromX11KeyName(token);
11584
11585     if (key != KSYM_UNDEFINED)
11586       result = -(int)key;
11587   }
11588
11589   if (result == -1)
11590   {
11591     if (isURL(token))
11592     {
11593       result = get_hash_from_key(token);        // unsigned int => int
11594       result = ABS(result);                     // may be negative now
11595       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11596
11597       setHashEntry(anim_url_hash, int2str(result, 0), token);
11598     }
11599   }
11600
11601   if (result == -1)
11602     result = ANIM_EVENT_ACTION_NONE;
11603
11604   return result;
11605 }
11606
11607 int get_parameter_value(char *value_raw, char *suffix, int type)
11608 {
11609   char *value = getStringToLower(value_raw);
11610   int result = 0;       // probably a save default value
11611
11612   if (strEqual(suffix, ".direction"))
11613   {
11614     result = (strEqual(value, "left")  ? MV_LEFT :
11615               strEqual(value, "right") ? MV_RIGHT :
11616               strEqual(value, "up")    ? MV_UP :
11617               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
11618   }
11619   else if (strEqual(suffix, ".position"))
11620   {
11621     result = (strEqual(value, "left")   ? POS_LEFT :
11622               strEqual(value, "right")  ? POS_RIGHT :
11623               strEqual(value, "top")    ? POS_TOP :
11624               strEqual(value, "upper")  ? POS_UPPER :
11625               strEqual(value, "middle") ? POS_MIDDLE :
11626               strEqual(value, "lower")  ? POS_LOWER :
11627               strEqual(value, "bottom") ? POS_BOTTOM :
11628               strEqual(value, "any")    ? POS_ANY :
11629               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
11630   }
11631   else if (strEqual(suffix, ".align"))
11632   {
11633     result = (strEqual(value, "left")   ? ALIGN_LEFT :
11634               strEqual(value, "right")  ? ALIGN_RIGHT :
11635               strEqual(value, "center") ? ALIGN_CENTER :
11636               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11637   }
11638   else if (strEqual(suffix, ".valign"))
11639   {
11640     result = (strEqual(value, "top")    ? VALIGN_TOP :
11641               strEqual(value, "bottom") ? VALIGN_BOTTOM :
11642               strEqual(value, "middle") ? VALIGN_MIDDLE :
11643               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11644   }
11645   else if (strEqual(suffix, ".anim_mode"))
11646   {
11647     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
11648               string_has_parameter(value, "loop")       ? ANIM_LOOP :
11649               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
11650               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
11651               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
11652               string_has_parameter(value, "random")     ? ANIM_RANDOM :
11653               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11654               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
11655               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
11656               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
11657               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11658               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
11659               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
11660               string_has_parameter(value, "all")        ? ANIM_ALL :
11661               string_has_parameter(value, "tiled")      ? ANIM_TILED :
11662               ANIM_DEFAULT);
11663
11664     if (string_has_parameter(value, "once"))
11665       result |= ANIM_ONCE;
11666
11667     if (string_has_parameter(value, "reverse"))
11668       result |= ANIM_REVERSE;
11669
11670     if (string_has_parameter(value, "opaque_player"))
11671       result |= ANIM_OPAQUE_PLAYER;
11672
11673     if (string_has_parameter(value, "static_panel"))
11674       result |= ANIM_STATIC_PANEL;
11675   }
11676   else if (strEqual(suffix, ".init_event") ||
11677            strEqual(suffix, ".anim_event"))
11678   {
11679     result = get_anim_parameter_values(value);
11680   }
11681   else if (strEqual(suffix, ".init_delay_action") ||
11682            strEqual(suffix, ".anim_delay_action") ||
11683            strEqual(suffix, ".post_delay_action") ||
11684            strEqual(suffix, ".init_event_action") ||
11685            strEqual(suffix, ".anim_event_action"))
11686   {
11687     result = get_anim_action_parameter_value(value_raw);
11688   }
11689   else if (strEqual(suffix, ".class"))
11690   {
11691     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11692               get_hash_from_key(value));
11693   }
11694   else if (strEqual(suffix, ".style"))
11695   {
11696     result = STYLE_DEFAULT;
11697
11698     if (string_has_parameter(value, "accurate_borders"))
11699       result |= STYLE_ACCURATE_BORDERS;
11700
11701     if (string_has_parameter(value, "inner_corners"))
11702       result |= STYLE_INNER_CORNERS;
11703
11704     if (string_has_parameter(value, "reverse"))
11705       result |= STYLE_REVERSE;
11706
11707     if (string_has_parameter(value, "leftmost_position"))
11708       result |= STYLE_LEFTMOST_POSITION;
11709
11710     if (string_has_parameter(value, "block_clicks"))
11711       result |= STYLE_BLOCK;
11712
11713     if (string_has_parameter(value, "passthrough_clicks"))
11714       result |= STYLE_PASSTHROUGH;
11715
11716     if (string_has_parameter(value, "multiple_actions"))
11717       result |= STYLE_MULTIPLE_ACTIONS;
11718   }
11719   else if (strEqual(suffix, ".fade_mode"))
11720   {
11721     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
11722               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
11723               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
11724               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
11725               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
11726               FADE_MODE_DEFAULT);
11727   }
11728   else if (strEqual(suffix, ".auto_delay_unit"))
11729   {
11730     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
11731               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11732               AUTO_DELAY_UNIT_DEFAULT);
11733   }
11734   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
11735   {
11736     result = gfx.get_font_from_token_function(value);
11737   }
11738   else          // generic parameter of type integer or boolean
11739   {
11740     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11741               type == TYPE_INTEGER ? get_integer_from_string(value) :
11742               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11743               ARG_UNDEFINED_VALUE);
11744   }
11745
11746   free(value);
11747
11748   return result;
11749 }
11750
11751 static int get_token_parameter_value(char *token, char *value_raw)
11752 {
11753   char *suffix;
11754
11755   if (token == NULL || value_raw == NULL)
11756     return ARG_UNDEFINED_VALUE;
11757
11758   suffix = strrchr(token, '.');
11759   if (suffix == NULL)
11760     suffix = token;
11761
11762   if (strEqual(suffix, ".element"))
11763     return getElementFromToken(value_raw);
11764
11765   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11766   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11767 }
11768
11769 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11770                                      boolean ignore_defaults)
11771 {
11772   int i;
11773
11774   for (i = 0; image_config_vars[i].token != NULL; i++)
11775   {
11776     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11777
11778     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11779     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11780       continue;
11781
11782     if (value != NULL)
11783       *image_config_vars[i].value =
11784         get_token_parameter_value(image_config_vars[i].token, value);
11785   }
11786 }
11787
11788 void InitMenuDesignSettings_Static(void)
11789 {
11790   // always start with reliable default values from static default config
11791   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11792 }
11793
11794 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11795 {
11796   int i;
11797
11798   // the following initializes hierarchical values from static configuration
11799
11800   // special case: initialize "ARG_DEFAULT" values in static default config
11801   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11802   titlescreen_initial_first_default.fade_mode  =
11803     title_initial_first_default.fade_mode;
11804   titlescreen_initial_first_default.fade_delay =
11805     title_initial_first_default.fade_delay;
11806   titlescreen_initial_first_default.post_delay =
11807     title_initial_first_default.post_delay;
11808   titlescreen_initial_first_default.auto_delay =
11809     title_initial_first_default.auto_delay;
11810   titlescreen_initial_first_default.auto_delay_unit =
11811     title_initial_first_default.auto_delay_unit;
11812   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
11813   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11814   titlescreen_first_default.post_delay = title_first_default.post_delay;
11815   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11816   titlescreen_first_default.auto_delay_unit =
11817     title_first_default.auto_delay_unit;
11818   titlemessage_initial_first_default.fade_mode  =
11819     title_initial_first_default.fade_mode;
11820   titlemessage_initial_first_default.fade_delay =
11821     title_initial_first_default.fade_delay;
11822   titlemessage_initial_first_default.post_delay =
11823     title_initial_first_default.post_delay;
11824   titlemessage_initial_first_default.auto_delay =
11825     title_initial_first_default.auto_delay;
11826   titlemessage_initial_first_default.auto_delay_unit =
11827     title_initial_first_default.auto_delay_unit;
11828   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
11829   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11830   titlemessage_first_default.post_delay = title_first_default.post_delay;
11831   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11832   titlemessage_first_default.auto_delay_unit =
11833     title_first_default.auto_delay_unit;
11834
11835   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
11836   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11837   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11838   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11839   titlescreen_initial_default.auto_delay_unit =
11840     title_initial_default.auto_delay_unit;
11841   titlescreen_default.fade_mode  = title_default.fade_mode;
11842   titlescreen_default.fade_delay = title_default.fade_delay;
11843   titlescreen_default.post_delay = title_default.post_delay;
11844   titlescreen_default.auto_delay = title_default.auto_delay;
11845   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11846   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
11847   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11848   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11849   titlemessage_initial_default.auto_delay_unit =
11850     title_initial_default.auto_delay_unit;
11851   titlemessage_default.fade_mode  = title_default.fade_mode;
11852   titlemessage_default.fade_delay = title_default.fade_delay;
11853   titlemessage_default.post_delay = title_default.post_delay;
11854   titlemessage_default.auto_delay = title_default.auto_delay;
11855   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11856
11857   // special case: initialize "ARG_DEFAULT" values in static default config
11858   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11859   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11860   {
11861     titlescreen_initial_first[i] = titlescreen_initial_first_default;
11862     titlescreen_first[i] = titlescreen_first_default;
11863     titlemessage_initial_first[i] = titlemessage_initial_first_default;
11864     titlemessage_first[i] = titlemessage_first_default;
11865
11866     titlescreen_initial[i] = titlescreen_initial_default;
11867     titlescreen[i] = titlescreen_default;
11868     titlemessage_initial[i] = titlemessage_initial_default;
11869     titlemessage[i] = titlemessage_default;
11870   }
11871
11872   // special case: initialize "ARG_DEFAULT" values in static default config
11873   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11874   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11875   {
11876     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
11877       continue;
11878
11879     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11880     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11881     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11882   }
11883
11884   // special case: initialize "ARG_DEFAULT" values in static default config
11885   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11886   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11887   {
11888     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11889     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11890     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11891
11892     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
11893       continue;
11894
11895     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11896   }
11897 }
11898
11899 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11900 {
11901   static struct
11902   {
11903     struct XY *dst, *src;
11904   }
11905   game_buttons_xy[] =
11906   {
11907     { &game.button.save,        &game.button.stop       },
11908     { &game.button.pause2,      &game.button.pause      },
11909     { &game.button.load,        &game.button.play       },
11910     { &game.button.undo,        &game.button.stop       },
11911     { &game.button.redo,        &game.button.play       },
11912
11913     { NULL,                     NULL                    }
11914   };
11915   int i, j;
11916
11917   // special case: initialize later added SETUP list size from LEVELS value
11918   if (menu.list_size[GAME_MODE_SETUP] == -1)
11919     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11920
11921   // set default position for snapshot buttons to stop/pause/play buttons
11922   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11923     if ((*game_buttons_xy[i].dst).x == -1 &&
11924         (*game_buttons_xy[i].dst).y == -1)
11925       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11926
11927   // --------------------------------------------------------------------------
11928   // dynamic viewports (including playfield margins, borders and alignments)
11929   // --------------------------------------------------------------------------
11930
11931   // dynamic viewports currently only supported for landscape mode
11932   int display_width  = MAX(video.display_width, video.display_height);
11933   int display_height = MIN(video.display_width, video.display_height);
11934
11935   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11936   {
11937     struct RectWithBorder *vp_window    = &viewport.window[i];
11938     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11939     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
11940     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
11941     boolean dynamic_window_width     = (vp_window->min_width     != -1);
11942     boolean dynamic_window_height    = (vp_window->min_height    != -1);
11943     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
11944     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11945
11946     // adjust window size if min/max width/height is specified
11947
11948     if (vp_window->min_width != -1)
11949     {
11950       int window_width = display_width;
11951
11952       // when using static window height, use aspect ratio of display
11953       if (vp_window->min_height == -1)
11954         window_width = vp_window->height * display_width / display_height;
11955
11956       vp_window->width = MAX(vp_window->min_width, window_width);
11957     }
11958
11959     if (vp_window->min_height != -1)
11960     {
11961       int window_height = display_height;
11962
11963       // when using static window width, use aspect ratio of display
11964       if (vp_window->min_width == -1)
11965         window_height = vp_window->width * display_height / display_width;
11966
11967       vp_window->height = MAX(vp_window->min_height, window_height);
11968     }
11969
11970     if (vp_window->max_width != -1)
11971       vp_window->width = MIN(vp_window->width, vp_window->max_width);
11972
11973     if (vp_window->max_height != -1)
11974       vp_window->height = MIN(vp_window->height, vp_window->max_height);
11975
11976     int playfield_width  = vp_window->width;
11977     int playfield_height = vp_window->height;
11978
11979     // adjust playfield size and position according to specified margins
11980
11981     playfield_width  -= vp_playfield->margin_left;
11982     playfield_width  -= vp_playfield->margin_right;
11983
11984     playfield_height -= vp_playfield->margin_top;
11985     playfield_height -= vp_playfield->margin_bottom;
11986
11987     // adjust playfield size if min/max width/height is specified
11988
11989     if (vp_playfield->min_width != -1)
11990       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11991
11992     if (vp_playfield->min_height != -1)
11993       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11994
11995     if (vp_playfield->max_width != -1)
11996       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11997
11998     if (vp_playfield->max_height != -1)
11999       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12000
12001     // adjust playfield position according to specified alignment
12002
12003     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12004       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12005     else if (vp_playfield->align == ALIGN_CENTER)
12006       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12007     else if (vp_playfield->align == ALIGN_RIGHT)
12008       vp_playfield->x += playfield_width - vp_playfield->width;
12009
12010     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12011       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12012     else if (vp_playfield->valign == VALIGN_MIDDLE)
12013       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12014     else if (vp_playfield->valign == VALIGN_BOTTOM)
12015       vp_playfield->y += playfield_height - vp_playfield->height;
12016
12017     vp_playfield->x += vp_playfield->margin_left;
12018     vp_playfield->y += vp_playfield->margin_top;
12019
12020     // adjust individual playfield borders if only default border is specified
12021
12022     if (vp_playfield->border_left == -1)
12023       vp_playfield->border_left = vp_playfield->border_size;
12024     if (vp_playfield->border_right == -1)
12025       vp_playfield->border_right = vp_playfield->border_size;
12026     if (vp_playfield->border_top == -1)
12027       vp_playfield->border_top = vp_playfield->border_size;
12028     if (vp_playfield->border_bottom == -1)
12029       vp_playfield->border_bottom = vp_playfield->border_size;
12030
12031     // set dynamic playfield borders if borders are specified as undefined
12032     // (but only if window size was dynamic and playfield size was static)
12033
12034     if (dynamic_window_width && !dynamic_playfield_width)
12035     {
12036       if (vp_playfield->border_left == -1)
12037       {
12038         vp_playfield->border_left = (vp_playfield->x -
12039                                      vp_playfield->margin_left);
12040         vp_playfield->x     -= vp_playfield->border_left;
12041         vp_playfield->width += vp_playfield->border_left;
12042       }
12043
12044       if (vp_playfield->border_right == -1)
12045       {
12046         vp_playfield->border_right = (vp_window->width -
12047                                       vp_playfield->x -
12048                                       vp_playfield->width -
12049                                       vp_playfield->margin_right);
12050         vp_playfield->width += vp_playfield->border_right;
12051       }
12052     }
12053
12054     if (dynamic_window_height && !dynamic_playfield_height)
12055     {
12056       if (vp_playfield->border_top == -1)
12057       {
12058         vp_playfield->border_top = (vp_playfield->y -
12059                                     vp_playfield->margin_top);
12060         vp_playfield->y      -= vp_playfield->border_top;
12061         vp_playfield->height += vp_playfield->border_top;
12062       }
12063
12064       if (vp_playfield->border_bottom == -1)
12065       {
12066         vp_playfield->border_bottom = (vp_window->height -
12067                                        vp_playfield->y -
12068                                        vp_playfield->height -
12069                                        vp_playfield->margin_bottom);
12070         vp_playfield->height += vp_playfield->border_bottom;
12071       }
12072     }
12073
12074     // adjust playfield size to be a multiple of a defined alignment tile size
12075
12076     int align_size = vp_playfield->align_size;
12077     int playfield_xtiles = vp_playfield->width  / align_size;
12078     int playfield_ytiles = vp_playfield->height / align_size;
12079     int playfield_width_corrected  = playfield_xtiles * align_size;
12080     int playfield_height_corrected = playfield_ytiles * align_size;
12081     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12082                                  i == GFX_SPECIAL_ARG_EDITOR);
12083
12084     if (is_playfield_mode &&
12085         dynamic_playfield_width &&
12086         vp_playfield->width != playfield_width_corrected)
12087     {
12088       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12089
12090       vp_playfield->width = playfield_width_corrected;
12091
12092       if (vp_playfield->align == ALIGN_LEFT)
12093       {
12094         vp_playfield->border_left += playfield_xdiff;
12095       }
12096       else if (vp_playfield->align == ALIGN_RIGHT)
12097       {
12098         vp_playfield->border_right += playfield_xdiff;
12099       }
12100       else if (vp_playfield->align == ALIGN_CENTER)
12101       {
12102         int border_left_diff  = playfield_xdiff / 2;
12103         int border_right_diff = playfield_xdiff - border_left_diff;
12104
12105         vp_playfield->border_left  += border_left_diff;
12106         vp_playfield->border_right += border_right_diff;
12107       }
12108     }
12109
12110     if (is_playfield_mode &&
12111         dynamic_playfield_height &&
12112         vp_playfield->height != playfield_height_corrected)
12113     {
12114       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12115
12116       vp_playfield->height = playfield_height_corrected;
12117
12118       if (vp_playfield->valign == VALIGN_TOP)
12119       {
12120         vp_playfield->border_top += playfield_ydiff;
12121       }
12122       else if (vp_playfield->align == VALIGN_BOTTOM)
12123       {
12124         vp_playfield->border_right += playfield_ydiff;
12125       }
12126       else if (vp_playfield->align == VALIGN_MIDDLE)
12127       {
12128         int border_top_diff    = playfield_ydiff / 2;
12129         int border_bottom_diff = playfield_ydiff - border_top_diff;
12130
12131         vp_playfield->border_top    += border_top_diff;
12132         vp_playfield->border_bottom += border_bottom_diff;
12133       }
12134     }
12135
12136     // adjust door positions according to specified alignment
12137
12138     for (j = 0; j < 2; j++)
12139     {
12140       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12141
12142       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12143         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12144       else if (vp_door->align == ALIGN_CENTER)
12145         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12146       else if (vp_door->align == ALIGN_RIGHT)
12147         vp_door->x += vp_window->width - vp_door->width;
12148
12149       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12150         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12151       else if (vp_door->valign == VALIGN_MIDDLE)
12152         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12153       else if (vp_door->valign == VALIGN_BOTTOM)
12154         vp_door->y += vp_window->height - vp_door->height;
12155     }
12156   }
12157 }
12158
12159 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12160 {
12161   static struct
12162   {
12163     struct XYTileSize *dst, *src;
12164     int graphic;
12165   }
12166   editor_buttons_xy[] =
12167   {
12168     {
12169       &editor.button.element_left,      &editor.palette.element_left,
12170       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12171     },
12172     {
12173       &editor.button.element_middle,    &editor.palette.element_middle,
12174       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12175     },
12176     {
12177       &editor.button.element_right,     &editor.palette.element_right,
12178       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12179     },
12180
12181     { NULL,                     NULL                    }
12182   };
12183   int i;
12184
12185   // set default position for element buttons to element graphics
12186   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12187   {
12188     if ((*editor_buttons_xy[i].dst).x == -1 &&
12189         (*editor_buttons_xy[i].dst).y == -1)
12190     {
12191       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12192
12193       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12194
12195       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12196     }
12197   }
12198
12199   // adjust editor palette rows and columns if specified to be dynamic
12200
12201   if (editor.palette.cols == -1)
12202   {
12203     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12204     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12205     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12206
12207     editor.palette.cols = (vp_width - sc_width) / bt_width;
12208
12209     if (editor.palette.x == -1)
12210     {
12211       int palette_width = editor.palette.cols * bt_width + sc_width;
12212
12213       editor.palette.x = (vp_width - palette_width) / 2;
12214     }
12215   }
12216
12217   if (editor.palette.rows == -1)
12218   {
12219     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12220     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12221     int tx_height = getFontHeight(FONT_TEXT_2);
12222
12223     editor.palette.rows = (vp_height - tx_height) / bt_height;
12224
12225     if (editor.palette.y == -1)
12226     {
12227       int palette_height = editor.palette.rows * bt_height + tx_height;
12228
12229       editor.palette.y = (vp_height - palette_height) / 2;
12230     }
12231   }
12232 }
12233
12234 static void LoadMenuDesignSettingsFromFilename(char *filename)
12235 {
12236   static struct TitleFadingInfo tfi;
12237   static struct TitleMessageInfo tmi;
12238   static struct TokenInfo title_tokens[] =
12239   {
12240     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12241     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12242     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12243     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12244     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12245
12246     { -1,               NULL,                   NULL                    }
12247   };
12248   static struct TokenInfo titlemessage_tokens[] =
12249   {
12250     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12251     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12252     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12253     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12254     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12255     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12256     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12257     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12258     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12259     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12260     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12261     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12262     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12263     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12264     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12265     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12266     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12267     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12268
12269     { -1,               NULL,                   NULL                    }
12270   };
12271   static struct
12272   {
12273     struct TitleFadingInfo *info;
12274     char *text;
12275   }
12276   title_info[] =
12277   {
12278     // initialize first titles from "enter screen" definitions, if defined
12279     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12280     { &title_first_default,             "menu.enter_screen.TITLE"       },
12281
12282     // initialize title screens from "next screen" definitions, if defined
12283     { &title_initial_default,           "menu.next_screen.TITLE"        },
12284     { &title_default,                   "menu.next_screen.TITLE"        },
12285
12286     { NULL,                             NULL                            }
12287   };
12288   static struct
12289   {
12290     struct TitleMessageInfo *array;
12291     char *text;
12292   }
12293   titlemessage_arrays[] =
12294   {
12295     // initialize first titles from "enter screen" definitions, if defined
12296     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12297     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12298     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12299     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12300
12301     // initialize titles from "next screen" definitions, if defined
12302     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12303     { titlescreen,                      "menu.next_screen.TITLE"        },
12304     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12305     { titlemessage,                     "menu.next_screen.TITLE"        },
12306
12307     // overwrite titles with title definitions, if defined
12308     { titlescreen_initial_first,        "[title_initial]"               },
12309     { titlescreen_first,                "[title]"                       },
12310     { titlemessage_initial_first,       "[title_initial]"               },
12311     { titlemessage_first,               "[title]"                       },
12312
12313     { titlescreen_initial,              "[title_initial]"               },
12314     { titlescreen,                      "[title]"                       },
12315     { titlemessage_initial,             "[title_initial]"               },
12316     { titlemessage,                     "[title]"                       },
12317
12318     // overwrite titles with title screen/message definitions, if defined
12319     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12320     { titlescreen_first,                "[titlescreen]"                 },
12321     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12322     { titlemessage_first,               "[titlemessage]"                },
12323
12324     { titlescreen_initial,              "[titlescreen_initial]"         },
12325     { titlescreen,                      "[titlescreen]"                 },
12326     { titlemessage_initial,             "[titlemessage_initial]"        },
12327     { titlemessage,                     "[titlemessage]"                },
12328
12329     { NULL,                             NULL                            }
12330   };
12331   SetupFileHash *setup_file_hash;
12332   int i, j, k;
12333
12334   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12335     return;
12336
12337   // the following initializes hierarchical values from dynamic configuration
12338
12339   // special case: initialize with default values that may be overwritten
12340   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12341   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12342   {
12343     struct TokenIntPtrInfo menu_config[] =
12344     {
12345       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12346       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12347       { "menu.list_size",       &menu.list_size[i]      }
12348     };
12349
12350     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12351     {
12352       char *token = menu_config[j].token;
12353       char *value = getHashEntry(setup_file_hash, token);
12354
12355       if (value != NULL)
12356         *menu_config[j].value = get_integer_from_string(value);
12357     }
12358   }
12359
12360   // special case: initialize with default values that may be overwritten
12361   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12362   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12363   {
12364     struct TokenIntPtrInfo menu_config[] =
12365     {
12366       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12367       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12368       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12369     };
12370
12371     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12372     {
12373       char *token = menu_config[j].token;
12374       char *value = getHashEntry(setup_file_hash, token);
12375
12376       if (value != NULL)
12377         *menu_config[j].value = get_integer_from_string(value);
12378     }
12379   }
12380
12381   // special case: initialize with default values that may be overwritten
12382   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12383   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12384   {
12385     struct TokenIntPtrInfo menu_config[] =
12386     {
12387       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12388       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12389     };
12390
12391     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12392     {
12393       char *token = menu_config[j].token;
12394       char *value = getHashEntry(setup_file_hash, token);
12395
12396       if (value != NULL)
12397         *menu_config[j].value = get_integer_from_string(value);
12398     }
12399   }
12400
12401   // special case: initialize with default values that may be overwritten
12402   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12403   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12404   {
12405     struct TokenIntPtrInfo menu_config[] =
12406     {
12407       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12408       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12409       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12410       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12411       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12412       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12413       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12414       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12415       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12416     };
12417
12418     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12419     {
12420       char *token = menu_config[j].token;
12421       char *value = getHashEntry(setup_file_hash, token);
12422
12423       if (value != NULL)
12424         *menu_config[j].value = get_integer_from_string(value);
12425     }
12426   }
12427
12428   // special case: initialize with default values that may be overwritten
12429   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12430   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12431   {
12432     struct TokenIntPtrInfo menu_config[] =
12433     {
12434       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12435       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12436       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12437       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12438       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12439       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12440       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12441       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12442       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12443     };
12444
12445     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12446     {
12447       char *token = menu_config[j].token;
12448       char *value = getHashEntry(setup_file_hash, token);
12449
12450       if (value != NULL)
12451         *menu_config[j].value = get_token_parameter_value(token, value);
12452     }
12453   }
12454
12455   // special case: initialize with default values that may be overwritten
12456   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12457   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12458   {
12459     struct
12460     {
12461       char *token_prefix;
12462       struct RectWithBorder *struct_ptr;
12463     }
12464     vp_struct[] =
12465     {
12466       { "viewport.window",      &viewport.window[i]     },
12467       { "viewport.playfield",   &viewport.playfield[i]  },
12468       { "viewport.door_1",      &viewport.door_1[i]     },
12469       { "viewport.door_2",      &viewport.door_2[i]     }
12470     };
12471
12472     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12473     {
12474       struct TokenIntPtrInfo vp_config[] =
12475       {
12476         { ".x",                 &vp_struct[j].struct_ptr->x             },
12477         { ".y",                 &vp_struct[j].struct_ptr->y             },
12478         { ".width",             &vp_struct[j].struct_ptr->width         },
12479         { ".height",            &vp_struct[j].struct_ptr->height        },
12480         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12481         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12482         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12483         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12484         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12485         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12486         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12487         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12488         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12489         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12490         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12491         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12492         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12493         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12494         { ".align",             &vp_struct[j].struct_ptr->align         },
12495         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12496       };
12497
12498       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12499       {
12500         char *token = getStringCat2(vp_struct[j].token_prefix,
12501                                     vp_config[k].token);
12502         char *value = getHashEntry(setup_file_hash, token);
12503
12504         if (value != NULL)
12505           *vp_config[k].value = get_token_parameter_value(token, value);
12506
12507         free(token);
12508       }
12509     }
12510   }
12511
12512   // special case: initialize with default values that may be overwritten
12513   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12514   for (i = 0; title_info[i].info != NULL; i++)
12515   {
12516     struct TitleFadingInfo *info = title_info[i].info;
12517     char *base_token = title_info[i].text;
12518
12519     for (j = 0; title_tokens[j].type != -1; j++)
12520     {
12521       char *token = getStringCat2(base_token, title_tokens[j].text);
12522       char *value = getHashEntry(setup_file_hash, token);
12523
12524       if (value != NULL)
12525       {
12526         int parameter_value = get_token_parameter_value(token, value);
12527
12528         tfi = *info;
12529
12530         *(int *)title_tokens[j].value = (int)parameter_value;
12531
12532         *info = tfi;
12533       }
12534
12535       free(token);
12536     }
12537   }
12538
12539   // special case: initialize with default values that may be overwritten
12540   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12541   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12542   {
12543     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12544     char *base_token = titlemessage_arrays[i].text;
12545
12546     for (j = 0; titlemessage_tokens[j].type != -1; j++)
12547     {
12548       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12549       char *value = getHashEntry(setup_file_hash, token);
12550
12551       if (value != NULL)
12552       {
12553         int parameter_value = get_token_parameter_value(token, value);
12554
12555         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12556         {
12557           tmi = array[k];
12558
12559           if (titlemessage_tokens[j].type == TYPE_INTEGER)
12560             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
12561           else
12562             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12563
12564           array[k] = tmi;
12565         }
12566       }
12567
12568       free(token);
12569     }
12570   }
12571
12572   // special case: check if network and preview player positions are redefined,
12573   // to compare this later against the main menu level preview being redefined
12574   struct TokenIntPtrInfo menu_config_players[] =
12575   {
12576     { "main.network_players.x", &menu.main.network_players.redefined    },
12577     { "main.network_players.y", &menu.main.network_players.redefined    },
12578     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12579     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12580     { "preview.x",              &preview.redefined                      },
12581     { "preview.y",              &preview.redefined                      }
12582   };
12583
12584   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12585     *menu_config_players[i].value = FALSE;
12586
12587   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12588     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12589       *menu_config_players[i].value = TRUE;
12590
12591   // read (and overwrite with) values that may be specified in config file
12592   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12593
12594   freeSetupFileHash(setup_file_hash);
12595 }
12596
12597 void LoadMenuDesignSettings(void)
12598 {
12599   char *filename_base = UNDEFINED_FILENAME, *filename_local;
12600
12601   InitMenuDesignSettings_Static();
12602   InitMenuDesignSettings_SpecialPreProcessing();
12603
12604   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12605   {
12606     // first look for special settings configured in level series config
12607     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12608
12609     if (fileExists(filename_base))
12610       LoadMenuDesignSettingsFromFilename(filename_base);
12611   }
12612
12613   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12614
12615   if (filename_local != NULL && !strEqual(filename_base, filename_local))
12616     LoadMenuDesignSettingsFromFilename(filename_local);
12617
12618   InitMenuDesignSettings_SpecialPostProcessing();
12619 }
12620
12621 void LoadMenuDesignSettings_AfterGraphics(void)
12622 {
12623   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12624 }
12625
12626 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12627 {
12628   char *filename = getEditorSetupFilename();
12629   SetupFileList *setup_file_list, *list;
12630   SetupFileHash *element_hash;
12631   int num_unknown_tokens = 0;
12632   int i;
12633
12634   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12635     return;
12636
12637   element_hash = newSetupFileHash();
12638
12639   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12640     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12641
12642   // determined size may be larger than needed (due to unknown elements)
12643   *num_elements = 0;
12644   for (list = setup_file_list; list != NULL; list = list->next)
12645     (*num_elements)++;
12646
12647   // add space for up to 3 more elements for padding that may be needed
12648   *num_elements += 3;
12649
12650   // free memory for old list of elements, if needed
12651   checked_free(*elements);
12652
12653   // allocate memory for new list of elements
12654   *elements = checked_malloc(*num_elements * sizeof(int));
12655
12656   *num_elements = 0;
12657   for (list = setup_file_list; list != NULL; list = list->next)
12658   {
12659     char *value = getHashEntry(element_hash, list->token);
12660
12661     if (value == NULL)          // try to find obsolete token mapping
12662     {
12663       char *mapped_token = get_mapped_token(list->token);
12664
12665       if (mapped_token != NULL)
12666       {
12667         value = getHashEntry(element_hash, mapped_token);
12668
12669         free(mapped_token);
12670       }
12671     }
12672
12673     if (value != NULL)
12674     {
12675       (*elements)[(*num_elements)++] = atoi(value);
12676     }
12677     else
12678     {
12679       if (num_unknown_tokens == 0)
12680       {
12681         Warn("---");
12682         Warn("unknown token(s) found in config file:");
12683         Warn("- config file: '%s'", filename);
12684
12685         num_unknown_tokens++;
12686       }
12687
12688       Warn("- token: '%s'", list->token);
12689     }
12690   }
12691
12692   if (num_unknown_tokens > 0)
12693     Warn("---");
12694
12695   while (*num_elements % 4)     // pad with empty elements, if needed
12696     (*elements)[(*num_elements)++] = EL_EMPTY;
12697
12698   freeSetupFileList(setup_file_list);
12699   freeSetupFileHash(element_hash);
12700
12701 #if 0
12702   for (i = 0; i < *num_elements; i++)
12703     Debug("editor", "element '%s' [%d]\n",
12704           element_info[(*elements)[i]].token_name, (*elements)[i]);
12705 #endif
12706 }
12707
12708 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12709                                                      boolean is_sound)
12710 {
12711   SetupFileHash *setup_file_hash = NULL;
12712   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12713   char *filename_music, *filename_prefix, *filename_info;
12714   struct
12715   {
12716     char *token;
12717     char **value_ptr;
12718   }
12719   token_to_value_ptr[] =
12720   {
12721     { "title_header",   &tmp_music_file_info.title_header       },
12722     { "artist_header",  &tmp_music_file_info.artist_header      },
12723     { "album_header",   &tmp_music_file_info.album_header       },
12724     { "year_header",    &tmp_music_file_info.year_header        },
12725
12726     { "title",          &tmp_music_file_info.title              },
12727     { "artist",         &tmp_music_file_info.artist             },
12728     { "album",          &tmp_music_file_info.album              },
12729     { "year",           &tmp_music_file_info.year               },
12730
12731     { NULL,             NULL                                    },
12732   };
12733   int i;
12734
12735   filename_music = (is_sound ? getCustomSoundFilename(basename) :
12736                     getCustomMusicFilename(basename));
12737
12738   if (filename_music == NULL)
12739     return NULL;
12740
12741   // ---------- try to replace file extension ----------
12742
12743   filename_prefix = getStringCopy(filename_music);
12744   if (strrchr(filename_prefix, '.') != NULL)
12745     *strrchr(filename_prefix, '.') = '\0';
12746   filename_info = getStringCat2(filename_prefix, ".txt");
12747
12748   if (fileExists(filename_info))
12749     setup_file_hash = loadSetupFileHash(filename_info);
12750
12751   free(filename_prefix);
12752   free(filename_info);
12753
12754   if (setup_file_hash == NULL)
12755   {
12756     // ---------- try to add file extension ----------
12757
12758     filename_prefix = getStringCopy(filename_music);
12759     filename_info = getStringCat2(filename_prefix, ".txt");
12760
12761     if (fileExists(filename_info))
12762       setup_file_hash = loadSetupFileHash(filename_info);
12763
12764     free(filename_prefix);
12765     free(filename_info);
12766   }
12767
12768   if (setup_file_hash == NULL)
12769     return NULL;
12770
12771   // ---------- music file info found ----------
12772
12773   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12774
12775   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12776   {
12777     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12778
12779     *token_to_value_ptr[i].value_ptr =
12780       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12781   }
12782
12783   tmp_music_file_info.basename = getStringCopy(basename);
12784   tmp_music_file_info.music = music;
12785   tmp_music_file_info.is_sound = is_sound;
12786
12787   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12788   *new_music_file_info = tmp_music_file_info;
12789
12790   return new_music_file_info;
12791 }
12792
12793 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12794 {
12795   return get_music_file_info_ext(basename, music, FALSE);
12796 }
12797
12798 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12799 {
12800   return get_music_file_info_ext(basename, sound, TRUE);
12801 }
12802
12803 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12804                                      char *basename, boolean is_sound)
12805 {
12806   for (; list != NULL; list = list->next)
12807     if (list->is_sound == is_sound && strEqual(list->basename, basename))
12808       return TRUE;
12809
12810   return FALSE;
12811 }
12812
12813 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12814 {
12815   return music_info_listed_ext(list, basename, FALSE);
12816 }
12817
12818 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12819 {
12820   return music_info_listed_ext(list, basename, TRUE);
12821 }
12822
12823 void LoadMusicInfo(void)
12824 {
12825   char *music_directory = getCustomMusicDirectory();
12826   int num_music = getMusicListSize();
12827   int num_music_noconf = 0;
12828   int num_sounds = getSoundListSize();
12829   Directory *dir;
12830   DirectoryEntry *dir_entry;
12831   struct FileInfo *music, *sound;
12832   struct MusicFileInfo *next, **new;
12833   int i;
12834
12835   while (music_file_info != NULL)
12836   {
12837     next = music_file_info->next;
12838
12839     checked_free(music_file_info->basename);
12840
12841     checked_free(music_file_info->title_header);
12842     checked_free(music_file_info->artist_header);
12843     checked_free(music_file_info->album_header);
12844     checked_free(music_file_info->year_header);
12845
12846     checked_free(music_file_info->title);
12847     checked_free(music_file_info->artist);
12848     checked_free(music_file_info->album);
12849     checked_free(music_file_info->year);
12850
12851     free(music_file_info);
12852
12853     music_file_info = next;
12854   }
12855
12856   new = &music_file_info;
12857
12858   for (i = 0; i < num_music; i++)
12859   {
12860     music = getMusicListEntry(i);
12861
12862     if (music->filename == NULL)
12863       continue;
12864
12865     if (strEqual(music->filename, UNDEFINED_FILENAME))
12866       continue;
12867
12868     // a configured file may be not recognized as music
12869     if (!FileIsMusic(music->filename))
12870       continue;
12871
12872     if (!music_info_listed(music_file_info, music->filename))
12873     {
12874       *new = get_music_file_info(music->filename, i);
12875
12876       if (*new != NULL)
12877         new = &(*new)->next;
12878     }
12879   }
12880
12881   if ((dir = openDirectory(music_directory)) == NULL)
12882   {
12883     Warn("cannot read music directory '%s'", music_directory);
12884
12885     return;
12886   }
12887
12888   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
12889   {
12890     char *basename = dir_entry->basename;
12891     boolean music_already_used = FALSE;
12892     int i;
12893
12894     // skip all music files that are configured in music config file
12895     for (i = 0; i < num_music; i++)
12896     {
12897       music = getMusicListEntry(i);
12898
12899       if (music->filename == NULL)
12900         continue;
12901
12902       if (strEqual(basename, music->filename))
12903       {
12904         music_already_used = TRUE;
12905         break;
12906       }
12907     }
12908
12909     if (music_already_used)
12910       continue;
12911
12912     if (!FileIsMusic(dir_entry->filename))
12913       continue;
12914
12915     if (!music_info_listed(music_file_info, basename))
12916     {
12917       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12918
12919       if (*new != NULL)
12920         new = &(*new)->next;
12921     }
12922
12923     num_music_noconf++;
12924   }
12925
12926   closeDirectory(dir);
12927
12928   for (i = 0; i < num_sounds; i++)
12929   {
12930     sound = getSoundListEntry(i);
12931
12932     if (sound->filename == NULL)
12933       continue;
12934
12935     if (strEqual(sound->filename, UNDEFINED_FILENAME))
12936       continue;
12937
12938     // a configured file may be not recognized as sound
12939     if (!FileIsSound(sound->filename))
12940       continue;
12941
12942     if (!sound_info_listed(music_file_info, sound->filename))
12943     {
12944       *new = get_sound_file_info(sound->filename, i);
12945       if (*new != NULL)
12946         new = &(*new)->next;
12947     }
12948   }
12949
12950   // add pointers to previous list nodes
12951
12952   struct MusicFileInfo *node = music_file_info;
12953
12954   while (node != NULL)
12955   {
12956     if (node->next)
12957       node->next->prev = node;
12958
12959     node = node->next;
12960   }
12961 }
12962
12963 static void add_helpanim_entry(int element, int action, int direction,
12964                                int delay, int *num_list_entries)
12965 {
12966   struct HelpAnimInfo *new_list_entry;
12967   (*num_list_entries)++;
12968
12969   helpanim_info =
12970     checked_realloc(helpanim_info,
12971                     *num_list_entries * sizeof(struct HelpAnimInfo));
12972   new_list_entry = &helpanim_info[*num_list_entries - 1];
12973
12974   new_list_entry->element = element;
12975   new_list_entry->action = action;
12976   new_list_entry->direction = direction;
12977   new_list_entry->delay = delay;
12978 }
12979
12980 static void print_unknown_token(char *filename, char *token, int token_nr)
12981 {
12982   if (token_nr == 0)
12983   {
12984     Warn("---");
12985     Warn("unknown token(s) found in config file:");
12986     Warn("- config file: '%s'", filename);
12987   }
12988
12989   Warn("- token: '%s'", token);
12990 }
12991
12992 static void print_unknown_token_end(int token_nr)
12993 {
12994   if (token_nr > 0)
12995     Warn("---");
12996 }
12997
12998 void LoadHelpAnimInfo(void)
12999 {
13000   char *filename = getHelpAnimFilename();
13001   SetupFileList *setup_file_list = NULL, *list;
13002   SetupFileHash *element_hash, *action_hash, *direction_hash;
13003   int num_list_entries = 0;
13004   int num_unknown_tokens = 0;
13005   int i;
13006
13007   if (fileExists(filename))
13008     setup_file_list = loadSetupFileList(filename);
13009
13010   if (setup_file_list == NULL)
13011   {
13012     // use reliable default values from static configuration
13013     SetupFileList *insert_ptr;
13014
13015     insert_ptr = setup_file_list =
13016       newSetupFileList(helpanim_config[0].token,
13017                        helpanim_config[0].value);
13018
13019     for (i = 1; helpanim_config[i].token; i++)
13020       insert_ptr = addListEntry(insert_ptr,
13021                                 helpanim_config[i].token,
13022                                 helpanim_config[i].value);
13023   }
13024
13025   element_hash   = newSetupFileHash();
13026   action_hash    = newSetupFileHash();
13027   direction_hash = newSetupFileHash();
13028
13029   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13030     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13031
13032   for (i = 0; i < NUM_ACTIONS; i++)
13033     setHashEntry(action_hash, element_action_info[i].suffix,
13034                  i_to_a(element_action_info[i].value));
13035
13036   // do not store direction index (bit) here, but direction value!
13037   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13038     setHashEntry(direction_hash, element_direction_info[i].suffix,
13039                  i_to_a(1 << element_direction_info[i].value));
13040
13041   for (list = setup_file_list; list != NULL; list = list->next)
13042   {
13043     char *element_token, *action_token, *direction_token;
13044     char *element_value, *action_value, *direction_value;
13045     int delay = atoi(list->value);
13046
13047     if (strEqual(list->token, "end"))
13048     {
13049       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13050
13051       continue;
13052     }
13053
13054     /* first try to break element into element/action/direction parts;
13055        if this does not work, also accept combined "element[.act][.dir]"
13056        elements (like "dynamite.active"), which are unique elements */
13057
13058     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13059     {
13060       element_value = getHashEntry(element_hash, list->token);
13061       if (element_value != NULL)        // element found
13062         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13063                            &num_list_entries);
13064       else
13065       {
13066         // no further suffixes found -- this is not an element
13067         print_unknown_token(filename, list->token, num_unknown_tokens++);
13068       }
13069
13070       continue;
13071     }
13072
13073     // token has format "<prefix>.<something>"
13074
13075     action_token = strchr(list->token, '.');    // suffix may be action ...
13076     direction_token = action_token;             // ... or direction
13077
13078     element_token = getStringCopy(list->token);
13079     *strchr(element_token, '.') = '\0';
13080
13081     element_value = getHashEntry(element_hash, element_token);
13082
13083     if (element_value == NULL)          // this is no element
13084     {
13085       element_value = getHashEntry(element_hash, list->token);
13086       if (element_value != NULL)        // combined element found
13087         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13088                            &num_list_entries);
13089       else
13090         print_unknown_token(filename, list->token, num_unknown_tokens++);
13091
13092       free(element_token);
13093
13094       continue;
13095     }
13096
13097     action_value = getHashEntry(action_hash, action_token);
13098
13099     if (action_value != NULL)           // action found
13100     {
13101       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13102                     &num_list_entries);
13103
13104       free(element_token);
13105
13106       continue;
13107     }
13108
13109     direction_value = getHashEntry(direction_hash, direction_token);
13110
13111     if (direction_value != NULL)        // direction found
13112     {
13113       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13114                          &num_list_entries);
13115
13116       free(element_token);
13117
13118       continue;
13119     }
13120
13121     if (strchr(action_token + 1, '.') == NULL)
13122     {
13123       // no further suffixes found -- this is not an action nor direction
13124
13125       element_value = getHashEntry(element_hash, list->token);
13126       if (element_value != NULL)        // combined element found
13127         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13128                            &num_list_entries);
13129       else
13130         print_unknown_token(filename, list->token, num_unknown_tokens++);
13131
13132       free(element_token);
13133
13134       continue;
13135     }
13136
13137     // token has format "<prefix>.<suffix>.<something>"
13138
13139     direction_token = strchr(action_token + 1, '.');
13140
13141     action_token = getStringCopy(action_token);
13142     *strchr(action_token + 1, '.') = '\0';
13143
13144     action_value = getHashEntry(action_hash, action_token);
13145
13146     if (action_value == NULL)           // this is no action
13147     {
13148       element_value = getHashEntry(element_hash, list->token);
13149       if (element_value != NULL)        // combined element found
13150         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13151                            &num_list_entries);
13152       else
13153         print_unknown_token(filename, list->token, num_unknown_tokens++);
13154
13155       free(element_token);
13156       free(action_token);
13157
13158       continue;
13159     }
13160
13161     direction_value = getHashEntry(direction_hash, direction_token);
13162
13163     if (direction_value != NULL)        // direction found
13164     {
13165       add_helpanim_entry(atoi(element_value), atoi(action_value),
13166                          atoi(direction_value), delay, &num_list_entries);
13167
13168       free(element_token);
13169       free(action_token);
13170
13171       continue;
13172     }
13173
13174     // this is no direction
13175
13176     element_value = getHashEntry(element_hash, list->token);
13177     if (element_value != NULL)          // combined element found
13178       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13179                          &num_list_entries);
13180     else
13181       print_unknown_token(filename, list->token, num_unknown_tokens++);
13182
13183     free(element_token);
13184     free(action_token);
13185   }
13186
13187   print_unknown_token_end(num_unknown_tokens);
13188
13189   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13190   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13191
13192   freeSetupFileList(setup_file_list);
13193   freeSetupFileHash(element_hash);
13194   freeSetupFileHash(action_hash);
13195   freeSetupFileHash(direction_hash);
13196
13197 #if 0
13198   for (i = 0; i < num_list_entries; i++)
13199     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13200           EL_NAME(helpanim_info[i].element),
13201           helpanim_info[i].element,
13202           helpanim_info[i].action,
13203           helpanim_info[i].direction,
13204           helpanim_info[i].delay);
13205 #endif
13206 }
13207
13208 void LoadHelpTextInfo(void)
13209 {
13210   char *filename = getHelpTextFilename();
13211   int i;
13212
13213   if (helptext_info != NULL)
13214   {
13215     freeSetupFileHash(helptext_info);
13216     helptext_info = NULL;
13217   }
13218
13219   if (fileExists(filename))
13220     helptext_info = loadSetupFileHash(filename);
13221
13222   if (helptext_info == NULL)
13223   {
13224     // use reliable default values from static configuration
13225     helptext_info = newSetupFileHash();
13226
13227     for (i = 0; helptext_config[i].token; i++)
13228       setHashEntry(helptext_info,
13229                    helptext_config[i].token,
13230                    helptext_config[i].value);
13231   }
13232
13233 #if 0
13234   BEGIN_HASH_ITERATION(helptext_info, itr)
13235   {
13236     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13237           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13238   }
13239   END_HASH_ITERATION(hash, itr)
13240 #endif
13241 }
13242
13243
13244 // ----------------------------------------------------------------------------
13245 // convert levels
13246 // ----------------------------------------------------------------------------
13247
13248 #define MAX_NUM_CONVERT_LEVELS          1000
13249
13250 void ConvertLevels(void)
13251 {
13252   static LevelDirTree *convert_leveldir = NULL;
13253   static int convert_level_nr = -1;
13254   static int num_levels_handled = 0;
13255   static int num_levels_converted = 0;
13256   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13257   int i;
13258
13259   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13260                                                global.convert_leveldir);
13261
13262   if (convert_leveldir == NULL)
13263     Fail("no such level identifier: '%s'", global.convert_leveldir);
13264
13265   leveldir_current = convert_leveldir;
13266
13267   if (global.convert_level_nr != -1)
13268   {
13269     convert_leveldir->first_level = global.convert_level_nr;
13270     convert_leveldir->last_level  = global.convert_level_nr;
13271   }
13272
13273   convert_level_nr = convert_leveldir->first_level;
13274
13275   PrintLine("=", 79);
13276   Print("Converting levels\n");
13277   PrintLine("-", 79);
13278   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13279   Print("Level series name:       '%s'\n", convert_leveldir->name);
13280   Print("Level series author:     '%s'\n", convert_leveldir->author);
13281   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13282   PrintLine("=", 79);
13283   Print("\n");
13284
13285   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13286     levels_failed[i] = FALSE;
13287
13288   while (convert_level_nr <= convert_leveldir->last_level)
13289   {
13290     char *level_filename;
13291     boolean new_level;
13292
13293     level_nr = convert_level_nr++;
13294
13295     Print("Level %03d: ", level_nr);
13296
13297     LoadLevel(level_nr);
13298     if (level.no_level_file || level.no_valid_file)
13299     {
13300       Print("(no level)\n");
13301       continue;
13302     }
13303
13304     Print("converting level ... ");
13305
13306 #if 0
13307     // special case: conversion of some EMC levels as requested by ACME
13308     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13309 #endif
13310
13311     level_filename = getDefaultLevelFilename(level_nr);
13312     new_level = !fileExists(level_filename);
13313
13314     if (new_level)
13315     {
13316       SaveLevel(level_nr);
13317
13318       num_levels_converted++;
13319
13320       Print("converted.\n");
13321     }
13322     else
13323     {
13324       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13325         levels_failed[level_nr] = TRUE;
13326
13327       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13328     }
13329
13330     num_levels_handled++;
13331   }
13332
13333   Print("\n");
13334   PrintLine("=", 79);
13335   Print("Number of levels handled: %d\n", num_levels_handled);
13336   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13337          (num_levels_handled ?
13338           num_levels_converted * 100 / num_levels_handled : 0));
13339   PrintLine("-", 79);
13340   Print("Summary (for automatic parsing by scripts):\n");
13341   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13342          convert_leveldir->identifier, num_levels_converted,
13343          num_levels_handled,
13344          (num_levels_handled ?
13345           num_levels_converted * 100 / num_levels_handled : 0));
13346
13347   if (num_levels_handled != num_levels_converted)
13348   {
13349     Print(", FAILED:");
13350     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13351       if (levels_failed[i])
13352         Print(" %03d", i);
13353   }
13354
13355   Print("\n");
13356   PrintLine("=", 79);
13357
13358   CloseAllAndExit(0);
13359 }
13360
13361
13362 // ----------------------------------------------------------------------------
13363 // create and save images for use in level sketches (raw BMP format)
13364 // ----------------------------------------------------------------------------
13365
13366 void CreateLevelSketchImages(void)
13367 {
13368   Bitmap *bitmap1;
13369   Bitmap *bitmap2;
13370   int i;
13371
13372   InitElementPropertiesGfxElement();
13373
13374   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13375   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13376
13377   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13378   {
13379     int element = getMappedElement(i);
13380     char basename1[16];
13381     char basename2[16];
13382     char *filename1;
13383     char *filename2;
13384
13385     sprintf(basename1, "%04d.bmp", i);
13386     sprintf(basename2, "%04ds.bmp", i);
13387
13388     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13389     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13390
13391     DrawSizedElement(0, 0, element, TILESIZE);
13392     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13393
13394     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13395       Fail("cannot save level sketch image file '%s'", filename1);
13396
13397     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13398     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13399
13400     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13401       Fail("cannot save level sketch image file '%s'", filename2);
13402
13403     free(filename1);
13404     free(filename2);
13405
13406     // create corresponding SQL statements (for normal and small images)
13407     if (i < 1000)
13408     {
13409       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13410       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13411     }
13412
13413     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13414     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13415
13416     // optional: create content for forum level sketch demonstration post
13417     if (options.debug)
13418       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13419   }
13420
13421   FreeBitmap(bitmap1);
13422   FreeBitmap(bitmap2);
13423
13424   if (options.debug)
13425     fprintf(stderr, "\n");
13426
13427   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13428
13429   CloseAllAndExit(0);
13430 }
13431
13432
13433 // ----------------------------------------------------------------------------
13434 // create and save images for element collecting animations (raw BMP format)
13435 // ----------------------------------------------------------------------------
13436
13437 static boolean createCollectImage(int element)
13438 {
13439   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13440 }
13441
13442 void CreateCollectElementImages(void)
13443 {
13444   int i, j;
13445   int num_steps = 8;
13446   int anim_frames = num_steps - 1;
13447   int tile_size = TILESIZE;
13448   int anim_width  = tile_size * anim_frames;
13449   int anim_height = tile_size;
13450   int num_collect_images = 0;
13451   int pos_collect_images = 0;
13452
13453   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13454     if (createCollectImage(i))
13455       num_collect_images++;
13456
13457   Info("Creating %d element collecting animation images ...",
13458        num_collect_images);
13459
13460   int dst_width  = anim_width * 2;
13461   int dst_height = anim_height * num_collect_images / 2;
13462   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13463   char *basename_bmp = "RocksCollect.bmp";
13464   char *basename_png = "RocksCollect.png";
13465   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13466   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13467   int len_filename_bmp = strlen(filename_bmp);
13468   int len_filename_png = strlen(filename_png);
13469   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13470   char cmd_convert[max_command_len];
13471
13472   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13473            filename_bmp,
13474            filename_png);
13475
13476   // force using RGBA surface for destination bitmap
13477   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13478                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13479
13480   dst_bitmap->surface =
13481     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13482
13483   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13484   {
13485     if (!createCollectImage(i))
13486       continue;
13487
13488     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13489     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13490     int graphic = el2img(i);
13491     char *token_name = element_info[i].token_name;
13492     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13493     Bitmap *src_bitmap;
13494     int src_x, src_y;
13495
13496     Info("- creating collecting image for '%s' ...", token_name);
13497
13498     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13499
13500     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13501                tile_size, tile_size, 0, 0);
13502
13503     // force using RGBA surface for temporary bitmap (using transparent black)
13504     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13505                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13506
13507     tmp_bitmap->surface =
13508       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13509
13510     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13511
13512     for (j = 0; j < anim_frames; j++)
13513     {
13514       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13515       int frame_size = frame_size_final * num_steps;
13516       int offset = (tile_size - frame_size_final) / 2;
13517       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13518
13519       while (frame_size > frame_size_final)
13520       {
13521         frame_size /= 2;
13522
13523         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13524
13525         FreeBitmap(frame_bitmap);
13526
13527         frame_bitmap = half_bitmap;
13528       }
13529
13530       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13531                        frame_size_final, frame_size_final,
13532                        dst_x + j * tile_size + offset, dst_y + offset);
13533
13534       FreeBitmap(frame_bitmap);
13535     }
13536
13537     tmp_bitmap->surface_masked = NULL;
13538
13539     FreeBitmap(tmp_bitmap);
13540
13541     pos_collect_images++;
13542   }
13543
13544   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13545     Fail("cannot save element collecting image file '%s'", filename_bmp);
13546
13547   FreeBitmap(dst_bitmap);
13548
13549   Info("Converting image file from BMP to PNG ...");
13550
13551   system(cmd_convert);
13552   unlink(filename_bmp);
13553
13554   Info("Done.");
13555
13556   CloseAllAndExit(0);
13557 }
13558
13559
13560 // ----------------------------------------------------------------------------
13561 // create and save images for custom and group elements (raw BMP format)
13562 // ----------------------------------------------------------------------------
13563
13564 void CreateCustomElementImages(char *directory)
13565 {
13566   char *src_basename = "RocksCE-template.ilbm";
13567   char *dst_basename = "RocksCE.bmp";
13568   char *src_filename = getPath2(directory, src_basename);
13569   char *dst_filename = getPath2(directory, dst_basename);
13570   Bitmap *src_bitmap;
13571   Bitmap *bitmap;
13572   int yoffset_ce = 0;
13573   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13574   int i;
13575
13576   InitVideoDefaults();
13577
13578   ReCreateBitmap(&backbuffer, video.width, video.height);
13579
13580   src_bitmap = LoadImage(src_filename);
13581
13582   bitmap = CreateBitmap(TILEX * 16 * 2,
13583                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13584                         DEFAULT_DEPTH);
13585
13586   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13587   {
13588     int x = i % 16;
13589     int y = i / 16;
13590     int ii = i + 1;
13591     int j;
13592
13593     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13594                TILEX * x, TILEY * y + yoffset_ce);
13595
13596     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13597                TILEX, TILEY,
13598                TILEX * x + TILEX * 16,
13599                TILEY * y + yoffset_ce);
13600
13601     for (j = 2; j >= 0; j--)
13602     {
13603       int c = ii % 10;
13604
13605       BlitBitmap(src_bitmap, bitmap,
13606                  TILEX + c * 7, 0, 6, 10,
13607                  TILEX * x + 6 + j * 7,
13608                  TILEY * y + 11 + yoffset_ce);
13609
13610       BlitBitmap(src_bitmap, bitmap,
13611                  TILEX + c * 8, TILEY, 6, 10,
13612                  TILEX * 16 + TILEX * x + 6 + j * 8,
13613                  TILEY * y + 10 + yoffset_ce);
13614
13615       ii /= 10;
13616     }
13617   }
13618
13619   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13620   {
13621     int x = i % 16;
13622     int y = i / 16;
13623     int ii = i + 1;
13624     int j;
13625
13626     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13627                TILEX * x, TILEY * y + yoffset_ge);
13628
13629     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13630                TILEX, TILEY,
13631                TILEX * x + TILEX * 16,
13632                TILEY * y + yoffset_ge);
13633
13634     for (j = 1; j >= 0; j--)
13635     {
13636       int c = ii % 10;
13637
13638       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13639                  TILEX * x + 6 + j * 10,
13640                  TILEY * y + 11 + yoffset_ge);
13641
13642       BlitBitmap(src_bitmap, bitmap,
13643                  TILEX + c * 8, TILEY + 12, 6, 10,
13644                  TILEX * 16 + TILEX * x + 10 + j * 8,
13645                  TILEY * y + 10 + yoffset_ge);
13646
13647       ii /= 10;
13648     }
13649   }
13650
13651   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13652     Fail("cannot save CE graphics file '%s'", dst_filename);
13653
13654   FreeBitmap(bitmap);
13655
13656   CloseAllAndExit(0);
13657 }