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