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