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