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