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