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