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