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