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