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