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