rnd-20040111-3-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16 #include <dirent.h>
17 #include <math.h>
18
19 #include "libgame/libgame.h"
20
21 #include "files.h"
22 #include "init.h"
23 #include "tools.h"
24 #include "tape.h"
25
26
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
28 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
29 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
30 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
32 #define LEVEL_HEADER_UNUSED     13      /* unused level header bytes  */
33 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
35 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
37 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
38 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
39 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
40 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
41
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
44 #define LEVEL_CHUNK_GRP1_SIZE(x) (2 + 8 + (x) * 2)
45
46 /* file identifier strings */
47 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
48 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
49 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
50
51 /* values for level file type identifier */
52 #define LEVEL_FILE_TYPE_UNKNOWN         0
53 #define LEVEL_FILE_TYPE_RND             1
54 #define LEVEL_FILE_TYPE_EM              2
55
56 #define LEVEL_FILE_TYPE_RND_PACKED      (10 + LEVEL_FILE_TYPE_RND)
57 #define LEVEL_FILE_TYPE_EM_PACKED       (10 + LEVEL_FILE_TYPE_EM)
58
59 #define IS_SINGLE_LEVEL_FILE(x)         (x < 10)
60 #define IS_PACKED_LEVEL_FILE(x)         (x > 10)
61
62
63 /* ========================================================================= */
64 /* level file functions                                                      */
65 /* ========================================================================= */
66
67 void setElementChangePages(struct ElementInfo *ei, int change_pages)
68 {
69   int change_page_size = sizeof(struct ElementChangeInfo);
70
71   ei->num_change_pages = MAX(1, change_pages);
72
73   ei->change_page =
74     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
75
76   if (ei->current_change_page >= ei->num_change_pages)
77     ei->current_change_page = ei->num_change_pages - 1;
78
79   ei->change = &ei->change_page[ei->current_change_page];
80 }
81
82 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
83 {
84   int x, y;
85
86   change->can_change = FALSE;
87
88   change->events = CE_BITMASK_DEFAULT;
89   change->sides = CH_SIDE_ANY;
90
91   change->target_element = EL_EMPTY_SPACE;
92
93   change->delay_fixed = 0;
94   change->delay_random = 0;
95   change->delay_frames = 1;
96
97   change->trigger_element = EL_EMPTY_SPACE;
98
99   change->explode = FALSE;
100   change->use_content = FALSE;
101   change->only_complete = FALSE;
102   change->use_random_change = FALSE;
103   change->random = 100;
104   change->power = CP_NON_DESTRUCTIVE;
105
106   for (x = 0; x < 3; x++)
107     for (y = 0; y < 3; y++)
108       change->content[x][y] = EL_EMPTY_SPACE;
109
110   change->direct_action = 0;
111   change->other_action = 0;
112
113   change->pre_change_function = NULL;
114   change->change_function = NULL;
115   change->post_change_function = NULL;
116 }
117
118 static void setLevelInfoToDefaults(struct LevelInfo *level)
119 {
120   int i, j, x, y;
121
122   level->file_version = FILE_VERSION_ACTUAL;
123   level->game_version = GAME_VERSION_ACTUAL;
124
125   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
126   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
127   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
128
129   level->fieldx = STD_LEV_FIELDX;
130   level->fieldy = STD_LEV_FIELDY;
131
132   for (x = 0; x < MAX_LEV_FIELDX; x++)
133     for (y = 0; y < MAX_LEV_FIELDY; y++)
134       level->field[x][y] = EL_SAND;
135
136   level->time = 100;
137   level->gems_needed = 0;
138   level->amoeba_speed = 10;
139   level->time_magic_wall = 10;
140   level->time_wheel = 10;
141   level->time_light = 10;
142   level->time_timegate = 10;
143   level->amoeba_content = EL_DIAMOND;
144   level->double_speed = FALSE;
145   level->initial_gravity = FALSE;
146   level->em_slippery_gems = FALSE;
147
148   level->use_custom_template = FALSE;
149
150   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
151     level->name[i] = '\0';
152   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
153     level->author[i] = '\0';
154
155   strcpy(level->name, NAMELESS_LEVEL_NAME);
156   strcpy(level->author, ANONYMOUS_NAME);
157
158   for (i = 0; i < 4; i++)
159   {
160     level->envelope_text[i][0] = '\0';
161     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
162     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
163   }
164
165   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
166     level->score[i] = 10;
167
168   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
169   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
170     for (x = 0; x < 3; x++)
171       for (y = 0; y < 3; y++)
172         level->yamyam_content[i][x][y] =
173           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
174
175   level->field[0][0] = EL_PLAYER_1;
176   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
177
178   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
179   {
180     setElementChangePages(&element_info[i], 1);
181     setElementChangeInfoToDefaults(element_info[i].change);
182   }
183
184   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
185   {
186     int element = EL_CUSTOM_START + i;
187
188     for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
189       element_info[element].description[j] = '\0';
190     if (element_info[element].custom_description != NULL)
191       strncpy(element_info[element].description,
192               element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
193     else
194       strcpy(element_info[element].description,
195              element_info[element].editor_description);
196
197     element_info[element].use_gfx_element = FALSE;
198     element_info[element].gfx_element = EL_EMPTY_SPACE;
199
200     element_info[element].collect_score = 10;           /* special default */
201     element_info[element].collect_count = 1;            /* special default */
202
203     element_info[element].push_delay_fixed = -1;        /* initialize later */
204     element_info[element].push_delay_random = -1;       /* initialize later */
205     element_info[element].move_delay_fixed = 0;
206     element_info[element].move_delay_random = 0;
207
208     element_info[element].move_pattern = MV_ALL_DIRECTIONS;
209     element_info[element].move_direction_initial = MV_NO_MOVING;
210     element_info[element].move_stepsize = TILEX / 8;
211
212     element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
213
214     for (x = 0; x < 3; x++)
215       for (y = 0; y < 3; y++)
216         element_info[element].content[x][y] = EL_EMPTY_SPACE;
217
218     element_info[element].access_type = 0;
219     element_info[element].access_layer = 0;
220     element_info[element].walk_to_action = 0;
221     element_info[element].smash_targets = 0;
222     element_info[element].deadliness = 0;
223     element_info[element].consistency = 0;
224
225     element_info[element].can_explode_by_fire = FALSE;
226     element_info[element].can_explode_smashed = FALSE;
227     element_info[element].can_explode_impact = FALSE;
228
229     element_info[element].current_change_page = 0;
230
231     /* start with no properties at all */
232     for (j = 0; j < NUM_EP_BITFIELDS; j++)
233       Properties[element][j] = EP_BITMASK_DEFAULT;
234
235     element_info[element].modified_settings = FALSE;
236   }
237
238   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
239   {
240     int element = EL_GROUP_START + i;
241
242     if (element_info[element].group == NULL)
243       element_info[element].group =
244         checked_malloc(sizeof(struct ElementGroupInfo));
245
246     for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
247       element_info[element].group->element[j] = EL_EMPTY_SPACE;
248
249     element_info[element].group->num_elements = 1;
250   }
251
252   BorderElement = EL_STEELWALL;
253
254   level->no_level_file = FALSE;
255
256   if (leveldir_current == NULL)         /* only when dumping level */
257     return;
258
259   /* try to determine better author name than 'anonymous' */
260   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
261   {
262     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
263     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
264   }
265   else
266   {
267     switch (LEVELCLASS(leveldir_current))
268     {
269       case LEVELCLASS_TUTORIAL:
270         strcpy(level->author, PROGRAM_AUTHOR_STRING);
271         break;
272
273       case LEVELCLASS_CONTRIB:
274         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
275         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
276         break;
277
278       case LEVELCLASS_PRIVATE:
279         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
280         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
281         break;
282
283       default:
284         /* keep default value */
285         break;
286     }
287   }
288 }
289
290 static void ActivateLevelTemplate()
291 {
292   /* Currently there is no special action needed to activate the template
293      data, because 'element_info' and 'Properties' overwrite the original
294      level data, while all other variables do not change. */
295 }
296
297 static char *getLevelFilenameFromBasename(char *basename)
298 {
299   static char *filename = NULL;
300
301   checked_free(filename);
302
303   filename = getPath2(getCurrentLevelDir(), basename);
304
305   return filename;
306 }
307
308 static char *getSingleLevelBasename(int nr, int type)
309 {
310   static char basename[MAX_FILENAME_LEN];
311
312   switch (type)
313   {
314     case LEVEL_FILE_TYPE_RND:
315       if (nr < 0)
316         sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
317       else
318         sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
319       break;
320
321     case LEVEL_FILE_TYPE_EM:
322       sprintf(basename, "%d", nr);
323       break;
324
325     default:
326       strcpy(basename, UNDEFINED_FILENAME);
327       break;
328   }
329
330   return basename;
331 }
332
333 static char *getPackedLevelBasename(int type)
334 {
335   static char basename[MAX_FILENAME_LEN];
336
337   switch (type)
338   {
339     default:
340       strcpy(basename, UNDEFINED_FILENAME);
341       break;
342   }
343
344   return basename;
345 }
346
347 static char *getSingleLevelFilename(int nr, int type)
348 {
349   return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
350 }
351
352 static char *getPackedLevelFilename(int type)
353 {
354   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
355 }
356
357 char *getDefaultLevelFilename(int nr)
358 {
359   return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
360 }
361
362 static struct LevelFileInfo *getLevelFileInfo(int nr)
363 {
364   static struct LevelFileInfo level_file_info;
365
366   level_file_info.nr = nr;
367
368   /* special case: level template */
369   if (nr < 0)
370   {
371     level_file_info.type = LEVEL_FILE_TYPE_RND;
372     level_file_info.filename = getDefaultLevelFilename(nr);
373
374     return &level_file_info;
375   }
376
377   /* 1st try: check for native Rocks'n'Diamonds level file */
378   level_file_info.type = LEVEL_FILE_TYPE_RND;
379   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
380   if (fileExists(level_file_info.filename))
381     return &level_file_info;
382
383   /* 2nd try: check for classic Emerald Mine level file */
384   level_file_info.type = LEVEL_FILE_TYPE_EM;
385   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
386   if (fileExists(level_file_info.filename))
387     return &level_file_info;
388
389   /* no known level file found -- use default values */
390   level_file_info.type = LEVEL_FILE_TYPE_RND;
391   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
392
393   return &level_file_info;
394 }
395
396 /* ------------------------------------------------------------------------- */
397 /* functions for loading R'n'D level                                         */
398 /* ------------------------------------------------------------------------- */
399
400 static int checkLevelElement(int element)
401 {
402   /* map some (historic, now obsolete) elements */
403
404 #if 1
405   switch (element)
406   {
407     case EL_PLAYER_OBSOLETE:
408       element = EL_PLAYER_1;
409       break;
410
411     case EL_KEY_OBSOLETE:
412       element = EL_KEY_1;
413
414     case EL_EM_KEY_1_FILE_OBSOLETE:
415       element = EL_EM_KEY_1;
416       break;
417
418     case EL_EM_KEY_2_FILE_OBSOLETE:
419       element = EL_EM_KEY_2;
420       break;
421
422     case EL_EM_KEY_3_FILE_OBSOLETE:
423       element = EL_EM_KEY_3;
424       break;
425
426     case EL_EM_KEY_4_FILE_OBSOLETE:
427       element = EL_EM_KEY_4;
428       break;
429
430     case EL_ENVELOPE_OBSOLETE:
431       element = EL_ENVELOPE_1;
432       break;
433
434     case EL_SP_EMPTY:
435       element = EL_EMPTY;
436       break;
437
438     default:
439       if (element >= NUM_FILE_ELEMENTS)
440       {
441         Error(ERR_WARN, "invalid level element %d", element);
442
443         element = EL_CHAR_QUESTION;
444       }
445       break;
446   }
447 #else
448   if (element >= NUM_FILE_ELEMENTS)
449   {
450     Error(ERR_WARN, "invalid level element %d", element);
451
452     element = EL_CHAR_QUESTION;
453   }
454   else if (element == EL_PLAYER_OBSOLETE)
455     element = EL_PLAYER_1;
456   else if (element == EL_KEY_OBSOLETE)
457     element = EL_KEY_1;
458 #endif
459
460   return element;
461 }
462
463 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
464 {
465   level->file_version = getFileVersion(file);
466   level->game_version = getFileVersion(file);
467
468   return chunk_size;
469 }
470
471 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
472 {
473   int i, x, y;
474
475   level->fieldx = getFile8Bit(file);
476   level->fieldy = getFile8Bit(file);
477
478   level->time           = getFile16BitBE(file);
479   level->gems_needed    = getFile16BitBE(file);
480
481   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
482     level->name[i] = getFile8Bit(file);
483   level->name[MAX_LEVEL_NAME_LEN] = 0;
484
485   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
486     level->score[i] = getFile8Bit(file);
487
488   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
489   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
490     for (y = 0; y < 3; y++)
491       for (x = 0; x < 3; x++)
492         level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
493
494   level->amoeba_speed           = getFile8Bit(file);
495   level->time_magic_wall        = getFile8Bit(file);
496   level->time_wheel             = getFile8Bit(file);
497   level->amoeba_content         = checkLevelElement(getFile8Bit(file));
498   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
499   level->initial_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
500   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
501   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
502
503   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
504
505   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
506
507   return chunk_size;
508 }
509
510 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
511 {
512   int i;
513
514   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
515     level->author[i] = getFile8Bit(file);
516   level->author[MAX_LEVEL_NAME_LEN] = 0;
517
518   return chunk_size;
519 }
520
521 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
522 {
523   int x, y;
524   int chunk_size_expected = level->fieldx * level->fieldy;
525
526   /* Note: "chunk_size" was wrong before version 2.0 when elements are
527      stored with 16-bit encoding (and should be twice as big then).
528      Even worse, playfield data was stored 16-bit when only yamyam content
529      contained 16-bit elements and vice versa. */
530
531   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
532     chunk_size_expected *= 2;
533
534   if (chunk_size_expected != chunk_size)
535   {
536     ReadUnusedBytesFromFile(file, chunk_size);
537     return chunk_size_expected;
538   }
539
540   for (y = 0; y < level->fieldy; y++)
541     for (x = 0; x < level->fieldx; x++)
542       level->field[x][y] =
543         checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
544                           getFile8Bit(file));
545   return chunk_size;
546 }
547
548 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
549 {
550   int i, x, y;
551   int header_size = 4;
552   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
553   int chunk_size_expected = header_size + content_size;
554
555   /* Note: "chunk_size" was wrong before version 2.0 when elements are
556      stored with 16-bit encoding (and should be twice as big then).
557      Even worse, playfield data was stored 16-bit when only yamyam content
558      contained 16-bit elements and vice versa. */
559
560   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
561     chunk_size_expected += content_size;
562
563   if (chunk_size_expected != chunk_size)
564   {
565     ReadUnusedBytesFromFile(file, chunk_size);
566     return chunk_size_expected;
567   }
568
569   getFile8Bit(file);
570   level->num_yamyam_contents = getFile8Bit(file);
571   getFile8Bit(file);
572   getFile8Bit(file);
573
574   /* correct invalid number of content fields -- should never happen */
575   if (level->num_yamyam_contents < 1 ||
576       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
577     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
578
579   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
580     for (y = 0; y < 3; y++)
581       for (x = 0; x < 3; x++)
582         level->yamyam_content[i][x][y] =
583           checkLevelElement(level->encoding_16bit_field ?
584                             getFile16BitBE(file) : getFile8Bit(file));
585   return chunk_size;
586 }
587
588 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
589 {
590   int i, x, y;
591   int element;
592   int num_contents, content_xsize, content_ysize;
593   int content_array[MAX_ELEMENT_CONTENTS][3][3];
594
595   element = checkLevelElement(getFile16BitBE(file));
596   num_contents = getFile8Bit(file);
597   content_xsize = getFile8Bit(file);
598   content_ysize = getFile8Bit(file);
599
600   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
601
602   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
603     for (y = 0; y < 3; y++)
604       for (x = 0; x < 3; x++)
605         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
606
607   /* correct invalid number of content fields -- should never happen */
608   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
609     num_contents = STD_ELEMENT_CONTENTS;
610
611   if (element == EL_YAMYAM)
612   {
613     level->num_yamyam_contents = num_contents;
614
615     for (i = 0; i < num_contents; i++)
616       for (y = 0; y < 3; y++)
617         for (x = 0; x < 3; x++)
618           level->yamyam_content[i][x][y] = content_array[i][x][y];
619   }
620   else if (element == EL_BD_AMOEBA)
621   {
622     level->amoeba_content = content_array[0][0][0];
623   }
624   else
625   {
626     Error(ERR_WARN, "cannot load content for element '%d'", element);
627   }
628
629   return chunk_size;
630 }
631
632 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
633 {
634   int i;
635   int element;
636   int envelope_nr;
637   int envelope_len;
638   int chunk_size_expected;
639
640   element = checkLevelElement(getFile16BitBE(file));
641   if (!IS_ENVELOPE(element))
642     element = EL_ENVELOPE_1;
643
644   envelope_nr = element - EL_ENVELOPE_1;
645
646   envelope_len = getFile16BitBE(file);
647
648   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
649   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
650
651   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
652
653   chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
654
655   if (chunk_size_expected != chunk_size)
656   {
657     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
658     return chunk_size_expected;
659   }
660
661   for (i = 0; i < envelope_len; i++)
662     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
663
664   return chunk_size;
665 }
666
667 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
668 {
669   int num_changed_custom_elements = getFile16BitBE(file);
670   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
671   int i;
672
673   if (chunk_size_expected != chunk_size)
674   {
675     ReadUnusedBytesFromFile(file, chunk_size - 2);
676     return chunk_size_expected;
677   }
678
679   for (i = 0; i < num_changed_custom_elements; i++)
680   {
681     int element = getFile16BitBE(file);
682     int properties = getFile32BitBE(file);
683
684     if (IS_CUSTOM_ELEMENT(element))
685       Properties[element][EP_BITFIELD_BASE] = properties;
686     else
687       Error(ERR_WARN, "invalid custom element number %d", element);
688   }
689
690   return chunk_size;
691 }
692
693 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
694 {
695   int num_changed_custom_elements = getFile16BitBE(file);
696   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
697   int i;
698
699   if (chunk_size_expected != chunk_size)
700   {
701     ReadUnusedBytesFromFile(file, chunk_size - 2);
702     return chunk_size_expected;
703   }
704
705   for (i = 0; i < num_changed_custom_elements; i++)
706   {
707     int element = getFile16BitBE(file);
708     int custom_target_element = getFile16BitBE(file);
709
710     if (IS_CUSTOM_ELEMENT(element))
711       element_info[element].change->target_element = custom_target_element;
712     else
713       Error(ERR_WARN, "invalid custom element number %d", element);
714   }
715
716   return chunk_size;
717 }
718
719 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
720 {
721   int num_changed_custom_elements = getFile16BitBE(file);
722   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
723   int i, j, x, y;
724
725   if (chunk_size_expected != chunk_size)
726   {
727     ReadUnusedBytesFromFile(file, chunk_size - 2);
728     return chunk_size_expected;
729   }
730
731   for (i = 0; i < num_changed_custom_elements; i++)
732   {
733     int element = getFile16BitBE(file);
734
735     if (!IS_CUSTOM_ELEMENT(element))
736     {
737       Error(ERR_WARN, "invalid custom element number %d", element);
738
739       element = EL_DUMMY;
740     }
741
742     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
743       element_info[element].description[j] = getFile8Bit(file);
744     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
745
746     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
747
748     /* some free bytes for future properties and padding */
749     ReadUnusedBytesFromFile(file, 7);
750
751     element_info[element].use_gfx_element = getFile8Bit(file);
752     element_info[element].gfx_element =
753       checkLevelElement(getFile16BitBE(file));
754
755     element_info[element].collect_score = getFile8Bit(file);
756     element_info[element].collect_count = getFile8Bit(file);
757
758     element_info[element].push_delay_fixed = getFile16BitBE(file);
759     element_info[element].push_delay_random = getFile16BitBE(file);
760     element_info[element].move_delay_fixed = getFile16BitBE(file);
761     element_info[element].move_delay_random = getFile16BitBE(file);
762
763     element_info[element].move_pattern = getFile16BitBE(file);
764     element_info[element].move_direction_initial = getFile8Bit(file);
765     element_info[element].move_stepsize = getFile8Bit(file);
766
767     for (y = 0; y < 3; y++)
768       for (x = 0; x < 3; x++)
769         element_info[element].content[x][y] =
770           checkLevelElement(getFile16BitBE(file));
771
772     element_info[element].change->events = getFile32BitBE(file);
773
774     element_info[element].change->target_element =
775       checkLevelElement(getFile16BitBE(file));
776
777     element_info[element].change->delay_fixed = getFile16BitBE(file);
778     element_info[element].change->delay_random = getFile16BitBE(file);
779     element_info[element].change->delay_frames = getFile16BitBE(file);
780
781     element_info[element].change->trigger_element =
782       checkLevelElement(getFile16BitBE(file));
783
784     element_info[element].change->explode = getFile8Bit(file);
785     element_info[element].change->use_content = getFile8Bit(file);
786     element_info[element].change->only_complete = getFile8Bit(file);
787     element_info[element].change->use_random_change = getFile8Bit(file);
788
789     element_info[element].change->random = getFile8Bit(file);
790     element_info[element].change->power = getFile8Bit(file);
791
792     for (y = 0; y < 3; y++)
793       for (x = 0; x < 3; x++)
794         element_info[element].change->content[x][y] =
795           checkLevelElement(getFile16BitBE(file));
796
797     element_info[element].slippery_type = getFile8Bit(file);
798
799     /* some free bytes for future properties and padding */
800     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
801
802     /* mark that this custom element has been modified */
803     element_info[element].modified_settings = TRUE;
804   }
805
806   return chunk_size;
807 }
808
809 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
810 {
811   struct ElementInfo *ei;
812   int chunk_size_expected;
813   int element;
814   int i, x, y;
815
816   element = getFile16BitBE(file);
817
818   if (!IS_CUSTOM_ELEMENT(element))
819   {
820     Error(ERR_WARN, "invalid custom element number %d", element);
821
822     element = EL_DUMMY;
823   }
824
825   ei = &element_info[element];
826
827   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
828     ei->description[i] = getFile8Bit(file);
829   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
830
831   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
832   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
833
834   ei->num_change_pages = getFile8Bit(file);
835
836   /* some free bytes for future base property values and padding */
837   ReadUnusedBytesFromFile(file, 5);
838
839   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
840   if (chunk_size_expected != chunk_size)
841   {
842     ReadUnusedBytesFromFile(file, chunk_size - 48);
843     return chunk_size_expected;
844   }
845
846   /* read custom property values */
847
848   ei->use_gfx_element = getFile8Bit(file);
849   ei->gfx_element = checkLevelElement(getFile16BitBE(file));
850
851   ei->collect_score = getFile8Bit(file);
852   ei->collect_count = getFile8Bit(file);
853
854   ei->push_delay_fixed = getFile16BitBE(file);
855   ei->push_delay_random = getFile16BitBE(file);
856   ei->move_delay_fixed = getFile16BitBE(file);
857   ei->move_delay_random = getFile16BitBE(file);
858
859   ei->move_pattern = getFile16BitBE(file);
860   ei->move_direction_initial = getFile8Bit(file);
861   ei->move_stepsize = getFile8Bit(file);
862
863   ei->slippery_type = getFile8Bit(file);
864
865   for (y = 0; y < 3; y++)
866     for (x = 0; x < 3; x++)
867       ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
868
869   /* some free bytes for future custom property values and padding */
870   ReadUnusedBytesFromFile(file, 12);
871
872   /* read change property values */
873
874   setElementChangePages(ei, ei->num_change_pages);
875
876   for (i = 0; i < ei->num_change_pages; i++)
877   {
878     struct ElementChangeInfo *change = &ei->change_page[i];
879
880     /* always start with reliable default values */
881     setElementChangeInfoToDefaults(change);
882
883     change->events = getFile32BitBE(file);
884
885     change->target_element = checkLevelElement(getFile16BitBE(file));
886
887     change->delay_fixed = getFile16BitBE(file);
888     change->delay_random = getFile16BitBE(file);
889     change->delay_frames = getFile16BitBE(file);
890
891     change->trigger_element = checkLevelElement(getFile16BitBE(file));
892
893     change->explode = getFile8Bit(file);
894     change->use_content = getFile8Bit(file);
895     change->only_complete = getFile8Bit(file);
896     change->use_random_change = getFile8Bit(file);
897
898     change->random = getFile8Bit(file);
899     change->power = getFile8Bit(file);
900
901     for (y = 0; y < 3; y++)
902       for (x = 0; x < 3; x++)
903         change->content[x][y] = checkLevelElement(getFile16BitBE(file));
904
905     change->can_change = getFile8Bit(file);
906
907     change->sides = getFile8Bit(file);
908
909     if (change->sides == CH_SIDE_NONE)  /* correct empty sides field */
910       change->sides = CH_SIDE_ANY;
911
912     /* some free bytes for future change property values and padding */
913     ReadUnusedBytesFromFile(file, 8);
914   }
915
916   /* mark this custom element as modified */
917   ei->modified_settings = TRUE;
918
919   return chunk_size;
920 }
921
922 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
923 {
924   struct ElementGroupInfo *group;
925   int chunk_size_expected;
926   int element;
927   int i;
928
929   element = getFile16BitBE(file);
930
931   if (!IS_GROUP_ELEMENT(element))
932   {
933     Error(ERR_WARN, "invalid group element number %d", element);
934
935     ReadUnusedBytesFromFile(file, chunk_size - 2);
936     return chunk_size;
937   }
938
939   group = element_info[element].group;
940
941   group->num_elements = getFile8Bit(file);
942
943   /* some free bytes for future values and padding */
944   ReadUnusedBytesFromFile(file, 7);
945
946   chunk_size_expected = LEVEL_CHUNK_GRP1_SIZE(group->num_elements);
947   if (chunk_size_expected != chunk_size)
948   {
949     ReadUnusedBytesFromFile(file, chunk_size - 10);
950     return chunk_size_expected;
951   }
952
953   for (i = 0; i < group->num_elements; i++)
954     group->element[i] = checkLevelElement(getFile16BitBE(file));
955
956   /* mark this group element as modified */
957   element_info[element].modified_settings = TRUE;
958
959   return chunk_size;
960 }
961
962 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
963                                       struct LevelFileInfo *level_file_info)
964 {
965   char *filename = level_file_info->filename;
966   char cookie[MAX_LINE_LEN];
967   char chunk_name[CHUNK_ID_LEN + 1];
968   int chunk_size;
969   FILE *file;
970
971   /* always start with reliable default values */
972   setLevelInfoToDefaults(level);
973
974   if (!(file = fopen(filename, MODE_READ)))
975   {
976     level->no_level_file = TRUE;
977
978     if (level != &level_template)
979       Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
980
981     return;
982   }
983
984   getFileChunkBE(file, chunk_name, NULL);
985   if (strcmp(chunk_name, "RND1") == 0)
986   {
987     getFile32BitBE(file);               /* not used */
988
989     getFileChunkBE(file, chunk_name, NULL);
990     if (strcmp(chunk_name, "CAVE") != 0)
991     {
992       Error(ERR_WARN, "unknown format of level file '%s'", filename);
993       fclose(file);
994       return;
995     }
996   }
997   else  /* check for pre-2.0 file format with cookie string */
998   {
999     strcpy(cookie, chunk_name);
1000     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1001     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1002       cookie[strlen(cookie) - 1] = '\0';
1003
1004     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1005     {
1006       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1007       fclose(file);
1008       return;
1009     }
1010
1011     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1012     {
1013       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1014       fclose(file);
1015       return;
1016     }
1017
1018     /* pre-2.0 level files have no game version, so use file version here */
1019     level->game_version = level->file_version;
1020   }
1021
1022   if (level->file_version < FILE_VERSION_1_2)
1023   {
1024     /* level files from versions before 1.2.0 without chunk structure */
1025     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
1026     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1027   }
1028   else
1029   {
1030     static struct
1031     {
1032       char *name;
1033       int size;
1034       int (*loader)(FILE *, int, struct LevelInfo *);
1035     }
1036     chunk_info[] =
1037     {
1038       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
1039       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
1040       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
1041       { "BODY", -1,                     LoadLevel_BODY },
1042       { "CONT", -1,                     LoadLevel_CONT },
1043       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
1044       { "CNT3", -1,                     LoadLevel_CNT3 },
1045       { "CUS1", -1,                     LoadLevel_CUS1 },
1046       { "CUS2", -1,                     LoadLevel_CUS2 },
1047       { "CUS3", -1,                     LoadLevel_CUS3 },
1048       { "CUS4", -1,                     LoadLevel_CUS4 },
1049       { "GRP1", -1,                     LoadLevel_GRP1 },
1050       {  NULL,  0,                      NULL }
1051     };
1052
1053     while (getFileChunkBE(file, chunk_name, &chunk_size))
1054     {
1055       int i = 0;
1056
1057       while (chunk_info[i].name != NULL &&
1058              strcmp(chunk_name, chunk_info[i].name) != 0)
1059         i++;
1060
1061       if (chunk_info[i].name == NULL)
1062       {
1063         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1064               chunk_name, filename);
1065         ReadUnusedBytesFromFile(file, chunk_size);
1066       }
1067       else if (chunk_info[i].size != -1 &&
1068                chunk_info[i].size != chunk_size)
1069       {
1070         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1071               chunk_size, chunk_name, filename);
1072         ReadUnusedBytesFromFile(file, chunk_size);
1073       }
1074       else
1075       {
1076         /* call function to load this level chunk */
1077         int chunk_size_expected =
1078           (chunk_info[i].loader)(file, chunk_size, level);
1079
1080         /* the size of some chunks cannot be checked before reading other
1081            chunks first (like "HEAD" and "BODY") that contain some header
1082            information, so check them here */
1083         if (chunk_size_expected != chunk_size)
1084         {
1085           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1086                 chunk_size, chunk_name, filename);
1087         }
1088       }
1089     }
1090   }
1091
1092   fclose(file);
1093 }
1094
1095 /* ------------------------------------------------------------------------- */
1096 /* functions for loading EM level                                            */
1097 /* ------------------------------------------------------------------------- */
1098
1099 static int map_em_element_yam(int element)
1100 {
1101   switch (element)
1102   {
1103     case 0x00:  return EL_EMPTY;
1104     case 0x01:  return EL_EMERALD;
1105     case 0x02:  return EL_DIAMOND;
1106     case 0x03:  return EL_ROCK;
1107     case 0x04:  return EL_ROBOT;
1108     case 0x05:  return EL_SPACESHIP_UP;
1109     case 0x06:  return EL_BOMB;
1110     case 0x07:  return EL_BUG_UP;
1111     case 0x08:  return EL_AMOEBA_DROP;
1112     case 0x09:  return EL_NUT;
1113     case 0x0a:  return EL_YAMYAM;
1114     case 0x0b:  return EL_QUICKSAND_FULL;
1115     case 0x0c:  return EL_SAND;
1116     case 0x0d:  return EL_WALL_SLIPPERY;
1117     case 0x0e:  return EL_STEELWALL;
1118     case 0x0f:  return EL_WALL;
1119     case 0x10:  return EL_EM_KEY_1;
1120     case 0x11:  return EL_EM_KEY_2;
1121     case 0x12:  return EL_EM_KEY_4;
1122     case 0x13:  return EL_EM_KEY_3;
1123     case 0x14:  return EL_MAGIC_WALL;
1124     case 0x15:  return EL_ROBOT_WHEEL;
1125     case 0x16:  return EL_DYNAMITE;
1126
1127     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
1128     case 0x18:  return EL_BUG_UP;                       /* EMC */
1129     case 0x1a:  return EL_DIAMOND;                      /* EMC */
1130     case 0x1b:  return EL_EMERALD;                      /* EMC */
1131     case 0x25:  return EL_NUT;                          /* EMC */
1132     case 0x80:  return EL_EMPTY;                        /* EMC */
1133     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
1134     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
1135     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
1136     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
1137     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
1138     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
1139     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
1140     case 0xbd:  return EL_SAND;                         /* EMC */
1141
1142     default:
1143       Error(ERR_WARN, "invalid level element %d", element);
1144       return EL_CHAR_QUESTION;
1145   }
1146 }
1147
1148 static int map_em_element_field(int element)
1149 {
1150   if (element >= 0xc8 && element <= 0xe1)
1151     return EL_CHAR_A + (element - 0xc8);
1152   else if (element >= 0xe2 && element <= 0xeb)
1153     return EL_CHAR_0 + (element - 0xe2);
1154
1155   switch (element)
1156   {
1157     case 0x00:  return EL_ROCK;
1158     case 0x02:  return EL_DIAMOND;
1159     case 0x03:  return EL_DIAMOND;
1160     case 0x04:  return EL_ROBOT;
1161     case 0x05:  return EL_ROBOT;                        /* EMC */
1162     case 0x08:  return EL_SPACESHIP_UP;
1163     case 0x09:  return EL_SPACESHIP_RIGHT;
1164     case 0x0a:  return EL_SPACESHIP_DOWN;
1165     case 0x0b:  return EL_SPACESHIP_LEFT;
1166     case 0x0c:  return EL_SPACESHIP_UP;
1167     case 0x0d:  return EL_SPACESHIP_RIGHT;
1168     case 0x0e:  return EL_SPACESHIP_DOWN;
1169     case 0x0f:  return EL_SPACESHIP_LEFT;
1170     case 0x10:  return EL_BOMB;
1171     case 0x12:  return EL_EMERALD;
1172     case 0x13:  return EL_EMERALD;
1173     case 0x14:  return EL_BUG_UP;
1174     case 0x15:  return EL_BUG_RIGHT;
1175     case 0x16:  return EL_BUG_DOWN;
1176     case 0x17:  return EL_BUG_LEFT;
1177     case 0x18:  return EL_BUG_UP;
1178     case 0x19:  return EL_BUG_RIGHT;
1179     case 0x1a:  return EL_BUG_DOWN;
1180     case 0x1b:  return EL_BUG_LEFT;
1181     case 0x1c:  return EL_AMOEBA_DROP;
1182     case 0x20:  return EL_ROCK;
1183     case 0x24:  return EL_MAGIC_WALL;
1184     case 0x25:  return EL_NUT;
1185
1186       /* looks like magic wheel, but is _always_ activated */
1187     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
1188
1189     case 0x29:  return EL_YAMYAM;
1190     case 0x2a:  return EL_YAMYAM;
1191     case 0x2b:  return EL_YAMYAM;                       /* EMC */
1192     case 0x2c:  return EL_YAMYAM;                       /* EMC */
1193     case 0x2d:  return EL_QUICKSAND_FULL;
1194     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
1195     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
1196     case 0x3b:  return EL_DYNAMITE_ACTIVE;
1197     case 0x3c:  return EL_DYNAMITE_ACTIVE;
1198     case 0x3d:  return EL_DYNAMITE_ACTIVE;
1199     case 0x3e:  return EL_DYNAMITE_ACTIVE;
1200     case 0x3f:  return EL_ACID_POOL_BOTTOM;
1201     case 0x40:  return EL_EXIT_OPEN;
1202     case 0x41:  return EL_EXIT_OPEN;
1203     case 0x42:  return EL_EXIT_OPEN;
1204     case 0x43:  return EL_BALLOON;
1205     case 0x4e:  return EL_INVISIBLE_WALL;
1206     case 0x65:  return EL_ACID;                         /* EMC */
1207     case 0x73:  return EL_SAND;                         /* EMC */
1208     case 0x74:  return EL_STEELWALL;
1209     case 0x7b:  return EL_ACID;
1210     case 0x80:  return EL_EMPTY;
1211     case 0x81:  return EL_WALL_SLIPPERY;
1212     case 0x82:  return EL_SAND;
1213     case 0x83:  return EL_STEELWALL;
1214     case 0x84:  return EL_WALL;
1215     case 0x85:  return EL_EM_KEY_1;
1216     case 0x86:  return EL_EM_KEY_2;
1217     case 0x87:  return EL_EM_KEY_4;
1218     case 0x88:  return EL_EM_KEY_3;
1219     case 0x89:  return EL_EM_GATE_1;
1220     case 0x8a:  return EL_EM_GATE_2;
1221     case 0x8b:  return EL_EM_GATE_4;
1222     case 0x8c:  return EL_EM_GATE_3;
1223     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC */
1224     case 0x8e:  return EL_EM_GATE_1_GRAY;
1225     case 0x8f:  return EL_EM_GATE_2_GRAY;
1226     case 0x90:  return EL_EM_GATE_4_GRAY;
1227     case 0x91:  return EL_EM_GATE_3_GRAY;
1228     case 0x92:  return EL_MAGIC_WALL;
1229     case 0x94:  return EL_QUICKSAND_EMPTY;
1230     case 0x95:  return EL_ACID_POOL_TOPLEFT;
1231     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
1232     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
1233     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
1234     case 0x99:  return EL_ACID;
1235     case 0x9a:  return EL_AMOEBA_DEAD;
1236     case 0x9b:  return EL_AMOEBA_DEAD;
1237     case 0x9c:  return EL_AMOEBA_DEAD;
1238     case 0x9d:  return EL_AMOEBA_DEAD;
1239     case 0x9e:  return EL_EXIT_CLOSED;
1240     case 0x9f:  return EL_CHAR_LESS;                    /* EMC */
1241     case 0x93:  return EL_ROBOT_WHEEL;
1242
1243       /* looks like normal dust, but behaves like wall */
1244     case 0xa0:  return EL_WALL;                         /* EMC */
1245
1246     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC */
1247     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC */
1248     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC */
1249     case 0xab:  return EL_EMC_WALL_7;                   /* EMC */
1250     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
1251     case 0xaf:  return EL_DYNAMITE;
1252     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC */
1253     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC */
1254
1255       /* (exact steel wall) */
1256     case 0xb3:  return EL_STEELWALL;                    /* EMC */
1257
1258     case 0xb4:  return EL_WALL_SLIPPERY;                /* EMC */
1259     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC */
1260     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC */
1261     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC */
1262     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
1263     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
1264     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
1265     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
1266     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
1267     case 0xbd:  return EL_SAND;                         /* EMC */
1268     case 0xec:  return EL_CHAR_PERIOD;
1269     case 0xed:  return EL_CHAR_EXCLAM;
1270     case 0xee:  return EL_CHAR_COLON;
1271     case 0xef:  return EL_CHAR_QUESTION;
1272     case 0xf0:  return EL_CHAR_GREATER;
1273     case 0xf1:  return EL_CHAR_COPYRIGHT;
1274     case 0xfe:  return EL_PLAYER_1;
1275     case 0xff:  return EL_PLAYER_2;
1276
1277     default:
1278       Error(ERR_WARN, "invalid level element %d", element);
1279       return EL_CHAR_QUESTION;
1280   }
1281 }
1282
1283 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1284                                      struct LevelFileInfo *level_file_info)
1285 {
1286   char *filename = level_file_info->filename;
1287   FILE *file;
1288   unsigned char body[40][64];
1289   unsigned char *leveldata = &body[0][0];
1290   unsigned char *header = &leveldata[2048];
1291   unsigned char code0 = 0x65;
1292   unsigned char code1 = 0x11;
1293   boolean level_is_crypted = FALSE;
1294   int nr = level_file_info->nr;
1295   int jx, jy;
1296   int i, x, y;
1297
1298   /* always start with reliable default values */
1299   setLevelInfoToDefaults(level);
1300
1301   if (!(file = fopen(filename, MODE_READ)))
1302   {
1303     level->no_level_file = TRUE;
1304
1305     Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1306
1307     return;
1308   }
1309
1310   for(i = 0; i < 2106; i++)
1311     leveldata[i] = fgetc(file);
1312
1313   fclose(file);
1314
1315   /* check if level data is crypted by testing against known starting bytes
1316      of the few existing crypted level files (from Emerald Mine 1 + 2) */
1317
1318   if ((leveldata[0] == 0xf1 ||
1319        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1320   {
1321     level_is_crypted = TRUE;
1322
1323     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
1324       leveldata[0] = 0xf1;
1325   }
1326
1327   if (level_is_crypted)         /* decode crypted level data */
1328   {
1329     for(i = 0; i < 2106; i++)
1330     {
1331       leveldata[i] ^= code0;
1332       leveldata[i] -= code1;
1333
1334       code0  = (code0 + 7) & 0xff;
1335     }
1336   }
1337
1338   level->fieldx = 64;
1339   level->fieldy = 32;
1340
1341   level->time           = header[46] * 10;
1342   level->gems_needed    = header[47];
1343
1344   /* The original Emerald Mine levels have their level number stored
1345      at the second byte of the level file...
1346      Do not trust this information at other level files, e.g. EMC,
1347      but correct it anyway (normally the first row is completely
1348      steel wall, so the correction does not hurt anyway). */
1349
1350   if (leveldata[1] == nr)
1351     leveldata[1] = leveldata[2];        /* correct level number field */
1352
1353   sprintf(level->name, "Level %d", nr);
1354
1355   level->score[SC_EMERALD]      = header[36];
1356   level->score[SC_DIAMOND]      = header[37];
1357   level->score[SC_ROBOT]        = header[38];
1358   level->score[SC_SPACESHIP]    = header[39];
1359   level->score[SC_BUG]          = header[40];
1360   level->score[SC_YAMYAM]       = header[41];
1361   level->score[SC_NUT]          = header[42];
1362   level->score[SC_DYNAMITE]     = header[43];
1363   level->score[SC_TIME_BONUS]   = header[44];
1364
1365   level->num_yamyam_contents = 4;
1366
1367   for(i = 0; i < level->num_yamyam_contents; i++)
1368     for(y = 0; y < 3; y++)
1369       for(x = 0; x < 3; x++)
1370         level->yamyam_content[i][x][y] =
1371           map_em_element_yam(header[i * 9 + y * 3 + x]);
1372
1373   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
1374   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
1375   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
1376   level->amoeba_content         = EL_DIAMOND;
1377
1378   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1379   {
1380     int new_element = map_em_element_field(body[y][x]);
1381
1382     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1383       new_element = EL_AMOEBA_WET;
1384
1385     level->field[x][y] = new_element;
1386   }
1387
1388   jx = (header[48] * 256 + header[49]) % 64;
1389   jy = (header[48] * 256 + header[49]) / 64;
1390   level->field[jx][jy] = EL_PLAYER_1;
1391
1392   jx = (header[50] * 256 + header[51]) % 64;
1393   jy = (header[50] * 256 + header[51]) / 64;
1394   level->field[jx][jy] = EL_PLAYER_2;
1395 }
1396
1397 void LoadLevelFromFileInfo(struct LevelInfo *level,
1398                            struct LevelFileInfo *level_file_info)
1399 {
1400   switch (level_file_info->type)
1401   {
1402     case LEVEL_FILE_TYPE_RND:
1403       LoadLevelFromFileInfo_RND(level, level_file_info);
1404       break;
1405
1406     case LEVEL_FILE_TYPE_EM:
1407       LoadLevelFromFileInfo_EM(level, level_file_info);
1408       break;
1409
1410     default:
1411       LoadLevelFromFileInfo_RND(level, level_file_info);
1412       break;
1413   }
1414 }
1415
1416 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1417 {
1418   static struct LevelFileInfo level_file_info;
1419
1420   level_file_info.nr = 0;                       /* unknown */
1421   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
1422   level_file_info.filename = filename;
1423
1424   LoadLevelFromFileInfo(level, &level_file_info);
1425 }
1426
1427 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1428 {
1429   if (leveldir_current == NULL)         /* only when dumping level */
1430     return;
1431
1432 #if 0
1433   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1434 #endif
1435
1436   /* determine correct game engine version of current level */
1437 #if 1
1438   if (!leveldir_current->latest_engine)
1439 #else
1440   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1441       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1442       IS_LEVELCLASS_UNDEFINED(leveldir_current))
1443 #endif
1444   {
1445 #if 0
1446     printf("\n::: This level is private or contributed: '%s'\n", filename);
1447 #endif
1448
1449 #if 0
1450     printf("\n::: Use the stored game engine version for this level\n");
1451 #endif
1452
1453     /* For all levels which are not forced to use the latest game engine
1454        version (normally user contributed, private and undefined levels),
1455        use the version of the game engine the levels were created for.
1456
1457        Since 2.0.1, the game engine version is now directly stored
1458        in the level file (chunk "VERS"), so there is no need anymore
1459        to set the game version from the file version (except for old,
1460        pre-2.0 levels, where the game version is still taken from the
1461        file format version used to store the level -- see above). */
1462
1463     /* do some special adjustments to support older level versions */
1464     if (level->file_version == FILE_VERSION_1_0)
1465     {
1466       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1467       Error(ERR_WARN, "using high speed movement for player");
1468
1469       /* player was faster than monsters in (pre-)1.0 levels */
1470       level->double_speed = TRUE;
1471     }
1472
1473     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1474     if (level->game_version == VERSION_IDENT(2,0,1,0))
1475       level->em_slippery_gems = TRUE;
1476   }
1477   else
1478   {
1479 #if 0
1480     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1481            leveldir_current->sort_priority, filename);
1482 #endif
1483
1484 #if 0
1485     printf("\n::: Use latest game engine version for this level.\n");
1486 #endif
1487
1488     /* For all levels which are forced to use the latest game engine version
1489        (normally all but user contributed, private and undefined levels), set
1490        the game engine version to the actual version; this allows for actual
1491        corrections in the game engine to take effect for existing, converted
1492        levels (from "classic" or other existing games) to make the emulation
1493        of the corresponding game more accurate, while (hopefully) not breaking
1494        existing levels created from other players. */
1495
1496 #if 0
1497     printf("::: changing engine from %d to %d\n",
1498            level->game_version, GAME_VERSION_ACTUAL);
1499 #endif
1500
1501     level->game_version = GAME_VERSION_ACTUAL;
1502
1503     /* Set special EM style gems behaviour: EM style gems slip down from
1504        normal, steel and growing wall. As this is a more fundamental change,
1505        it seems better to set the default behaviour to "off" (as it is more
1506        natural) and make it configurable in the level editor (as a property
1507        of gem style elements). Already existing converted levels (neither
1508        private nor contributed levels) are changed to the new behaviour. */
1509
1510     if (level->file_version < FILE_VERSION_2_0)
1511       level->em_slippery_gems = TRUE;
1512   }
1513
1514 #if 0
1515   printf("::: => %d\n", level->game_version);
1516 #endif
1517 }
1518
1519 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1520 {
1521   int i, j;
1522
1523   /* map custom element change events that have changed in newer versions
1524      (these following values were accidentally changed in version 3.0.1) */
1525   if (level->game_version <= VERSION_IDENT(3,0,0,0))
1526   {
1527     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1528     {
1529       int element = EL_CUSTOM_START + i;
1530
1531       /* order of checking and copying events to be mapped is important */
1532       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1533       {
1534         if (HAS_CHANGE_EVENT(element, j - 2))
1535         {
1536           SET_CHANGE_EVENT(element, j - 2, FALSE);
1537           SET_CHANGE_EVENT(element, j, TRUE);
1538         }
1539       }
1540
1541       /* order of checking and copying events to be mapped is important */
1542       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1543       {
1544         if (HAS_CHANGE_EVENT(element, j - 1))
1545         {
1546           SET_CHANGE_EVENT(element, j - 1, FALSE);
1547           SET_CHANGE_EVENT(element, j, TRUE);
1548         }
1549       }
1550     }
1551   }
1552
1553   /* some custom element change events get mapped since version 3.0.3 */
1554   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1555   {
1556     int element = EL_CUSTOM_START + i;
1557
1558     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1559         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1560     {
1561       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1562       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1563
1564       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1565     }
1566   }
1567
1568   /* initialize "can_change" field for old levels with only one change page */
1569   if (level->game_version <= VERSION_IDENT(3,0,2,0))
1570   {
1571     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1572     {
1573       int element = EL_CUSTOM_START + i;
1574
1575       if (CAN_CHANGE(element))
1576         element_info[element].change->can_change = TRUE;
1577     }
1578   }
1579
1580 #if 0
1581   /* set default push delay values (corrected since version 3.0.7-1) */
1582   if (level->game_version < VERSION_IDENT(3,0,7,1))
1583   {
1584     game.default_push_delay_fixed = 2;
1585     game.default_push_delay_random = 8;
1586   }
1587   else
1588   {
1589     game.default_push_delay_fixed = 8;
1590     game.default_push_delay_random = 8;
1591   }
1592
1593   /* set uninitialized push delay values of custom elements in older levels */
1594   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1595   {
1596     int element = EL_CUSTOM_START + i;
1597
1598     if (element_info[element].push_delay_fixed == -1)
1599       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1600     if (element_info[element].push_delay_random == -1)
1601       element_info[element].push_delay_random = game.default_push_delay_random;
1602   }
1603 #endif
1604
1605   /* initialize element properties for level editor etc. */
1606   InitElementPropertiesEngine(level->game_version);
1607 }
1608
1609 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1610 {
1611   int x, y;
1612
1613   /* map elements that have changed in newer versions */
1614   for (y = 0; y < level->fieldy; y++)
1615   {
1616     for (x = 0; x < level->fieldx; x++)
1617     {
1618       int element = level->field[x][y];
1619
1620       if (level->game_version <= VERSION_IDENT(2,2,0,0))
1621       {
1622         /* map game font elements */
1623         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1624                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1625                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1626                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1627       }
1628
1629       if (level->game_version < VERSION_IDENT(3,0,0,0))
1630       {
1631         /* map Supaplex gravity tube elements */
1632         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1633                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1634                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1635                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1636                    element);
1637       }
1638
1639       level->field[x][y] = element;
1640     }
1641   }
1642
1643   /* copy elements to runtime playfield array */
1644   for (x = 0; x < MAX_LEV_FIELDX; x++)
1645     for (y = 0; y < MAX_LEV_FIELDY; y++)
1646       Feld[x][y] = level->field[x][y];
1647
1648   /* initialize level size variables for faster access */
1649   lev_fieldx = level->fieldx;
1650   lev_fieldy = level->fieldy;
1651
1652   /* determine border element for this level */
1653   SetBorderElement();
1654 }
1655
1656 void LoadLevelTemplate(int nr)
1657 {
1658 #if 1
1659   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1660   char *filename = level_file_info->filename;
1661
1662   LoadLevelFromFileInfo(&level_template, level_file_info);
1663 #else
1664   char *filename = getDefaultLevelFilename(nr);
1665
1666   LoadLevelFromFilename_RND(&level_template, filename);
1667 #endif
1668
1669   LoadLevel_InitVersion(&level, filename);
1670   LoadLevel_InitElements(&level, filename);
1671
1672   ActivateLevelTemplate();
1673 }
1674
1675 void LoadLevel(int nr)
1676 {
1677 #if 1
1678   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1679   char *filename = level_file_info->filename;
1680
1681   LoadLevelFromFileInfo(&level, level_file_info);
1682 #else
1683   char *filename = getLevelFilename(nr);
1684
1685   LoadLevelFromFilename_RND(&level, filename);
1686 #endif
1687
1688   if (level.use_custom_template)
1689     LoadLevelTemplate(-1);
1690
1691 #if 1
1692   LoadLevel_InitVersion(&level, filename);
1693   LoadLevel_InitElements(&level, filename);
1694   LoadLevel_InitPlayfield(&level, filename);
1695 #else
1696   LoadLevel_InitLevel(&level, filename);
1697 #endif
1698 }
1699
1700 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1701 {
1702   putFileVersion(file, level->file_version);
1703   putFileVersion(file, level->game_version);
1704 }
1705
1706 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1707 {
1708   int i, x, y;
1709
1710   putFile8Bit(file, level->fieldx);
1711   putFile8Bit(file, level->fieldy);
1712
1713   putFile16BitBE(file, level->time);
1714   putFile16BitBE(file, level->gems_needed);
1715
1716   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1717     putFile8Bit(file, level->name[i]);
1718
1719   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1720     putFile8Bit(file, level->score[i]);
1721
1722   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1723     for (y = 0; y < 3; y++)
1724       for (x = 0; x < 3; x++)
1725         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1726                            level->yamyam_content[i][x][y]));
1727   putFile8Bit(file, level->amoeba_speed);
1728   putFile8Bit(file, level->time_magic_wall);
1729   putFile8Bit(file, level->time_wheel);
1730   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1731                      level->amoeba_content));
1732   putFile8Bit(file, (level->double_speed ? 1 : 0));
1733   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1734   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1735   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1736
1737   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1738
1739   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1740 }
1741
1742 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1743 {
1744   int i;
1745
1746   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1747     putFile8Bit(file, level->author[i]);
1748 }
1749
1750 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1751 {
1752   int x, y;
1753
1754   for (y = 0; y < level->fieldy; y++) 
1755     for (x = 0; x < level->fieldx; x++) 
1756       if (level->encoding_16bit_field)
1757         putFile16BitBE(file, level->field[x][y]);
1758       else
1759         putFile8Bit(file, level->field[x][y]);
1760 }
1761
1762 #if 0
1763 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1764 {
1765   int i, x, y;
1766
1767   putFile8Bit(file, EL_YAMYAM);
1768   putFile8Bit(file, level->num_yamyam_contents);
1769   putFile8Bit(file, 0);
1770   putFile8Bit(file, 0);
1771
1772   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1773     for (y = 0; y < 3; y++)
1774       for (x = 0; x < 3; x++)
1775         if (level->encoding_16bit_field)
1776           putFile16BitBE(file, level->yamyam_content[i][x][y]);
1777         else
1778           putFile8Bit(file, level->yamyam_content[i][x][y]);
1779 }
1780 #endif
1781
1782 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1783 {
1784   int i, x, y;
1785   int num_contents, content_xsize, content_ysize;
1786   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1787
1788   if (element == EL_YAMYAM)
1789   {
1790     num_contents = level->num_yamyam_contents;
1791     content_xsize = 3;
1792     content_ysize = 3;
1793
1794     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1795       for (y = 0; y < 3; y++)
1796         for (x = 0; x < 3; x++)
1797           content_array[i][x][y] = level->yamyam_content[i][x][y];
1798   }
1799   else if (element == EL_BD_AMOEBA)
1800   {
1801     num_contents = 1;
1802     content_xsize = 1;
1803     content_ysize = 1;
1804
1805     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1806       for (y = 0; y < 3; y++)
1807         for (x = 0; x < 3; x++)
1808           content_array[i][x][y] = EL_EMPTY;
1809     content_array[0][0][0] = level->amoeba_content;
1810   }
1811   else
1812   {
1813     /* chunk header already written -- write empty chunk data */
1814     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1815
1816     Error(ERR_WARN, "cannot save content for element '%d'", element);
1817     return;
1818   }
1819
1820   putFile16BitBE(file, element);
1821   putFile8Bit(file, num_contents);
1822   putFile8Bit(file, content_xsize);
1823   putFile8Bit(file, content_ysize);
1824
1825   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1826
1827   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1828     for (y = 0; y < 3; y++)
1829       for (x = 0; x < 3; x++)
1830         putFile16BitBE(file, content_array[i][x][y]);
1831 }
1832
1833 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1834 {
1835   int i;
1836   int envelope_nr = element - EL_ENVELOPE_1;
1837   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1838
1839   putFile16BitBE(file, element);
1840   putFile16BitBE(file, envelope_len);
1841   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1842   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1843
1844   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1845
1846   for (i = 0; i < envelope_len; i++)
1847     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1848 }
1849
1850 #if 0
1851 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1852                            int num_changed_custom_elements)
1853 {
1854   int i, check = 0;
1855
1856   putFile16BitBE(file, num_changed_custom_elements);
1857
1858   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1859   {
1860     int element = EL_CUSTOM_START + i;
1861
1862     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1863     {
1864       if (check < num_changed_custom_elements)
1865       {
1866         putFile16BitBE(file, element);
1867         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1868       }
1869
1870       check++;
1871     }
1872   }
1873
1874   if (check != num_changed_custom_elements)     /* should not happen */
1875     Error(ERR_WARN, "inconsistent number of custom element properties");
1876 }
1877 #endif
1878
1879 #if 0
1880 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1881                            int num_changed_custom_elements)
1882 {
1883   int i, check = 0;
1884
1885   putFile16BitBE(file, num_changed_custom_elements);
1886
1887   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1888   {
1889     int element = EL_CUSTOM_START + i;
1890
1891     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1892     {
1893       if (check < num_changed_custom_elements)
1894       {
1895         putFile16BitBE(file, element);
1896         putFile16BitBE(file, element_info[element].change->target_element);
1897       }
1898
1899       check++;
1900     }
1901   }
1902
1903   if (check != num_changed_custom_elements)     /* should not happen */
1904     Error(ERR_WARN, "inconsistent number of custom target elements");
1905 }
1906 #endif
1907
1908 #if 0
1909 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1910                            int num_changed_custom_elements)
1911 {
1912   int i, j, x, y, check = 0;
1913
1914   putFile16BitBE(file, num_changed_custom_elements);
1915
1916   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1917   {
1918     int element = EL_CUSTOM_START + i;
1919
1920     if (element_info[element].modified_settings)
1921     {
1922       if (check < num_changed_custom_elements)
1923       {
1924         putFile16BitBE(file, element);
1925
1926         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1927           putFile8Bit(file, element_info[element].description[j]);
1928
1929         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1930
1931         /* some free bytes for future properties and padding */
1932         WriteUnusedBytesToFile(file, 7);
1933
1934         putFile8Bit(file, element_info[element].use_gfx_element);
1935         putFile16BitBE(file, element_info[element].gfx_element);
1936
1937         putFile8Bit(file, element_info[element].collect_score);
1938         putFile8Bit(file, element_info[element].collect_count);
1939
1940         putFile16BitBE(file, element_info[element].push_delay_fixed);
1941         putFile16BitBE(file, element_info[element].push_delay_random);
1942         putFile16BitBE(file, element_info[element].move_delay_fixed);
1943         putFile16BitBE(file, element_info[element].move_delay_random);
1944
1945         putFile16BitBE(file, element_info[element].move_pattern);
1946         putFile8Bit(file, element_info[element].move_direction_initial);
1947         putFile8Bit(file, element_info[element].move_stepsize);
1948
1949         for (y = 0; y < 3; y++)
1950           for (x = 0; x < 3; x++)
1951             putFile16BitBE(file, element_info[element].content[x][y]);
1952
1953         putFile32BitBE(file, element_info[element].change->events);
1954
1955         putFile16BitBE(file, element_info[element].change->target_element);
1956
1957         putFile16BitBE(file, element_info[element].change->delay_fixed);
1958         putFile16BitBE(file, element_info[element].change->delay_random);
1959         putFile16BitBE(file, element_info[element].change->delay_frames);
1960
1961         putFile16BitBE(file, element_info[element].change->trigger_element);
1962
1963         putFile8Bit(file, element_info[element].change->explode);
1964         putFile8Bit(file, element_info[element].change->use_content);
1965         putFile8Bit(file, element_info[element].change->only_complete);
1966         putFile8Bit(file, element_info[element].change->use_random_change);
1967
1968         putFile8Bit(file, element_info[element].change->random);
1969         putFile8Bit(file, element_info[element].change->power);
1970
1971         for (y = 0; y < 3; y++)
1972           for (x = 0; x < 3; x++)
1973             putFile16BitBE(file, element_info[element].change->content[x][y]);
1974
1975         putFile8Bit(file, element_info[element].slippery_type);
1976
1977         /* some free bytes for future properties and padding */
1978         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1979       }
1980
1981       check++;
1982     }
1983   }
1984
1985   if (check != num_changed_custom_elements)     /* should not happen */
1986     Error(ERR_WARN, "inconsistent number of custom element properties");
1987 }
1988 #endif
1989
1990 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1991 {
1992   struct ElementInfo *ei = &element_info[element];
1993   int i, x, y;
1994
1995   putFile16BitBE(file, element);
1996
1997   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1998     putFile8Bit(file, ei->description[i]);
1999
2000   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2001   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
2002
2003   putFile8Bit(file, ei->num_change_pages);
2004
2005   /* some free bytes for future base property values and padding */
2006   WriteUnusedBytesToFile(file, 5);
2007
2008   /* write custom property values */
2009
2010   putFile8Bit(file, ei->use_gfx_element);
2011   putFile16BitBE(file, ei->gfx_element);
2012
2013   putFile8Bit(file, ei->collect_score);
2014   putFile8Bit(file, ei->collect_count);
2015
2016   putFile16BitBE(file, ei->push_delay_fixed);
2017   putFile16BitBE(file, ei->push_delay_random);
2018   putFile16BitBE(file, ei->move_delay_fixed);
2019   putFile16BitBE(file, ei->move_delay_random);
2020
2021   putFile16BitBE(file, ei->move_pattern);
2022   putFile8Bit(file, ei->move_direction_initial);
2023   putFile8Bit(file, ei->move_stepsize);
2024
2025   putFile8Bit(file, ei->slippery_type);
2026
2027   for (y = 0; y < 3; y++)
2028     for (x = 0; x < 3; x++)
2029       putFile16BitBE(file, ei->content[x][y]);
2030
2031   /* some free bytes for future custom property values and padding */
2032   WriteUnusedBytesToFile(file, 12);
2033
2034   /* write change property values */
2035
2036   for (i = 0; i < ei->num_change_pages; i++)
2037   {
2038     struct ElementChangeInfo *change = &ei->change_page[i];
2039
2040     putFile32BitBE(file, change->events);
2041
2042     putFile16BitBE(file, change->target_element);
2043
2044     putFile16BitBE(file, change->delay_fixed);
2045     putFile16BitBE(file, change->delay_random);
2046     putFile16BitBE(file, change->delay_frames);
2047
2048     putFile16BitBE(file, change->trigger_element);
2049
2050     putFile8Bit(file, change->explode);
2051     putFile8Bit(file, change->use_content);
2052     putFile8Bit(file, change->only_complete);
2053     putFile8Bit(file, change->use_random_change);
2054
2055     putFile8Bit(file, change->random);
2056     putFile8Bit(file, change->power);
2057
2058     for (y = 0; y < 3; y++)
2059       for (x = 0; x < 3; x++)
2060         putFile16BitBE(file, change->content[x][y]);
2061
2062     putFile8Bit(file, change->can_change);
2063
2064     putFile8Bit(file, change->sides);
2065
2066     /* some free bytes for future change property values and padding */
2067     WriteUnusedBytesToFile(file, 8);
2068   }
2069 }
2070
2071 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2072 {
2073   struct ElementGroupInfo *group = element_info[element].group;
2074   int i;
2075
2076   putFile16BitBE(file, element);
2077
2078   putFile8Bit(file, group->num_elements);
2079
2080   /* some free bytes for future values and padding */
2081   WriteUnusedBytesToFile(file, 7);
2082
2083   for (i = 0; i < group->num_elements; i++)
2084     putFile16BitBE(file, group->element[i]);
2085 }
2086
2087 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2088 {
2089   int body_chunk_size;
2090   int i, x, y;
2091   FILE *file;
2092
2093   if (!(file = fopen(filename, MODE_WRITE)))
2094   {
2095     Error(ERR_WARN, "cannot save level file '%s'", filename);
2096     return;
2097   }
2098
2099   level->file_version = FILE_VERSION_ACTUAL;
2100   level->game_version = GAME_VERSION_ACTUAL;
2101
2102   /* check level field for 16-bit elements */
2103   level->encoding_16bit_field = FALSE;
2104   for (y = 0; y < level->fieldy; y++) 
2105     for (x = 0; x < level->fieldx; x++) 
2106       if (level->field[x][y] > 255)
2107         level->encoding_16bit_field = TRUE;
2108
2109   /* check yamyam content for 16-bit elements */
2110   level->encoding_16bit_yamyam = FALSE;
2111   for (i = 0; i < level->num_yamyam_contents; i++)
2112     for (y = 0; y < 3; y++)
2113       for (x = 0; x < 3; x++)
2114         if (level->yamyam_content[i][x][y] > 255)
2115           level->encoding_16bit_yamyam = TRUE;
2116
2117   /* check amoeba content for 16-bit elements */
2118   level->encoding_16bit_amoeba = FALSE;
2119   if (level->amoeba_content > 255)
2120     level->encoding_16bit_amoeba = TRUE;
2121
2122   /* calculate size of "BODY" chunk */
2123   body_chunk_size =
2124     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2125
2126   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2127   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2128
2129   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2130   SaveLevel_VERS(file, level);
2131
2132   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2133   SaveLevel_HEAD(file, level);
2134
2135   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2136   SaveLevel_AUTH(file, level);
2137
2138   putFileChunkBE(file, "BODY", body_chunk_size);
2139   SaveLevel_BODY(file, level);
2140
2141   if (level->encoding_16bit_yamyam ||
2142       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2143   {
2144     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2145     SaveLevel_CNT2(file, level, EL_YAMYAM);
2146   }
2147
2148   if (level->encoding_16bit_amoeba)
2149   {
2150     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2151     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2152   }
2153
2154   /* check for envelope content */
2155   for (i = 0; i < 4; i++)
2156   {
2157     if (strlen(level->envelope_text[i]) > 0)
2158     {
2159       int envelope_len = strlen(level->envelope_text[i]) + 1;
2160
2161       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
2162       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2163     }
2164   }
2165
2166   /* check for non-default custom elements (unless using template level) */
2167   if (!level->use_custom_template)
2168   {
2169     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2170     {
2171       int element = EL_CUSTOM_START + i;
2172
2173       if (element_info[element].modified_settings)
2174       {
2175         int num_change_pages = element_info[element].num_change_pages;
2176
2177         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2178         SaveLevel_CUS4(file, level, element);
2179       }
2180     }
2181   }
2182
2183   /* check for non-default group elements (unless using template level) */
2184   if (!level->use_custom_template)
2185   {
2186     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2187     {
2188       int element = EL_GROUP_START + i;
2189
2190       if (element_info[element].modified_settings)
2191       {
2192         int num_elements = element_info[element].group->num_elements;
2193
2194         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE(num_elements));
2195         SaveLevel_GRP1(file, level, element);
2196       }
2197     }
2198   }
2199
2200   fclose(file);
2201
2202   SetFilePermissions(filename, PERMS_PRIVATE);
2203 }
2204
2205 void SaveLevel(int nr)
2206 {
2207   char *filename = getDefaultLevelFilename(nr);
2208
2209   SaveLevelFromFilename(&level, filename);
2210 }
2211
2212 void SaveLevelTemplate()
2213 {
2214   char *filename = getDefaultLevelFilename(-1);
2215
2216   SaveLevelFromFilename(&level, filename);
2217 }
2218
2219 void DumpLevel(struct LevelInfo *level)
2220 {
2221   printf_line("-", 79);
2222   printf("Level xxx (file version %08d, game version %08d)\n",
2223          level->file_version, level->game_version);
2224   printf_line("-", 79);
2225
2226   printf("Level Author: '%s'\n", level->author);
2227   printf("Level Title:  '%s'\n", level->name);
2228   printf("\n");
2229   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2230   printf("\n");
2231   printf("Level Time:  %d seconds\n", level->time);
2232   printf("Gems needed: %d\n", level->gems_needed);
2233   printf("\n");
2234   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2235   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
2236   printf("Time for Light:      %d seconds\n", level->time_light);
2237   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
2238   printf("\n");
2239   printf("Amoeba Speed: %d\n", level->amoeba_speed);
2240   printf("\n");
2241   printf("Gravity:                %s\n", (level->initial_gravity ? "yes" : "no"));
2242   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
2243   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2244
2245   printf_line("-", 79);
2246 }
2247
2248
2249 /* ========================================================================= */
2250 /* tape file functions                                                       */
2251 /* ========================================================================= */
2252
2253 static void setTapeInfoToDefaults()
2254 {
2255   int i;
2256
2257   /* always start with reliable default values (empty tape) */
2258   TapeErase();
2259
2260   /* default values (also for pre-1.2 tapes) with only the first player */
2261   tape.player_participates[0] = TRUE;
2262   for (i = 1; i < MAX_PLAYERS; i++)
2263     tape.player_participates[i] = FALSE;
2264
2265   /* at least one (default: the first) player participates in every tape */
2266   tape.num_participating_players = 1;
2267
2268   tape.level_nr = level_nr;
2269   tape.counter = 0;
2270   tape.changed = FALSE;
2271
2272   tape.recording = FALSE;
2273   tape.playing = FALSE;
2274   tape.pausing = FALSE;
2275 }
2276
2277 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2278 {
2279   tape->file_version = getFileVersion(file);
2280   tape->game_version = getFileVersion(file);
2281
2282   return chunk_size;
2283 }
2284
2285 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2286 {
2287   int i;
2288
2289   tape->random_seed = getFile32BitBE(file);
2290   tape->date        = getFile32BitBE(file);
2291   tape->length      = getFile32BitBE(file);
2292
2293   /* read header fields that are new since version 1.2 */
2294   if (tape->file_version >= FILE_VERSION_1_2)
2295   {
2296     byte store_participating_players = getFile8Bit(file);
2297     int engine_version;
2298
2299     /* since version 1.2, tapes store which players participate in the tape */
2300     tape->num_participating_players = 0;
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       tape->player_participates[i] = FALSE;
2304
2305       if (store_participating_players & (1 << i))
2306       {
2307         tape->player_participates[i] = TRUE;
2308         tape->num_participating_players++;
2309       }
2310     }
2311
2312     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2313
2314     engine_version = getFileVersion(file);
2315     if (engine_version > 0)
2316       tape->engine_version = engine_version;
2317     else
2318       tape->engine_version = tape->game_version;
2319   }
2320
2321   return chunk_size;
2322 }
2323
2324 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2325 {
2326   int level_identifier_size;
2327   int i;
2328
2329   level_identifier_size = getFile16BitBE(file);
2330
2331   tape->level_identifier =
2332     checked_realloc(tape->level_identifier, level_identifier_size);
2333
2334   for (i = 0; i < level_identifier_size; i++)
2335     tape->level_identifier[i] = getFile8Bit(file);
2336
2337   tape->level_nr = getFile16BitBE(file);
2338
2339   chunk_size = 2 + level_identifier_size + 2;
2340
2341   return chunk_size;
2342 }
2343
2344 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2345 {
2346   int i, j;
2347   int chunk_size_expected =
2348     (tape->num_participating_players + 1) * tape->length;
2349
2350   if (chunk_size_expected != chunk_size)
2351   {
2352     ReadUnusedBytesFromFile(file, chunk_size);
2353     return chunk_size_expected;
2354   }
2355
2356   for (i = 0; i < tape->length; i++)
2357   {
2358     if (i >= MAX_TAPELEN)
2359       break;
2360
2361     for (j = 0; j < MAX_PLAYERS; j++)
2362     {
2363       tape->pos[i].action[j] = MV_NO_MOVING;
2364
2365       if (tape->player_participates[j])
2366         tape->pos[i].action[j] = getFile8Bit(file);
2367     }
2368
2369     tape->pos[i].delay = getFile8Bit(file);
2370
2371     if (tape->file_version == FILE_VERSION_1_0)
2372     {
2373       /* eliminate possible diagonal moves in old tapes */
2374       /* this is only for backward compatibility */
2375
2376       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2377       byte action = tape->pos[i].action[0];
2378       int k, num_moves = 0;
2379
2380       for (k = 0; k<4; k++)
2381       {
2382         if (action & joy_dir[k])
2383         {
2384           tape->pos[i + num_moves].action[0] = joy_dir[k];
2385           if (num_moves > 0)
2386             tape->pos[i + num_moves].delay = 0;
2387           num_moves++;
2388         }
2389       }
2390
2391       if (num_moves > 1)
2392       {
2393         num_moves--;
2394         i += num_moves;
2395         tape->length += num_moves;
2396       }
2397     }
2398     else if (tape->file_version < FILE_VERSION_2_0)
2399     {
2400       /* convert pre-2.0 tapes to new tape format */
2401
2402       if (tape->pos[i].delay > 1)
2403       {
2404         /* action part */
2405         tape->pos[i + 1] = tape->pos[i];
2406         tape->pos[i + 1].delay = 1;
2407
2408         /* delay part */
2409         for (j = 0; j < MAX_PLAYERS; j++)
2410           tape->pos[i].action[j] = MV_NO_MOVING;
2411         tape->pos[i].delay--;
2412
2413         i++;
2414         tape->length++;
2415       }
2416     }
2417
2418     if (feof(file))
2419       break;
2420   }
2421
2422   if (i != tape->length)
2423     chunk_size = (tape->num_participating_players + 1) * i;
2424
2425   return chunk_size;
2426 }
2427
2428 void LoadTapeFromFilename(char *filename)
2429 {
2430   char cookie[MAX_LINE_LEN];
2431   char chunk_name[CHUNK_ID_LEN + 1];
2432   FILE *file;
2433   int chunk_size;
2434
2435   /* always start with reliable default values */
2436   setTapeInfoToDefaults();
2437
2438   if (!(file = fopen(filename, MODE_READ)))
2439     return;
2440
2441   getFileChunkBE(file, chunk_name, NULL);
2442   if (strcmp(chunk_name, "RND1") == 0)
2443   {
2444     getFile32BitBE(file);               /* not used */
2445
2446     getFileChunkBE(file, chunk_name, NULL);
2447     if (strcmp(chunk_name, "TAPE") != 0)
2448     {
2449       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2450       fclose(file);
2451       return;
2452     }
2453   }
2454   else  /* check for pre-2.0 file format with cookie string */
2455   {
2456     strcpy(cookie, chunk_name);
2457     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2458     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2459       cookie[strlen(cookie) - 1] = '\0';
2460
2461     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2462     {
2463       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2464       fclose(file);
2465       return;
2466     }
2467
2468     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2469     {
2470       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2471       fclose(file);
2472       return;
2473     }
2474
2475     /* pre-2.0 tape files have no game version, so use file version here */
2476     tape.game_version = tape.file_version;
2477   }
2478
2479   if (tape.file_version < FILE_VERSION_1_2)
2480   {
2481     /* tape files from versions before 1.2.0 without chunk structure */
2482     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2483     LoadTape_BODY(file, 2 * tape.length,  &tape);
2484   }
2485   else
2486   {
2487     static struct
2488     {
2489       char *name;
2490       int size;
2491       int (*loader)(FILE *, int, struct TapeInfo *);
2492     }
2493     chunk_info[] =
2494     {
2495       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
2496       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
2497       { "INFO", -1,                     LoadTape_INFO },
2498       { "BODY", -1,                     LoadTape_BODY },
2499       {  NULL,  0,                      NULL }
2500     };
2501
2502     while (getFileChunkBE(file, chunk_name, &chunk_size))
2503     {
2504       int i = 0;
2505
2506       while (chunk_info[i].name != NULL &&
2507              strcmp(chunk_name, chunk_info[i].name) != 0)
2508         i++;
2509
2510       if (chunk_info[i].name == NULL)
2511       {
2512         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2513               chunk_name, filename);
2514         ReadUnusedBytesFromFile(file, chunk_size);
2515       }
2516       else if (chunk_info[i].size != -1 &&
2517                chunk_info[i].size != chunk_size)
2518       {
2519         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2520               chunk_size, chunk_name, filename);
2521         ReadUnusedBytesFromFile(file, chunk_size);
2522       }
2523       else
2524       {
2525         /* call function to load this tape chunk */
2526         int chunk_size_expected =
2527           (chunk_info[i].loader)(file, chunk_size, &tape);
2528
2529         /* the size of some chunks cannot be checked before reading other
2530            chunks first (like "HEAD" and "BODY") that contain some header
2531            information, so check them here */
2532         if (chunk_size_expected != chunk_size)
2533         {
2534           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2535                 chunk_size, chunk_name, filename);
2536         }
2537       }
2538     }
2539   }
2540
2541   fclose(file);
2542
2543   tape.length_seconds = GetTapeLength();
2544
2545 #if 0
2546   printf("::: tape game version: %d\n", tape.game_version);
2547   printf("::: tape engine version: %d\n", tape.engine_version);
2548 #endif
2549 }
2550
2551 void LoadTape(int nr)
2552 {
2553   char *filename = getTapeFilename(nr);
2554
2555   LoadTapeFromFilename(filename);
2556 }
2557
2558 void LoadSolutionTape(int nr)
2559 {
2560   char *filename = getSolutionTapeFilename(nr);
2561
2562   LoadTapeFromFilename(filename);
2563 }
2564
2565 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2566 {
2567   putFileVersion(file, tape->file_version);
2568   putFileVersion(file, tape->game_version);
2569 }
2570
2571 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2572 {
2573   int i;
2574   byte store_participating_players = 0;
2575
2576   /* set bits for participating players for compact storage */
2577   for (i = 0; i < MAX_PLAYERS; i++)
2578     if (tape->player_participates[i])
2579       store_participating_players |= (1 << i);
2580
2581   putFile32BitBE(file, tape->random_seed);
2582   putFile32BitBE(file, tape->date);
2583   putFile32BitBE(file, tape->length);
2584
2585   putFile8Bit(file, store_participating_players);
2586
2587   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2588   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2589
2590   putFileVersion(file, tape->engine_version);
2591 }
2592
2593 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2594 {
2595   int level_identifier_size = strlen(tape->level_identifier) + 1;
2596   int i;
2597
2598   putFile16BitBE(file, level_identifier_size);
2599
2600   for (i = 0; i < level_identifier_size; i++)
2601     putFile8Bit(file, tape->level_identifier[i]);
2602
2603   putFile16BitBE(file, tape->level_nr);
2604 }
2605
2606 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2607 {
2608   int i, j;
2609
2610   for (i = 0; i < tape->length; i++)
2611   {
2612     for (j = 0; j < MAX_PLAYERS; j++)
2613       if (tape->player_participates[j])
2614         putFile8Bit(file, tape->pos[i].action[j]);
2615
2616     putFile8Bit(file, tape->pos[i].delay);
2617   }
2618 }
2619
2620 void SaveTape(int nr)
2621 {
2622   char *filename = getTapeFilename(nr);
2623   FILE *file;
2624   boolean new_tape = TRUE;
2625   int num_participating_players = 0;
2626   int info_chunk_size;
2627   int body_chunk_size;
2628   int i;
2629
2630   InitTapeDirectory(leveldir_current->filename);
2631
2632   /* if a tape still exists, ask to overwrite it */
2633   if (access(filename, F_OK) == 0)
2634   {
2635     new_tape = FALSE;
2636     if (!Request("Replace old tape ?", REQ_ASK))
2637       return;
2638   }
2639
2640   if (!(file = fopen(filename, MODE_WRITE)))
2641   {
2642     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2643     return;
2644   }
2645
2646   tape.file_version = FILE_VERSION_ACTUAL;
2647   tape.game_version = GAME_VERSION_ACTUAL;
2648
2649   /* count number of participating players  */
2650   for (i = 0; i < MAX_PLAYERS; i++)
2651     if (tape.player_participates[i])
2652       num_participating_players++;
2653
2654   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2655   body_chunk_size = (num_participating_players + 1) * tape.length;
2656
2657   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2658   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2659
2660   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2661   SaveTape_VERS(file, &tape);
2662
2663   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2664   SaveTape_HEAD(file, &tape);
2665
2666   putFileChunkBE(file, "INFO", info_chunk_size);
2667   SaveTape_INFO(file, &tape);
2668
2669   putFileChunkBE(file, "BODY", body_chunk_size);
2670   SaveTape_BODY(file, &tape);
2671
2672   fclose(file);
2673
2674   SetFilePermissions(filename, PERMS_PRIVATE);
2675
2676   tape.changed = FALSE;
2677
2678   if (new_tape)
2679     Request("tape saved !", REQ_CONFIRM);
2680 }
2681
2682 void DumpTape(struct TapeInfo *tape)
2683 {
2684   int i, j;
2685
2686   if (TAPE_IS_EMPTY(*tape))
2687   {
2688     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2689     return;
2690   }
2691
2692   printf_line("-", 79);
2693   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2694          tape->level_nr, tape->file_version, tape->game_version);
2695   printf("Level series identifier: '%s'\n", tape->level_identifier);
2696   printf_line("-", 79);
2697
2698   for (i = 0; i < tape->length; i++)
2699   {
2700     if (i >= MAX_TAPELEN)
2701       break;
2702
2703     printf("%03d: ", i);
2704
2705     for (j = 0; j < MAX_PLAYERS; j++)
2706     {
2707       if (tape->player_participates[j])
2708       {
2709         int action = tape->pos[i].action[j];
2710
2711         printf("%d:%02x ", j, action);
2712         printf("[%c%c%c%c|%c%c] - ",
2713                (action & JOY_LEFT ? '<' : ' '),
2714                (action & JOY_RIGHT ? '>' : ' '),
2715                (action & JOY_UP ? '^' : ' '),
2716                (action & JOY_DOWN ? 'v' : ' '),
2717                (action & JOY_BUTTON_1 ? '1' : ' '),
2718                (action & JOY_BUTTON_2 ? '2' : ' '));
2719       }
2720     }
2721
2722     printf("(%03d)\n", tape->pos[i].delay);
2723   }
2724
2725   printf_line("-", 79);
2726 }
2727
2728
2729 /* ========================================================================= */
2730 /* score file functions                                                      */
2731 /* ========================================================================= */
2732
2733 void LoadScore(int nr)
2734 {
2735   int i;
2736   char *filename = getScoreFilename(nr);
2737   char cookie[MAX_LINE_LEN];
2738   char line[MAX_LINE_LEN];
2739   char *line_ptr;
2740   FILE *file;
2741
2742   /* always start with reliable default values */
2743   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2744   {
2745     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2746     highscore[i].Score = 0;
2747   }
2748
2749   if (!(file = fopen(filename, MODE_READ)))
2750     return;
2751
2752   /* check file identifier */
2753   fgets(cookie, MAX_LINE_LEN, file);
2754   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2755     cookie[strlen(cookie) - 1] = '\0';
2756
2757   if (!checkCookieString(cookie, SCORE_COOKIE))
2758   {
2759     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2760     fclose(file);
2761     return;
2762   }
2763
2764   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2765   {
2766     fscanf(file, "%d", &highscore[i].Score);
2767     fgets(line, MAX_LINE_LEN, file);
2768
2769     if (line[strlen(line) - 1] == '\n')
2770       line[strlen(line) - 1] = '\0';
2771
2772     for (line_ptr = line; *line_ptr; line_ptr++)
2773     {
2774       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2775       {
2776         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2777         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2778         break;
2779       }
2780     }
2781   }
2782
2783   fclose(file);
2784 }
2785
2786 void SaveScore(int nr)
2787 {
2788   int i;
2789   char *filename = getScoreFilename(nr);
2790   FILE *file;
2791
2792   InitScoreDirectory(leveldir_current->filename);
2793
2794   if (!(file = fopen(filename, MODE_WRITE)))
2795   {
2796     Error(ERR_WARN, "cannot save score for level %d", nr);
2797     return;
2798   }
2799
2800   fprintf(file, "%s\n\n", SCORE_COOKIE);
2801
2802   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2803     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2804
2805   fclose(file);
2806
2807   SetFilePermissions(filename, PERMS_PUBLIC);
2808 }
2809
2810
2811 /* ========================================================================= */
2812 /* setup file functions                                                      */
2813 /* ========================================================================= */
2814
2815 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2816
2817 /* global setup */
2818 #define SETUP_TOKEN_PLAYER_NAME                 0
2819 #define SETUP_TOKEN_SOUND                       1
2820 #define SETUP_TOKEN_SOUND_LOOPS                 2
2821 #define SETUP_TOKEN_SOUND_MUSIC                 3
2822 #define SETUP_TOKEN_SOUND_SIMPLE                4
2823 #define SETUP_TOKEN_TOONS                       5
2824 #define SETUP_TOKEN_SCROLL_DELAY                6
2825 #define SETUP_TOKEN_SOFT_SCROLLING              7
2826 #define SETUP_TOKEN_FADING                      8
2827 #define SETUP_TOKEN_AUTORECORD                  9
2828 #define SETUP_TOKEN_QUICK_DOORS                 10
2829 #define SETUP_TOKEN_TEAM_MODE                   11
2830 #define SETUP_TOKEN_HANDICAP                    12
2831 #define SETUP_TOKEN_TIME_LIMIT                  13
2832 #define SETUP_TOKEN_FULLSCREEN                  14
2833 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2834 #define SETUP_TOKEN_GRAPHICS_SET                16
2835 #define SETUP_TOKEN_SOUNDS_SET                  17
2836 #define SETUP_TOKEN_MUSIC_SET                   18
2837 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2838 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2839 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2840
2841 #define NUM_GLOBAL_SETUP_TOKENS                 22
2842
2843 /* editor setup */
2844 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2845 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2846 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2847 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2848 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2849 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2850 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2851 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2852 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2853 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2854 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2855 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
2856
2857 #define NUM_EDITOR_SETUP_TOKENS                 12
2858
2859 /* shortcut setup */
2860 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2861 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2862 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2863
2864 #define NUM_SHORTCUT_SETUP_TOKENS               3
2865
2866 /* player setup */
2867 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2868 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2869 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2870 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2871 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2872 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2873 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2874 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2875 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2876 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
2877 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2878 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2879 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2880 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2881 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2882 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
2883
2884 #define NUM_PLAYER_SETUP_TOKENS                 16
2885
2886 /* system setup */
2887 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2888 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2889
2890 #define NUM_SYSTEM_SETUP_TOKENS                 2
2891
2892 /* options setup */
2893 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2894
2895 #define NUM_OPTIONS_SETUP_TOKENS                1
2896
2897
2898 static struct SetupInfo si;
2899 static struct SetupEditorInfo sei;
2900 static struct SetupShortcutInfo ssi;
2901 static struct SetupInputInfo sii;
2902 static struct SetupSystemInfo syi;
2903 static struct OptionInfo soi;
2904
2905 static struct TokenInfo global_setup_tokens[] =
2906 {
2907   { TYPE_STRING, &si.player_name,       "player_name"                   },
2908   { TYPE_SWITCH, &si.sound,             "sound"                         },
2909   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2910   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2911   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2912   { TYPE_SWITCH, &si.toons,             "toons"                         },
2913   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2914   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2915   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2916   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2917   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2918   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2919   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2920   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2921   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2922   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2923   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2924   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2925   { TYPE_STRING, &si.music_set,         "music_set"                     },
2926   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2927   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2928   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2929 };
2930
2931 static struct TokenInfo editor_setup_tokens[] =
2932 {
2933   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2934   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2935   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2936   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2937   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2938   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2939   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2940   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2941   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2942   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2943   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2944   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
2945 };
2946
2947 static struct TokenInfo shortcut_setup_tokens[] =
2948 {
2949   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2950   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2951   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2952 };
2953
2954 static struct TokenInfo player_setup_tokens[] =
2955 {
2956   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2957   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2958   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2959   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2960   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2961   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2962   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2963   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2964   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2965   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
2966   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2967   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2968   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2969   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2970   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2971   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
2972 };
2973
2974 static struct TokenInfo system_setup_tokens[] =
2975 {
2976   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2977   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2978 };
2979
2980 static struct TokenInfo options_setup_tokens[] =
2981 {
2982   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2983 };
2984
2985 static char *get_corrected_login_name(char *login_name)
2986 {
2987   /* needed because player name must be a fixed length string */
2988   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2989
2990   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2991   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2992
2993   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2994     if (strchr(login_name_new, ' '))
2995       *strchr(login_name_new, ' ') = '\0';
2996
2997   return login_name_new;
2998 }
2999
3000 static void setSetupInfoToDefaults(struct SetupInfo *si)
3001 {
3002   int i;
3003
3004   si->player_name = get_corrected_login_name(getLoginName());
3005
3006   si->sound = TRUE;
3007   si->sound_loops = TRUE;
3008   si->sound_music = TRUE;
3009   si->sound_simple = TRUE;
3010   si->toons = TRUE;
3011   si->double_buffering = TRUE;
3012   si->direct_draw = !si->double_buffering;
3013   si->scroll_delay = TRUE;
3014   si->soft_scrolling = TRUE;
3015   si->fading = FALSE;
3016   si->autorecord = TRUE;
3017   si->quick_doors = FALSE;
3018   si->team_mode = FALSE;
3019   si->handicap = TRUE;
3020   si->time_limit = TRUE;
3021   si->fullscreen = FALSE;
3022   si->ask_on_escape = TRUE;
3023
3024   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3025   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3026   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3027   si->override_level_graphics = FALSE;
3028   si->override_level_sounds = FALSE;
3029   si->override_level_music = FALSE;
3030
3031   si->editor.el_boulderdash = TRUE;
3032   si->editor.el_emerald_mine = TRUE;
3033   si->editor.el_more = TRUE;
3034   si->editor.el_sokoban = TRUE;
3035   si->editor.el_supaplex = TRUE;
3036   si->editor.el_diamond_caves = TRUE;
3037   si->editor.el_dx_boulderdash = TRUE;
3038   si->editor.el_chars = TRUE;
3039   si->editor.el_custom = TRUE;
3040   si->editor.el_custom_more = FALSE;
3041
3042   si->editor.el_headlines = TRUE;
3043   si->editor.el_user_defined = FALSE;
3044
3045   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3046   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3047   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3048
3049   for (i = 0; i < MAX_PLAYERS; i++)
3050   {
3051     si->input[i].use_joystick = FALSE;
3052     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3053     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
3054     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3055     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
3056     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
3057     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3058     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
3059     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
3060     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
3061     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
3062     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3063     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
3064     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
3065     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
3066     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
3067   }
3068
3069   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3070   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3071
3072   si->options.verbose = FALSE;
3073 }
3074
3075 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3076 {
3077   int i, pnr;
3078
3079   if (!setup_file_hash)
3080     return;
3081
3082   /* global setup */
3083   si = setup;
3084   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3085     setSetupInfo(global_setup_tokens, i,
3086                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3087   setup = si;
3088
3089   /* editor setup */
3090   sei = setup.editor;
3091   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3092     setSetupInfo(editor_setup_tokens, i,
3093                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3094   setup.editor = sei;
3095
3096   /* shortcut setup */
3097   ssi = setup.shortcut;
3098   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3099     setSetupInfo(shortcut_setup_tokens, i,
3100                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3101   setup.shortcut = ssi;
3102
3103   /* player setup */
3104   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3105   {
3106     char prefix[30];
3107
3108     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3109
3110     sii = setup.input[pnr];
3111     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3112     {
3113       char full_token[100];
3114
3115       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3116       setSetupInfo(player_setup_tokens, i,
3117                    getHashEntry(setup_file_hash, full_token));
3118     }
3119     setup.input[pnr] = sii;
3120   }
3121
3122   /* system setup */
3123   syi = setup.system;
3124   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3125     setSetupInfo(system_setup_tokens, i,
3126                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3127   setup.system = syi;
3128
3129   /* options setup */
3130   soi = setup.options;
3131   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3132     setSetupInfo(options_setup_tokens, i,
3133                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3134   setup.options = soi;
3135 }
3136
3137 void LoadSetup()
3138 {
3139   char *filename = getSetupFilename();
3140   SetupFileHash *setup_file_hash = NULL;
3141
3142   /* always start with reliable default values */
3143   setSetupInfoToDefaults(&setup);
3144
3145   setup_file_hash = loadSetupFileHash(filename);
3146
3147   if (setup_file_hash)
3148   {
3149     char *player_name_new;
3150
3151     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3152     decodeSetupFileHash(setup_file_hash);
3153
3154     setup.direct_draw = !setup.double_buffering;
3155
3156     freeSetupFileHash(setup_file_hash);
3157
3158     /* needed to work around problems with fixed length strings */
3159     player_name_new = get_corrected_login_name(setup.player_name);
3160     free(setup.player_name);
3161     setup.player_name = player_name_new;
3162   }
3163   else
3164     Error(ERR_WARN, "using default setup values");
3165 }
3166
3167 void SaveSetup()
3168 {
3169   char *filename = getSetupFilename();
3170   FILE *file;
3171   int i, pnr;
3172
3173   InitUserDataDirectory();
3174
3175   if (!(file = fopen(filename, MODE_WRITE)))
3176   {
3177     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3178     return;
3179   }
3180
3181   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3182                                                getCookie("SETUP")));
3183   fprintf(file, "\n");
3184
3185   /* global setup */
3186   si = setup;
3187   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3188   {
3189     /* just to make things nicer :) */
3190     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3191         i == SETUP_TOKEN_GRAPHICS_SET)
3192       fprintf(file, "\n");
3193
3194     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3195   }
3196
3197   /* editor setup */
3198   sei = setup.editor;
3199   fprintf(file, "\n");
3200   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3201     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3202
3203   /* shortcut setup */
3204   ssi = setup.shortcut;
3205   fprintf(file, "\n");
3206   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3207     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3208
3209   /* player setup */
3210   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3211   {
3212     char prefix[30];
3213
3214     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3215     fprintf(file, "\n");
3216
3217     sii = setup.input[pnr];
3218     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3219       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3220   }
3221
3222   /* system setup */
3223   syi = setup.system;
3224   fprintf(file, "\n");
3225   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3226     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3227
3228   /* options setup */
3229   soi = setup.options;
3230   fprintf(file, "\n");
3231   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3232     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3233
3234   fclose(file);
3235
3236   SetFilePermissions(filename, PERMS_PRIVATE);
3237 }
3238
3239 void LoadCustomElementDescriptions()
3240 {
3241   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3242   SetupFileHash *setup_file_hash;
3243   int i;
3244
3245   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3246   {
3247     if (element_info[i].custom_description != NULL)
3248     {
3249       free(element_info[i].custom_description);
3250       element_info[i].custom_description = NULL;
3251     }
3252   }
3253
3254   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3255     return;
3256
3257   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3258   {
3259     char *token = getStringCat2(element_info[i].token_name, ".name");
3260     char *value = getHashEntry(setup_file_hash, token);
3261
3262     if (value != NULL)
3263       element_info[i].custom_description = getStringCopy(value);
3264
3265     free(token);
3266   }
3267
3268   freeSetupFileHash(setup_file_hash);
3269 }
3270
3271 void LoadSpecialMenuDesignSettings()
3272 {
3273   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3274   SetupFileHash *setup_file_hash;
3275   int i, j;
3276
3277   /* always start with reliable default values from default config */
3278   for (i = 0; image_config_vars[i].token != NULL; i++)
3279     for (j = 0; image_config[j].token != NULL; j++)
3280       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3281         *image_config_vars[i].value =
3282           get_auto_parameter_value(image_config_vars[i].token,
3283                                    image_config[j].value);
3284
3285   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3286     return;
3287
3288   /* special case: initialize with default values that may be overwritten */
3289   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3290   {
3291     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3292     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3293     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3294
3295     if (value_x != NULL)
3296       menu.draw_xoffset[i] = get_integer_from_string(value_x);
3297     if (value_y != NULL)
3298       menu.draw_yoffset[i] = get_integer_from_string(value_y);
3299     if (list_size != NULL)
3300       menu.list_size[i] = get_integer_from_string(list_size);
3301   }
3302
3303   /* read (and overwrite with) values that may be specified in config file */
3304   for (i = 0; image_config_vars[i].token != NULL; i++)
3305   {
3306     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3307
3308     if (value != NULL)
3309       *image_config_vars[i].value =
3310         get_auto_parameter_value(image_config_vars[i].token, value);
3311   }
3312
3313   freeSetupFileHash(setup_file_hash);
3314 }
3315
3316 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3317 {
3318   char *filename = getEditorSetupFilename();
3319   SetupFileList *setup_file_list, *list;
3320   SetupFileHash *element_hash;
3321   int num_unknown_tokens = 0;
3322   int i;
3323
3324   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3325     return;
3326
3327   element_hash = newSetupFileHash();
3328
3329   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3330     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3331
3332   /* determined size may be larger than needed (due to unknown elements) */
3333   *num_elements = 0;
3334   for (list = setup_file_list; list != NULL; list = list->next)
3335     (*num_elements)++;
3336
3337   /* add space for up to 3 more elements for padding that may be needed */
3338   *num_elements += 3;
3339
3340   *elements = checked_malloc(*num_elements * sizeof(int));
3341
3342   *num_elements = 0;
3343   for (list = setup_file_list; list != NULL; list = list->next)
3344   {
3345     char *value = getHashEntry(element_hash, list->token);
3346
3347     if (value)
3348     {
3349       (*elements)[(*num_elements)++] = atoi(value);
3350     }
3351     else
3352     {
3353       if (num_unknown_tokens == 0)
3354       {
3355         Error(ERR_RETURN_LINE, "-");
3356         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3357         Error(ERR_RETURN, "- config file: '%s'", filename);
3358
3359         num_unknown_tokens++;
3360       }
3361
3362       Error(ERR_RETURN, "- token: '%s'", list->token);
3363     }
3364   }
3365
3366   if (num_unknown_tokens > 0)
3367     Error(ERR_RETURN_LINE, "-");
3368
3369   while (*num_elements % 4)     /* pad with empty elements, if needed */
3370     (*elements)[(*num_elements)++] = EL_EMPTY;
3371
3372   freeSetupFileList(setup_file_list);
3373   freeSetupFileHash(element_hash);
3374
3375 #if 0
3376   /* TEST-ONLY */
3377   for (i = 0; i < *num_elements; i++)
3378     printf("editor: element '%s' [%d]\n",
3379            element_info[(*elements)[i]].token_name, (*elements)[i]);
3380 #endif
3381 }
3382
3383 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3384                                                      boolean is_sound)
3385 {
3386   SetupFileHash *setup_file_hash = NULL;
3387   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3388   char *filename_music, *filename_prefix, *filename_info;
3389   struct
3390   {
3391     char *token;
3392     char **value_ptr;
3393   }
3394   token_to_value_ptr[] =
3395   {
3396     { "title_header",   &tmp_music_file_info.title_header       },
3397     { "artist_header",  &tmp_music_file_info.artist_header      },
3398     { "album_header",   &tmp_music_file_info.album_header       },
3399     { "year_header",    &tmp_music_file_info.year_header        },
3400
3401     { "title",          &tmp_music_file_info.title              },
3402     { "artist",         &tmp_music_file_info.artist             },
3403     { "album",          &tmp_music_file_info.album              },
3404     { "year",           &tmp_music_file_info.year               },
3405
3406     { NULL,             NULL                                    },
3407   };
3408   int i;
3409
3410   filename_music = (is_sound ? getCustomSoundFilename(basename) :
3411                     getCustomMusicFilename(basename));
3412
3413   if (filename_music == NULL)
3414     return NULL;
3415
3416   /* ---------- try to replace file extension ---------- */
3417
3418   filename_prefix = getStringCopy(filename_music);
3419   if (strrchr(filename_prefix, '.') != NULL)
3420     *strrchr(filename_prefix, '.') = '\0';
3421   filename_info = getStringCat2(filename_prefix, ".txt");
3422
3423 #if 0
3424   printf("trying to load file '%s'...\n", filename_info);
3425 #endif
3426
3427   if (fileExists(filename_info))
3428     setup_file_hash = loadSetupFileHash(filename_info);
3429
3430   free(filename_prefix);
3431   free(filename_info);
3432
3433   if (setup_file_hash == NULL)
3434   {
3435     /* ---------- try to add file extension ---------- */
3436
3437     filename_prefix = getStringCopy(filename_music);
3438     filename_info = getStringCat2(filename_prefix, ".txt");
3439
3440 #if 0
3441     printf("trying to load file '%s'...\n", filename_info);
3442 #endif
3443
3444     if (fileExists(filename_info))
3445       setup_file_hash = loadSetupFileHash(filename_info);
3446
3447     free(filename_prefix);
3448     free(filename_info);
3449   }
3450
3451   if (setup_file_hash == NULL)
3452     return NULL;
3453
3454   /* ---------- music file info found ---------- */
3455
3456   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3457
3458   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3459   {
3460     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3461
3462     *token_to_value_ptr[i].value_ptr =
3463       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3464   }
3465
3466   tmp_music_file_info.basename = getStringCopy(basename);
3467   tmp_music_file_info.music = music;
3468   tmp_music_file_info.is_sound = is_sound;
3469
3470   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3471   *new_music_file_info = tmp_music_file_info;
3472
3473   return new_music_file_info;
3474 }
3475
3476 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3477 {
3478   return get_music_file_info_ext(basename, music, FALSE);
3479 }
3480
3481 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3482 {
3483   return get_music_file_info_ext(basename, sound, TRUE);
3484 }
3485
3486 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3487                                      char *basename, boolean is_sound)
3488 {
3489   for (; list != NULL; list = list->next)
3490     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3491       return TRUE;
3492
3493   return FALSE;
3494 }
3495
3496 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3497 {
3498   return music_info_listed_ext(list, basename, FALSE);
3499 }
3500
3501 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3502 {
3503   return music_info_listed_ext(list, basename, TRUE);
3504 }
3505
3506 void LoadMusicInfo()
3507 {
3508   char *music_directory = getCustomMusicDirectory();
3509   int num_music = getMusicListSize();
3510   int num_music_noconf = 0;
3511   int num_sounds = getSoundListSize();
3512   DIR *dir;
3513   struct dirent *dir_entry;
3514   struct FileInfo *music, *sound;
3515   struct MusicFileInfo *next, **new;
3516   int i;
3517
3518   while (music_file_info != NULL)
3519   {
3520     next = music_file_info->next;
3521
3522     checked_free(music_file_info->basename);
3523
3524     checked_free(music_file_info->title_header);
3525     checked_free(music_file_info->artist_header);
3526     checked_free(music_file_info->album_header);
3527     checked_free(music_file_info->year_header);
3528
3529     checked_free(music_file_info->title);
3530     checked_free(music_file_info->artist);
3531     checked_free(music_file_info->album);
3532     checked_free(music_file_info->year);
3533
3534     free(music_file_info);
3535
3536     music_file_info = next;
3537   }
3538
3539   new = &music_file_info;
3540
3541 #if 0
3542   printf("::: num_music == %d\n", num_music);
3543 #endif
3544
3545   for (i = 0; i < num_music; i++)
3546   {
3547     music = getMusicListEntry(i);
3548
3549 #if 0
3550     printf("::: %d [%08x]\n", i, music->filename);
3551 #endif
3552
3553     if (music->filename == NULL)
3554       continue;
3555
3556     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3557       continue;
3558
3559     /* a configured file may be not recognized as music */
3560     if (!FileIsMusic(music->filename))
3561       continue;
3562
3563 #if 0
3564     printf("::: -> '%s' (configured)\n", music->filename);
3565 #endif
3566
3567     if (!music_info_listed(music_file_info, music->filename))
3568     {
3569       *new = get_music_file_info(music->filename, i);
3570       if (*new != NULL)
3571         new = &(*new)->next;
3572     }
3573   }
3574
3575   if ((dir = opendir(music_directory)) == NULL)
3576   {
3577     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3578     return;
3579   }
3580
3581   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
3582   {
3583     char *basename = dir_entry->d_name;
3584     boolean music_already_used = FALSE;
3585     int i;
3586
3587     /* skip all music files that are configured in music config file */
3588     for (i = 0; i < num_music; i++)
3589     {
3590       music = getMusicListEntry(i);
3591
3592       if (music->filename == NULL)
3593         continue;
3594
3595       if (strcmp(basename, music->filename) == 0)
3596       {
3597         music_already_used = TRUE;
3598         break;
3599       }
3600     }
3601
3602     if (music_already_used)
3603       continue;
3604
3605     if (!FileIsMusic(basename))
3606       continue;
3607
3608 #if 0
3609     printf("::: -> '%s' (found in directory)\n", basename);
3610 #endif
3611
3612     if (!music_info_listed(music_file_info, basename))
3613     {
3614       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3615       if (*new != NULL)
3616         new = &(*new)->next;
3617     }
3618
3619     num_music_noconf++;
3620   }
3621
3622   closedir(dir);
3623
3624   for (i = 0; i < num_sounds; i++)
3625   {
3626     sound = getSoundListEntry(i);
3627
3628     if (sound->filename == NULL)
3629       continue;
3630
3631     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3632       continue;
3633
3634     /* a configured file may be not recognized as sound */
3635     if (!FileIsSound(sound->filename))
3636       continue;
3637
3638 #if 0
3639     printf("::: -> '%s' (configured)\n", sound->filename);
3640 #endif
3641
3642     if (!sound_info_listed(music_file_info, sound->filename))
3643     {
3644       *new = get_sound_file_info(sound->filename, i);
3645       if (*new != NULL)
3646         new = &(*new)->next;
3647     }
3648   }
3649
3650 #if 0
3651   /* TEST-ONLY */
3652   for (next = music_file_info; next != NULL; next = next->next)
3653     printf("::: title == '%s'\n", next->title);
3654 #endif
3655 }
3656
3657 void add_helpanim_entry(int element, int action, int direction, int delay,
3658                         int *num_list_entries)
3659 {
3660   struct HelpAnimInfo *new_list_entry;
3661   (*num_list_entries)++;
3662
3663   helpanim_info =
3664     checked_realloc(helpanim_info,
3665                     *num_list_entries * sizeof(struct HelpAnimInfo));
3666   new_list_entry = &helpanim_info[*num_list_entries - 1];
3667
3668   new_list_entry->element = element;
3669   new_list_entry->action = action;
3670   new_list_entry->direction = direction;
3671   new_list_entry->delay = delay;
3672 }
3673
3674 void print_unknown_token(char *filename, char *token, int token_nr)
3675 {
3676   if (token_nr == 0)
3677   {
3678     Error(ERR_RETURN_LINE, "-");
3679     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3680     Error(ERR_RETURN, "- config file: '%s'", filename);
3681   }
3682
3683   Error(ERR_RETURN, "- token: '%s'", token);
3684 }
3685
3686 void print_unknown_token_end(int token_nr)
3687 {
3688   if (token_nr > 0)
3689     Error(ERR_RETURN_LINE, "-");
3690 }
3691
3692 void LoadHelpAnimInfo()
3693 {
3694   char *filename = getHelpAnimFilename();
3695   SetupFileList *setup_file_list = NULL, *list;
3696   SetupFileHash *element_hash, *action_hash, *direction_hash;
3697   int num_list_entries = 0;
3698   int num_unknown_tokens = 0;
3699   int i;
3700
3701   if (fileExists(filename))
3702     setup_file_list = loadSetupFileList(filename);
3703
3704   if (setup_file_list == NULL)
3705   {
3706     /* use reliable default values from static configuration */
3707     SetupFileList *insert_ptr;
3708
3709     insert_ptr = setup_file_list =
3710       newSetupFileList(helpanim_config[0].token,
3711                        helpanim_config[0].value);
3712
3713     for (i = 1; helpanim_config[i].token; i++)
3714       insert_ptr = addListEntry(insert_ptr,
3715                                 helpanim_config[i].token,
3716                                 helpanim_config[i].value);
3717   }
3718
3719   element_hash   = newSetupFileHash();
3720   action_hash    = newSetupFileHash();
3721   direction_hash = newSetupFileHash();
3722
3723   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3724     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3725
3726   for (i = 0; i < NUM_ACTIONS; i++)
3727     setHashEntry(action_hash, element_action_info[i].suffix,
3728                  i_to_a(element_action_info[i].value));
3729
3730   /* do not store direction index (bit) here, but direction value! */
3731   for (i = 0; i < NUM_DIRECTIONS; i++)
3732     setHashEntry(direction_hash, element_direction_info[i].suffix,
3733                  i_to_a(1 << element_direction_info[i].value));
3734
3735   for (list = setup_file_list; list != NULL; list = list->next)
3736   {
3737     char *element_token, *action_token, *direction_token;
3738     char *element_value, *action_value, *direction_value;
3739     int delay = atoi(list->value);
3740
3741     if (strcmp(list->token, "end") == 0)
3742     {
3743       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3744
3745       continue;
3746     }
3747
3748     /* first try to break element into element/action/direction parts;
3749        if this does not work, also accept combined "element[.act][.dir]"
3750        elements (like "dynamite.active"), which are unique elements */
3751
3752     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
3753     {
3754       element_value = getHashEntry(element_hash, list->token);
3755       if (element_value != NULL)        /* element found */
3756         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3757                            &num_list_entries);
3758       else
3759       {
3760         /* no further suffixes found -- this is not an element */
3761         print_unknown_token(filename, list->token, num_unknown_tokens++);
3762       }
3763
3764       continue;
3765     }
3766
3767     /* token has format "<prefix>.<something>" */
3768
3769     action_token = strchr(list->token, '.');    /* suffix may be action ... */
3770     direction_token = action_token;             /* ... or direction */
3771
3772     element_token = getStringCopy(list->token);
3773     *strchr(element_token, '.') = '\0';
3774
3775     element_value = getHashEntry(element_hash, element_token);
3776
3777     if (element_value == NULL)          /* this is no element */
3778     {
3779       element_value = getHashEntry(element_hash, list->token);
3780       if (element_value != NULL)        /* combined element found */
3781         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3782                            &num_list_entries);
3783       else
3784         print_unknown_token(filename, list->token, num_unknown_tokens++);
3785
3786       free(element_token);
3787
3788       continue;
3789     }
3790
3791     action_value = getHashEntry(action_hash, action_token);
3792
3793     if (action_value != NULL)           /* action found */
3794     {
3795       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3796                     &num_list_entries);
3797
3798       free(element_token);
3799
3800       continue;
3801     }
3802
3803     direction_value = getHashEntry(direction_hash, direction_token);
3804
3805     if (direction_value != NULL)        /* direction found */
3806     {
3807       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3808                          &num_list_entries);
3809
3810       free(element_token);
3811
3812       continue;
3813     }
3814
3815     if (strchr(action_token + 1, '.') == NULL)
3816     {
3817       /* no further suffixes found -- this is not an action nor direction */
3818
3819       element_value = getHashEntry(element_hash, list->token);
3820       if (element_value != NULL)        /* combined element found */
3821         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3822                            &num_list_entries);
3823       else
3824         print_unknown_token(filename, list->token, num_unknown_tokens++);
3825
3826       free(element_token);
3827
3828       continue;
3829     }
3830
3831     /* token has format "<prefix>.<suffix>.<something>" */
3832
3833     direction_token = strchr(action_token + 1, '.');
3834
3835     action_token = getStringCopy(action_token);
3836     *strchr(action_token + 1, '.') = '\0';
3837
3838     action_value = getHashEntry(action_hash, action_token);
3839
3840     if (action_value == NULL)           /* this is no action */
3841     {
3842       element_value = getHashEntry(element_hash, list->token);
3843       if (element_value != NULL)        /* combined element found */
3844         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3845                            &num_list_entries);
3846       else
3847         print_unknown_token(filename, list->token, num_unknown_tokens++);
3848
3849       free(element_token);
3850       free(action_token);
3851
3852       continue;
3853     }
3854
3855     direction_value = getHashEntry(direction_hash, direction_token);
3856
3857     if (direction_value != NULL)        /* direction found */
3858     {
3859       add_helpanim_entry(atoi(element_value), atoi(action_value),
3860                          atoi(direction_value), delay, &num_list_entries);
3861
3862       free(element_token);
3863       free(action_token);
3864
3865       continue;
3866     }
3867
3868     /* this is no direction */
3869
3870     element_value = getHashEntry(element_hash, list->token);
3871     if (element_value != NULL)          /* combined element found */
3872       add_helpanim_entry(atoi(element_value), -1, -1, delay,
3873                          &num_list_entries);
3874     else
3875       print_unknown_token(filename, list->token, num_unknown_tokens++);
3876
3877     free(element_token);
3878     free(action_token);
3879   }
3880
3881   print_unknown_token_end(num_unknown_tokens);
3882
3883   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3884   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
3885
3886   freeSetupFileList(setup_file_list);
3887   freeSetupFileHash(element_hash);
3888   freeSetupFileHash(action_hash);
3889   freeSetupFileHash(direction_hash);
3890
3891 #if 0
3892   /* TEST ONLY */
3893   for (i = 0; i < num_list_entries; i++)
3894     printf("::: %d, %d, %d => %d\n",
3895            helpanim_info[i].element,
3896            helpanim_info[i].action,
3897            helpanim_info[i].direction,
3898            helpanim_info[i].delay);
3899 #endif
3900 }
3901
3902 void LoadHelpTextInfo()
3903 {
3904   char *filename = getHelpTextFilename();
3905   int i;
3906
3907   if (helptext_info != NULL)
3908   {
3909     freeSetupFileHash(helptext_info);
3910     helptext_info = NULL;
3911   }
3912
3913   if (fileExists(filename))
3914     helptext_info = loadSetupFileHash(filename);
3915
3916   if (helptext_info == NULL)
3917   {
3918     /* use reliable default values from static configuration */
3919     helptext_info = newSetupFileHash();
3920
3921     for (i = 0; helptext_config[i].token; i++)
3922       setHashEntry(helptext_info,
3923                    helptext_config[i].token,
3924                    helptext_config[i].value);
3925   }
3926
3927 #if 0
3928   /* TEST ONLY */
3929   BEGIN_HASH_ITERATION(helptext_info, itr)
3930   {
3931     printf("::: '%s' => '%s'\n",
3932            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3933   }
3934   END_HASH_ITERATION(hash, itr)
3935 #endif
3936 }