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