601c74cf8e9f3814e0d20f82cb776855fb8e8daa
[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     0       /* 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
53 /* ========================================================================= */
54 /* level file functions                                                      */
55 /* ========================================================================= */
56
57 void setElementChangePages(struct ElementInfo *ei, int change_pages)
58 {
59   int change_page_size = sizeof(struct ElementChangeInfo);
60
61   ei->num_change_pages = MAX(1, change_pages);
62
63   ei->change_page =
64     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
65
66   if (ei->current_change_page >= ei->num_change_pages)
67     ei->current_change_page = ei->num_change_pages - 1;
68
69   ei->change = &ei->change_page[ei->current_change_page];
70 }
71
72 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
73 {
74   int x, y;
75
76   change->can_change = FALSE;
77
78   change->events = CE_BITMASK_DEFAULT;
79
80   change->trigger_player = CH_PLAYER_ANY;
81   change->trigger_side = CH_SIDE_ANY;
82   change->trigger_page = CH_PAGE_ANY;
83
84   change->target_element = EL_EMPTY_SPACE;
85
86   change->delay_fixed = 0;
87   change->delay_random = 0;
88   change->delay_frames = 1;
89
90   change->trigger_element = EL_EMPTY_SPACE;
91
92   change->explode = FALSE;
93   change->use_target_content = FALSE;
94   change->only_if_complete = FALSE;
95   change->use_random_replace = FALSE;
96   change->random_percentage = 100;
97   change->replace_when = CP_WHEN_EMPTY;
98
99   for (x = 0; x < 3; x++)
100     for (y = 0; y < 3; y++)
101       change->target_content[x][y] = EL_EMPTY_SPACE;
102
103   change->direct_action = 0;
104   change->other_action = 0;
105
106   change->pre_change_function = NULL;
107   change->change_function = NULL;
108   change->post_change_function = NULL;
109 }
110
111 static void setLevelInfoToDefaults(struct LevelInfo *level)
112 {
113   static boolean clipboard_elements_initialized = FALSE;
114
115   int i, j, x, y;
116
117   level->native_em_level = &native_em_level;
118
119   level->game_engine_type = GAME_ENGINE_TYPE_RND;
120
121   level->file_version = FILE_VERSION_ACTUAL;
122   level->game_version = GAME_VERSION_ACTUAL;
123
124   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
125   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
126   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
127
128   level->fieldx = STD_LEV_FIELDX;
129   level->fieldy = STD_LEV_FIELDY;
130
131   for (x = 0; x < MAX_LEV_FIELDX; x++)
132     for (y = 0; y < MAX_LEV_FIELDY; y++)
133       level->field[x][y] = EL_SAND;
134
135   level->time = 100;
136   level->gems_needed = 0;
137
138   level->amoeba_speed = 10;
139
140   level->time_magic_wall = 10;
141   level->time_wheel = 10;
142   level->time_light = 10;
143   level->time_timegate = 10;
144
145   level->amoeba_content = EL_DIAMOND;
146
147   level->double_speed = FALSE;
148   level->initial_gravity = FALSE;
149   level->em_slippery_gems = FALSE;
150   level->instant_relocation = FALSE;
151   level->can_pass_to_walkable = FALSE;
152   level->grow_into_diggable = TRUE;
153
154   level->block_last_field = FALSE;      /* EM does not block by default */
155   level->sp_block_last_field = TRUE;    /* SP blocks the last field */
156   level->block_delay = 8;               /* when blocking, block 8 frames */
157   level->sp_block_delay = 9;            /* SP indeed blocks 9 frames, not 8 */
158
159   level->can_move_into_acid_bits = ~0;  /* everything can move into acid */
160   level->dont_collide_with_bits = ~0;   /* always deadly when colliding */
161
162   level->use_spring_bug = FALSE;
163   level->use_step_counter = FALSE;
164
165   level->use_custom_template = FALSE;
166
167   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
168     level->name[i] = '\0';
169   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
170     level->author[i] = '\0';
171
172   strcpy(level->name, NAMELESS_LEVEL_NAME);
173   strcpy(level->author, ANONYMOUS_NAME);
174
175   for (i = 0; i < 4; i++)
176   {
177     level->envelope_text[i][0] = '\0';
178     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
179     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
180   }
181
182   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
183     level->score[i] = 10;
184
185   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
186   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
187     for (x = 0; x < 3; x++)
188       for (y = 0; y < 3; y++)
189         level->yamyam_content[i][x][y] =
190           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
191
192   level->field[0][0] = EL_PLAYER_1;
193   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
194
195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
196   {
197     int element = i;
198
199     /* never initialize clipboard elements after the very first time */
200     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
201       continue;
202
203     setElementChangePages(&element_info[element], 1);
204     setElementChangeInfoToDefaults(element_info[element].change);
205
206     if (IS_CUSTOM_ELEMENT(element) ||
207         IS_GROUP_ELEMENT(element) ||
208         IS_INTERNAL_ELEMENT(element))
209     {
210       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
211         element_info[element].description[j] = '\0';
212
213       if (element_info[element].custom_description != NULL)
214         strncpy(element_info[element].description,
215                 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
216       else
217         strcpy(element_info[element].description,
218                element_info[element].editor_description);
219
220       element_info[element].use_gfx_element = FALSE;
221       element_info[element].gfx_element = EL_EMPTY_SPACE;
222
223       element_info[element].modified_settings = FALSE;
224     }
225
226     if (IS_CUSTOM_ELEMENT(element) ||
227         IS_INTERNAL_ELEMENT(element))
228     {
229       element_info[element].access_direction = MV_ALL_DIRECTIONS;
230
231       element_info[element].collect_score = 10;         /* special default */
232       element_info[element].collect_count = 1;          /* special default */
233
234       element_info[element].push_delay_fixed = -1;      /* initialize later */
235       element_info[element].push_delay_random = -1;     /* initialize later */
236       element_info[element].drop_delay_fixed = 0;
237       element_info[element].drop_delay_random = 0;
238       element_info[element].move_delay_fixed = 0;
239       element_info[element].move_delay_random = 0;
240
241       element_info[element].move_pattern = MV_ALL_DIRECTIONS;
242       element_info[element].move_direction_initial = MV_START_AUTOMATIC;
243       element_info[element].move_stepsize = TILEX / 8;
244
245       element_info[element].move_enter_element = EL_EMPTY_SPACE;
246       element_info[element].move_leave_element = EL_EMPTY_SPACE;
247       element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
248
249       element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
250
251       element_info[element].explosion_type = EXPLODES_3X3;
252       element_info[element].explosion_delay = 16;
253       element_info[element].ignition_delay = 8;
254
255       for (x = 0; x < 3; x++)
256         for (y = 0; y < 3; y++)
257           element_info[element].content[x][y] = EL_EMPTY_SPACE;
258
259       element_info[element].access_type = 0;
260       element_info[element].access_layer = 0;
261       element_info[element].access_protected = 0;
262       element_info[element].walk_to_action = 0;
263       element_info[element].smash_targets = 0;
264       element_info[element].deadliness = 0;
265
266       element_info[element].can_explode_by_fire = FALSE;
267       element_info[element].can_explode_smashed = FALSE;
268       element_info[element].can_explode_impact = FALSE;
269
270       element_info[element].current_change_page = 0;
271
272       /* start with no properties at all */
273       for (j = 0; j < NUM_EP_BITFIELDS; j++)
274         Properties[element][j] = EP_BITMASK_DEFAULT;
275
276       /* now set default properties */
277       SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
278     }
279
280     if (IS_GROUP_ELEMENT(element) ||
281         IS_INTERNAL_ELEMENT(element))
282     {
283       /* initialize memory for list of elements in group */
284       if (element_info[element].group == NULL)
285         element_info[element].group =
286           checked_malloc(sizeof(struct ElementGroupInfo));
287
288       for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
289         element_info[element].group->element[j] = EL_EMPTY_SPACE;
290
291       /* default: only one element in group */
292       element_info[element].group->num_elements = 1;
293
294       element_info[element].group->choice_mode = ANIM_RANDOM;
295     }
296   }
297
298   clipboard_elements_initialized = TRUE;
299
300   BorderElement = EL_STEELWALL;
301
302   level->no_valid_file = FALSE;
303
304   level->changed = FALSE;
305
306   if (leveldir_current == NULL)         /* only when dumping level */
307     return;
308
309   /* try to determine better author name than 'anonymous' */
310   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
311   {
312     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
313     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
314   }
315   else
316   {
317     switch (LEVELCLASS(leveldir_current))
318     {
319       case LEVELCLASS_TUTORIAL:
320         strcpy(level->author, PROGRAM_AUTHOR_STRING);
321         break;
322
323       case LEVELCLASS_CONTRIB:
324         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
325         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
326         break;
327
328       case LEVELCLASS_PRIVATE:
329         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
330         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
331         break;
332
333       default:
334         /* keep default value */
335         break;
336     }
337   }
338 }
339
340 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
341 {
342   level_file_info->nr = 0;
343   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
344   level_file_info->packed = FALSE;
345   level_file_info->basename = NULL;
346   level_file_info->filename = NULL;
347 }
348
349 static void ActivateLevelTemplate()
350 {
351   /* Currently there is no special action needed to activate the template
352      data, because 'element_info' and 'Properties' overwrite the original
353      level data, while all other variables do not change. */
354 }
355
356 static char *getLevelFilenameFromBasename(char *basename)
357 {
358   static char *filename = NULL;
359
360   checked_free(filename);
361
362   filename = getPath2(getCurrentLevelDir(), basename);
363
364   return filename;
365 }
366
367 static int getFileTypeFromBasename(char *basename)
368 {
369   static char *filename = NULL;
370   struct stat file_status;
371
372   /* ---------- try to determine file type from filename ---------- */
373
374   /* check for typical filename of a Supaplex level package file */
375   if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
376                                  strncmp(basename, "LEVELS.D", 8) == 0))
377     return LEVEL_FILE_TYPE_SP;
378
379   /* ---------- try to determine file type from filesize ---------- */
380
381   checked_free(filename);
382   filename = getPath2(getCurrentLevelDir(), basename);
383
384   if (stat(filename, &file_status) == 0)
385   {
386     /* check for typical filesize of a Supaplex level package file */
387     if (file_status.st_size == 170496)
388       return LEVEL_FILE_TYPE_SP;
389   }
390
391   return LEVEL_FILE_TYPE_UNKNOWN;
392 }
393
394 static char *getSingleLevelBasename(int nr, int type)
395 {
396   static char basename[MAX_FILENAME_LEN];
397   char *level_filename = getStringCopy(leveldir_current->level_filename);
398
399   if (level_filename == NULL)
400     level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
401
402   switch (type)
403   {
404     case LEVEL_FILE_TYPE_RND:
405       if (nr < 0)
406         sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
407       else
408         sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
409       break;
410
411     case LEVEL_FILE_TYPE_EM:
412       sprintf(basename, "%d", nr);
413       break;
414
415     case LEVEL_FILE_TYPE_UNKNOWN:
416     default:
417       sprintf(basename, level_filename, nr);
418       break;
419   }
420
421   free(level_filename);
422
423   return basename;
424 }
425
426 static char *getPackedLevelBasename(int type)
427 {
428   static char basename[MAX_FILENAME_LEN];
429   char *directory = getCurrentLevelDir();
430   DIR *dir;
431   struct dirent *dir_entry;
432
433   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
434
435   if ((dir = opendir(directory)) == NULL)
436   {
437     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
438
439     return basename;
440   }
441
442   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
443   {
444     char *entry_basename = dir_entry->d_name;
445     int entry_type = getFileTypeFromBasename(entry_basename);
446
447     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
448     {
449       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
450           type == entry_type)
451       {
452         strcpy(basename, entry_basename);
453
454         break;
455       }
456     }
457   }
458
459   closedir(dir);
460
461   return basename;
462 }
463
464 static char *getSingleLevelFilename(int nr, int type)
465 {
466   return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
467 }
468
469 #if 0
470 static char *getPackedLevelFilename(int type)
471 {
472   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
473 }
474 #endif
475
476 char *getDefaultLevelFilename(int nr)
477 {
478   return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
479 }
480
481 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
482                                                  int type)
483 {
484   lfi->type = type;
485   lfi->packed = FALSE;
486   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
487   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
488 }
489
490 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
491                                                  int type)
492 {
493   lfi->type = type;
494   lfi->packed = TRUE;
495   lfi->basename = getPackedLevelBasename(lfi->type);
496   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
497 }
498
499 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
500 {
501   /* special case: level number is negative => check for level template file */
502   if (lfi->nr < 0)
503   {
504     setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
505
506     return;
507   }
508
509   if (leveldir_current->level_filename != NULL)
510   {
511     /* check for file name/pattern specified in "levelinfo.conf" */
512     setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
513     if (fileExists(lfi->filename))
514       return;
515   }
516
517   /* check for native Rocks'n'Diamonds level file */
518   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
519   if (fileExists(lfi->filename))
520     return;
521
522   /* check for classic Emerald Mine level file */
523   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
524   if (fileExists(lfi->filename))
525     return;
526
527   /* check for various packed level file formats */
528   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
529   if (fileExists(lfi->filename))
530     return;
531
532   /* no known level file found -- try to use default values */
533   setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
534 }
535
536 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
537 {
538   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
539     lfi->type = getFileTypeFromBasename(lfi->basename);
540 }
541
542 #if 1
543 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
544 {
545   /* always start with reliable default values */
546   setFileInfoToDefaults(level_file_info);
547
548   level_file_info->nr = nr;     /* set requested level number */
549
550   determineLevelFileInfo_Filename(level_file_info);
551   determineLevelFileInfo_Filetype(level_file_info);
552 }
553
554 #else
555
556 static struct LevelFileInfo *getLevelFileInfo(int nr)
557 {
558   static struct LevelFileInfo level_file_info;
559
560   /* always start with reliable default values */
561   setFileInfoToDefaults(&level_file_info);
562
563   level_file_info.nr = nr;      /* set requested level number */
564
565   determineLevelFileInfo_Filename(&level_file_info);
566   determineLevelFileInfo_Filetype(&level_file_info);
567
568   return &level_file_info;
569 }
570 #endif
571
572 /* ------------------------------------------------------------------------- */
573 /* functions for loading R'n'D level                                         */
574 /* ------------------------------------------------------------------------- */
575
576 int getMappedElement(int element)
577 {
578   /* remap some (historic, now obsolete) elements */
579
580 #if 1
581   switch (element)
582   {
583     case EL_PLAYER_OBSOLETE:
584       element = EL_PLAYER_1;
585       break;
586
587     case EL_KEY_OBSOLETE:
588       element = EL_KEY_1;
589
590     case EL_EM_KEY_1_FILE_OBSOLETE:
591       element = EL_EM_KEY_1;
592       break;
593
594     case EL_EM_KEY_2_FILE_OBSOLETE:
595       element = EL_EM_KEY_2;
596       break;
597
598     case EL_EM_KEY_3_FILE_OBSOLETE:
599       element = EL_EM_KEY_3;
600       break;
601
602     case EL_EM_KEY_4_FILE_OBSOLETE:
603       element = EL_EM_KEY_4;
604       break;
605
606     case EL_ENVELOPE_OBSOLETE:
607       element = EL_ENVELOPE_1;
608       break;
609
610     case EL_SP_EMPTY:
611       element = EL_EMPTY;
612       break;
613
614     default:
615       if (element >= NUM_FILE_ELEMENTS)
616       {
617         Error(ERR_WARN, "invalid level element %d", element);
618
619         element = EL_UNKNOWN;
620       }
621       break;
622   }
623 #else
624   if (element >= NUM_FILE_ELEMENTS)
625   {
626     Error(ERR_WARN, "invalid level element %d", element);
627
628     element = EL_UNKNOWN;
629   }
630   else if (element == EL_PLAYER_OBSOLETE)
631     element = EL_PLAYER_1;
632   else if (element == EL_KEY_OBSOLETE)
633     element = EL_KEY_1;
634 #endif
635
636   return element;
637 }
638
639 int getMappedElementByVersion(int element, int game_version)
640 {
641   /* remap some elements due to certain game version */
642
643   if (game_version <= VERSION_IDENT(2,2,0,0))
644   {
645     /* map game font elements */
646     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
647                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
648                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
649                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
650   }
651
652   if (game_version < VERSION_IDENT(3,0,0,0))
653   {
654     /* map Supaplex gravity tube elements */
655     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
656                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
657                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
658                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
659                element);
660   }
661
662   return element;
663 }
664
665 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
666 {
667   level->file_version = getFileVersion(file);
668   level->game_version = getFileVersion(file);
669
670   return chunk_size;
671 }
672
673 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
674 {
675   int i, x, y;
676
677   level->fieldx = getFile8Bit(file);
678   level->fieldy = getFile8Bit(file);
679
680   level->time           = getFile16BitBE(file);
681   level->gems_needed    = getFile16BitBE(file);
682
683   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
684     level->name[i] = getFile8Bit(file);
685   level->name[MAX_LEVEL_NAME_LEN] = 0;
686
687   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
688     level->score[i] = getFile8Bit(file);
689
690   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
691   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
692     for (y = 0; y < 3; y++)
693       for (x = 0; x < 3; x++)
694         level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
695
696   level->amoeba_speed           = getFile8Bit(file);
697   level->time_magic_wall        = getFile8Bit(file);
698   level->time_wheel             = getFile8Bit(file);
699   level->amoeba_content         = getMappedElement(getFile8Bit(file));
700   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
701   level->initial_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
702   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
703   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
704
705   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
706
707   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
708   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
709   level->can_move_into_acid_bits = getFile32BitBE(file);
710   level->dont_collide_with_bits = getFile8Bit(file);
711
712   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
713   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
714
715   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
716   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
717   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
718
719   level->game_engine_type       = getFile8Bit(file);
720
721   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
722
723   return chunk_size;
724 }
725
726 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
727 {
728   int i;
729
730   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
731     level->author[i] = getFile8Bit(file);
732   level->author[MAX_LEVEL_NAME_LEN] = 0;
733
734   return chunk_size;
735 }
736
737 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
738 {
739   int x, y;
740   int chunk_size_expected = level->fieldx * level->fieldy;
741
742   /* Note: "chunk_size" was wrong before version 2.0 when elements are
743      stored with 16-bit encoding (and should be twice as big then).
744      Even worse, playfield data was stored 16-bit when only yamyam content
745      contained 16-bit elements and vice versa. */
746
747   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
748     chunk_size_expected *= 2;
749
750   if (chunk_size_expected != chunk_size)
751   {
752     ReadUnusedBytesFromFile(file, chunk_size);
753     return chunk_size_expected;
754   }
755
756   for (y = 0; y < level->fieldy; y++)
757     for (x = 0; x < level->fieldx; x++)
758       level->field[x][y] =
759         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
760                          getFile8Bit(file));
761   return chunk_size;
762 }
763
764 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
765 {
766   int i, x, y;
767   int header_size = 4;
768   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
769   int chunk_size_expected = header_size + content_size;
770
771   /* Note: "chunk_size" was wrong before version 2.0 when elements are
772      stored with 16-bit encoding (and should be twice as big then).
773      Even worse, playfield data was stored 16-bit when only yamyam content
774      contained 16-bit elements and vice versa. */
775
776   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
777     chunk_size_expected += content_size;
778
779   if (chunk_size_expected != chunk_size)
780   {
781     ReadUnusedBytesFromFile(file, chunk_size);
782     return chunk_size_expected;
783   }
784
785   getFile8Bit(file);
786   level->num_yamyam_contents = getFile8Bit(file);
787   getFile8Bit(file);
788   getFile8Bit(file);
789
790   /* correct invalid number of content fields -- should never happen */
791   if (level->num_yamyam_contents < 1 ||
792       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
793     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
794
795   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
796     for (y = 0; y < 3; y++)
797       for (x = 0; x < 3; x++)
798         level->yamyam_content[i][x][y] =
799           getMappedElement(level->encoding_16bit_field ?
800                            getFile16BitBE(file) : getFile8Bit(file));
801   return chunk_size;
802 }
803
804 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
805 {
806   int i, x, y;
807   int element;
808   int num_contents, content_xsize, content_ysize;
809   int content_array[MAX_ELEMENT_CONTENTS][3][3];
810
811   element = getMappedElement(getFile16BitBE(file));
812   num_contents = getFile8Bit(file);
813   content_xsize = getFile8Bit(file);
814   content_ysize = getFile8Bit(file);
815
816   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
817
818   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
819     for (y = 0; y < 3; y++)
820       for (x = 0; x < 3; x++)
821         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
822
823   /* correct invalid number of content fields -- should never happen */
824   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
825     num_contents = STD_ELEMENT_CONTENTS;
826
827   if (element == EL_YAMYAM)
828   {
829     level->num_yamyam_contents = num_contents;
830
831     for (i = 0; i < num_contents; i++)
832       for (y = 0; y < 3; y++)
833         for (x = 0; x < 3; x++)
834           level->yamyam_content[i][x][y] = content_array[i][x][y];
835   }
836   else if (element == EL_BD_AMOEBA)
837   {
838     level->amoeba_content = content_array[0][0][0];
839   }
840   else
841   {
842     Error(ERR_WARN, "cannot load content for element '%d'", element);
843   }
844
845   return chunk_size;
846 }
847
848 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
849 {
850   int i;
851   int element;
852   int envelope_nr;
853   int envelope_len;
854   int chunk_size_expected;
855
856   element = getMappedElement(getFile16BitBE(file));
857   if (!IS_ENVELOPE(element))
858     element = EL_ENVELOPE_1;
859
860   envelope_nr = element - EL_ENVELOPE_1;
861
862   envelope_len = getFile16BitBE(file);
863
864   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
865   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
866
867   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
868
869   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
870   if (chunk_size_expected != chunk_size)
871   {
872     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
873     return chunk_size_expected;
874   }
875
876   for (i = 0; i < envelope_len; i++)
877     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
878
879   return chunk_size;
880 }
881
882 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
883 {
884   int num_changed_custom_elements = getFile16BitBE(file);
885   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
886   int i;
887
888   if (chunk_size_expected != chunk_size)
889   {
890     ReadUnusedBytesFromFile(file, chunk_size - 2);
891     return chunk_size_expected;
892   }
893
894   for (i = 0; i < num_changed_custom_elements; i++)
895   {
896     int element = getFile16BitBE(file);
897     int properties = getFile32BitBE(file);
898
899     if (IS_CUSTOM_ELEMENT(element))
900       Properties[element][EP_BITFIELD_BASE] = properties;
901     else
902       Error(ERR_WARN, "invalid custom element number %d", element);
903   }
904
905   return chunk_size;
906 }
907
908 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
909 {
910   int num_changed_custom_elements = getFile16BitBE(file);
911   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
912   int i;
913
914   if (chunk_size_expected != chunk_size)
915   {
916     ReadUnusedBytesFromFile(file, chunk_size - 2);
917     return chunk_size_expected;
918   }
919
920   for (i = 0; i < num_changed_custom_elements; i++)
921   {
922     int element = getFile16BitBE(file);
923     int custom_target_element = getFile16BitBE(file);
924
925     if (IS_CUSTOM_ELEMENT(element))
926       element_info[element].change->target_element = custom_target_element;
927     else
928       Error(ERR_WARN, "invalid custom element number %d", element);
929   }
930
931   return chunk_size;
932 }
933
934 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
935 {
936   int num_changed_custom_elements = getFile16BitBE(file);
937   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
938   int i, j, x, y;
939
940   if (chunk_size_expected != chunk_size)
941   {
942     ReadUnusedBytesFromFile(file, chunk_size - 2);
943     return chunk_size_expected;
944   }
945
946   for (i = 0; i < num_changed_custom_elements; i++)
947   {
948     int element = getFile16BitBE(file);
949
950     if (!IS_CUSTOM_ELEMENT(element))
951     {
952       Error(ERR_WARN, "invalid custom element number %d", element);
953
954       element = EL_INTERNAL_DUMMY;
955     }
956
957     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
958       element_info[element].description[j] = getFile8Bit(file);
959     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
960
961     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
962
963     /* some free bytes for future properties and padding */
964     ReadUnusedBytesFromFile(file, 7);
965
966     element_info[element].use_gfx_element = getFile8Bit(file);
967     element_info[element].gfx_element =
968       getMappedElement(getFile16BitBE(file));
969
970     element_info[element].collect_score = getFile8Bit(file);
971     element_info[element].collect_count = getFile8Bit(file);
972
973     element_info[element].push_delay_fixed = getFile16BitBE(file);
974     element_info[element].push_delay_random = getFile16BitBE(file);
975     element_info[element].move_delay_fixed = getFile16BitBE(file);
976     element_info[element].move_delay_random = getFile16BitBE(file);
977
978     element_info[element].move_pattern = getFile16BitBE(file);
979     element_info[element].move_direction_initial = getFile8Bit(file);
980     element_info[element].move_stepsize = getFile8Bit(file);
981
982     for (y = 0; y < 3; y++)
983       for (x = 0; x < 3; x++)
984         element_info[element].content[x][y] =
985           getMappedElement(getFile16BitBE(file));
986
987     element_info[element].change->events = getFile32BitBE(file);
988
989     element_info[element].change->target_element =
990       getMappedElement(getFile16BitBE(file));
991
992     element_info[element].change->delay_fixed = getFile16BitBE(file);
993     element_info[element].change->delay_random = getFile16BitBE(file);
994     element_info[element].change->delay_frames = getFile16BitBE(file);
995
996     element_info[element].change->trigger_element =
997       getMappedElement(getFile16BitBE(file));
998
999     element_info[element].change->explode = getFile8Bit(file);
1000     element_info[element].change->use_target_content = getFile8Bit(file);
1001     element_info[element].change->only_if_complete = getFile8Bit(file);
1002     element_info[element].change->use_random_replace = getFile8Bit(file);
1003
1004     element_info[element].change->random_percentage = getFile8Bit(file);
1005     element_info[element].change->replace_when = getFile8Bit(file);
1006
1007     for (y = 0; y < 3; y++)
1008       for (x = 0; x < 3; x++)
1009         element_info[element].change->target_content[x][y] =
1010           getMappedElement(getFile16BitBE(file));
1011
1012     element_info[element].slippery_type = getFile8Bit(file);
1013
1014     /* some free bytes for future properties and padding */
1015     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1016
1017     /* mark that this custom element has been modified */
1018     element_info[element].modified_settings = TRUE;
1019   }
1020
1021   return chunk_size;
1022 }
1023
1024 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1025 {
1026   struct ElementInfo *ei;
1027   int chunk_size_expected;
1028   int element;
1029   int i, x, y;
1030
1031   element = getFile16BitBE(file);
1032
1033   if (!IS_CUSTOM_ELEMENT(element))
1034   {
1035     Error(ERR_WARN, "invalid custom element number %d", element);
1036
1037     ReadUnusedBytesFromFile(file, chunk_size - 2);
1038     return chunk_size;
1039   }
1040
1041   ei = &element_info[element];
1042
1043   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1044     ei->description[i] = getFile8Bit(file);
1045   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1046
1047   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1048   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
1049
1050   ei->num_change_pages = getFile8Bit(file);
1051
1052   /* some free bytes for future base property values and padding */
1053   ReadUnusedBytesFromFile(file, 5);
1054
1055   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1056   if (chunk_size_expected != chunk_size)
1057   {
1058     ReadUnusedBytesFromFile(file, chunk_size - 48);
1059     return chunk_size_expected;
1060   }
1061
1062   /* read custom property values */
1063
1064   ei->use_gfx_element = getFile8Bit(file);
1065   ei->gfx_element = getMappedElement(getFile16BitBE(file));
1066
1067   ei->collect_score = getFile8Bit(file);
1068   ei->collect_count = getFile8Bit(file);
1069
1070   ei->drop_delay_fixed = getFile8Bit(file);
1071   ei->push_delay_fixed = getFile8Bit(file);
1072   ei->drop_delay_random = getFile8Bit(file);
1073   ei->push_delay_random = getFile8Bit(file);
1074   ei->move_delay_fixed = getFile16BitBE(file);
1075   ei->move_delay_random = getFile16BitBE(file);
1076
1077   /* bits 0 - 15 of "move_pattern" ... */
1078   ei->move_pattern = getFile16BitBE(file);
1079   ei->move_direction_initial = getFile8Bit(file);
1080   ei->move_stepsize = getFile8Bit(file);
1081
1082   ei->slippery_type = getFile8Bit(file);
1083
1084   for (y = 0; y < 3; y++)
1085     for (x = 0; x < 3; x++)
1086       ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1087
1088   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1089   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1090   ei->move_leave_type = getFile8Bit(file);
1091
1092   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1093   ei->move_pattern |= (getFile16BitBE(file) << 16);
1094
1095   ei->access_direction = getFile8Bit(file);
1096
1097   ei->explosion_delay = getFile8Bit(file);
1098   ei->ignition_delay = getFile8Bit(file);
1099   ei->explosion_type = getFile8Bit(file);
1100
1101   /* some free bytes for future custom property values and padding */
1102   ReadUnusedBytesFromFile(file, 1);
1103
1104   /* read change property values */
1105
1106   setElementChangePages(ei, ei->num_change_pages);
1107
1108   for (i = 0; i < ei->num_change_pages; i++)
1109   {
1110     struct ElementChangeInfo *change = &ei->change_page[i];
1111
1112     /* always start with reliable default values */
1113     setElementChangeInfoToDefaults(change);
1114
1115     change->events = getFile32BitBE(file);
1116
1117     change->target_element = getMappedElement(getFile16BitBE(file));
1118
1119     change->delay_fixed = getFile16BitBE(file);
1120     change->delay_random = getFile16BitBE(file);
1121     change->delay_frames = getFile16BitBE(file);
1122
1123     change->trigger_element = getMappedElement(getFile16BitBE(file));
1124
1125     change->explode = getFile8Bit(file);
1126     change->use_target_content = getFile8Bit(file);
1127     change->only_if_complete = getFile8Bit(file);
1128     change->use_random_replace = getFile8Bit(file);
1129
1130     change->random_percentage = getFile8Bit(file);
1131     change->replace_when = getFile8Bit(file);
1132
1133     for (y = 0; y < 3; y++)
1134       for (x = 0; x < 3; x++)
1135         change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
1136
1137     change->can_change = getFile8Bit(file);
1138
1139     change->trigger_side = getFile8Bit(file);
1140
1141 #if 1
1142     change->trigger_player = getFile8Bit(file);
1143     change->trigger_page = getFile8Bit(file);
1144
1145     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1146                             CH_PAGE_ANY : (1 << change->trigger_page));
1147
1148     /* some free bytes for future change property values and padding */
1149     ReadUnusedBytesFromFile(file, 6);
1150
1151 #else
1152
1153     /* some free bytes for future change property values and padding */
1154     ReadUnusedBytesFromFile(file, 8);
1155 #endif
1156   }
1157
1158   /* mark this custom element as modified */
1159   ei->modified_settings = TRUE;
1160
1161   return chunk_size;
1162 }
1163
1164 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1165 {
1166   struct ElementInfo *ei;
1167   struct ElementGroupInfo *group;
1168   int element;
1169   int i;
1170
1171   element = getFile16BitBE(file);
1172
1173   if (!IS_GROUP_ELEMENT(element))
1174   {
1175     Error(ERR_WARN, "invalid group element number %d", element);
1176
1177     ReadUnusedBytesFromFile(file, chunk_size - 2);
1178     return chunk_size;
1179   }
1180
1181   ei = &element_info[element];
1182
1183   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1184     ei->description[i] = getFile8Bit(file);
1185   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1186
1187   group = element_info[element].group;
1188
1189   group->num_elements = getFile8Bit(file);
1190
1191   ei->use_gfx_element = getFile8Bit(file);
1192   ei->gfx_element = getMappedElement(getFile16BitBE(file));
1193
1194   group->choice_mode = getFile8Bit(file);
1195
1196   /* some free bytes for future values and padding */
1197   ReadUnusedBytesFromFile(file, 3);
1198
1199   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1200     group->element[i] = getMappedElement(getFile16BitBE(file));
1201
1202   /* mark this group element as modified */
1203   element_info[element].modified_settings = TRUE;
1204
1205   return chunk_size;
1206 }
1207
1208 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1209                                       struct LevelFileInfo *level_file_info)
1210 {
1211   char *filename = level_file_info->filename;
1212   char cookie[MAX_LINE_LEN];
1213   char chunk_name[CHUNK_ID_LEN + 1];
1214   int chunk_size;
1215   FILE *file;
1216
1217   if (!(file = fopen(filename, MODE_READ)))
1218   {
1219     level->no_valid_file = TRUE;
1220
1221     if (level != &level_template)
1222       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1223
1224     return;
1225   }
1226
1227   getFileChunkBE(file, chunk_name, NULL);
1228   if (strcmp(chunk_name, "RND1") == 0)
1229   {
1230     getFile32BitBE(file);               /* not used */
1231
1232     getFileChunkBE(file, chunk_name, NULL);
1233     if (strcmp(chunk_name, "CAVE") != 0)
1234     {
1235       level->no_valid_file = TRUE;
1236
1237       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1238       fclose(file);
1239       return;
1240     }
1241   }
1242   else  /* check for pre-2.0 file format with cookie string */
1243   {
1244     strcpy(cookie, chunk_name);
1245     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1246     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1247       cookie[strlen(cookie) - 1] = '\0';
1248
1249     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1250     {
1251       level->no_valid_file = TRUE;
1252
1253       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1254       fclose(file);
1255       return;
1256     }
1257
1258     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1259     {
1260       level->no_valid_file = TRUE;
1261
1262       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1263       fclose(file);
1264       return;
1265     }
1266
1267     /* pre-2.0 level files have no game version, so use file version here */
1268     level->game_version = level->file_version;
1269   }
1270
1271   if (level->file_version < FILE_VERSION_1_2)
1272   {
1273     /* level files from versions before 1.2.0 without chunk structure */
1274     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
1275     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1276   }
1277   else
1278   {
1279     static struct
1280     {
1281       char *name;
1282       int size;
1283       int (*loader)(FILE *, int, struct LevelInfo *);
1284     }
1285     chunk_info[] =
1286     {
1287       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
1288       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
1289       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
1290       { "BODY", -1,                     LoadLevel_BODY },
1291       { "CONT", -1,                     LoadLevel_CONT },
1292       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
1293       { "CNT3", -1,                     LoadLevel_CNT3 },
1294       { "CUS1", -1,                     LoadLevel_CUS1 },
1295       { "CUS2", -1,                     LoadLevel_CUS2 },
1296       { "CUS3", -1,                     LoadLevel_CUS3 },
1297       { "CUS4", -1,                     LoadLevel_CUS4 },
1298       { "GRP1", -1,                     LoadLevel_GRP1 },
1299       {  NULL,  0,                      NULL }
1300     };
1301
1302     while (getFileChunkBE(file, chunk_name, &chunk_size))
1303     {
1304       int i = 0;
1305
1306       while (chunk_info[i].name != NULL &&
1307              strcmp(chunk_name, chunk_info[i].name) != 0)
1308         i++;
1309
1310       if (chunk_info[i].name == NULL)
1311       {
1312         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1313               chunk_name, filename);
1314         ReadUnusedBytesFromFile(file, chunk_size);
1315       }
1316       else if (chunk_info[i].size != -1 &&
1317                chunk_info[i].size != chunk_size)
1318       {
1319         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1320               chunk_size, chunk_name, filename);
1321         ReadUnusedBytesFromFile(file, chunk_size);
1322       }
1323       else
1324       {
1325         /* call function to load this level chunk */
1326         int chunk_size_expected =
1327           (chunk_info[i].loader)(file, chunk_size, level);
1328
1329         /* the size of some chunks cannot be checked before reading other
1330            chunks first (like "HEAD" and "BODY") that contain some header
1331            information, so check them here */
1332         if (chunk_size_expected != chunk_size)
1333         {
1334           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1335                 chunk_size, chunk_name, filename);
1336         }
1337       }
1338     }
1339   }
1340
1341   fclose(file);
1342 }
1343
1344 /* ------------------------------------------------------------------------- */
1345 /* functions for loading EM level                                            */
1346 /* ------------------------------------------------------------------------- */
1347
1348 #if 0
1349
1350 static int map_em_element_yam(int element)
1351 {
1352   switch (element)
1353   {
1354     case 0x00:  return EL_EMPTY;
1355     case 0x01:  return EL_EMERALD;
1356     case 0x02:  return EL_DIAMOND;
1357     case 0x03:  return EL_ROCK;
1358     case 0x04:  return EL_ROBOT;
1359     case 0x05:  return EL_SPACESHIP_UP;
1360     case 0x06:  return EL_BOMB;
1361     case 0x07:  return EL_BUG_UP;
1362     case 0x08:  return EL_AMOEBA_DROP;
1363     case 0x09:  return EL_NUT;
1364     case 0x0a:  return EL_YAMYAM;
1365     case 0x0b:  return EL_QUICKSAND_FULL;
1366     case 0x0c:  return EL_SAND;
1367     case 0x0d:  return EL_WALL_SLIPPERY;
1368     case 0x0e:  return EL_STEELWALL;
1369     case 0x0f:  return EL_WALL;
1370     case 0x10:  return EL_EM_KEY_1;
1371     case 0x11:  return EL_EM_KEY_2;
1372     case 0x12:  return EL_EM_KEY_4;
1373     case 0x13:  return EL_EM_KEY_3;
1374     case 0x14:  return EL_MAGIC_WALL;
1375     case 0x15:  return EL_ROBOT_WHEEL;
1376     case 0x16:  return EL_DYNAMITE;
1377
1378     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
1379     case 0x18:  return EL_BUG_UP;                       /* EMC */
1380     case 0x1a:  return EL_DIAMOND;                      /* EMC */
1381     case 0x1b:  return EL_EMERALD;                      /* EMC */
1382     case 0x25:  return EL_NUT;                          /* EMC */
1383     case 0x80:  return EL_EMPTY;                        /* EMC */
1384     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
1385     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
1386     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
1387     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
1388     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
1389     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
1390     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
1391     case 0xbd:  return EL_SAND;                         /* EMC */
1392
1393     default:
1394       Error(ERR_WARN, "invalid level element %d", element);
1395       return EL_UNKNOWN;
1396   }
1397 }
1398
1399 static int map_em_element_field(int element)
1400 {
1401   if (element >= 0xc8 && element <= 0xe1)
1402     return EL_CHAR_A + (element - 0xc8);
1403   else if (element >= 0xe2 && element <= 0xeb)
1404     return EL_CHAR_0 + (element - 0xe2);
1405
1406   switch (element)
1407   {
1408     case 0x00:  return EL_ROCK;
1409     case 0x01:  return EL_ROCK;                         /* EMC */
1410     case 0x02:  return EL_DIAMOND;
1411     case 0x03:  return EL_DIAMOND;
1412     case 0x04:  return EL_ROBOT;
1413     case 0x05:  return EL_ROBOT;                        /* EMC */
1414     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
1415     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
1416     case 0x08:  return EL_SPACESHIP_UP;
1417     case 0x09:  return EL_SPACESHIP_RIGHT;
1418     case 0x0a:  return EL_SPACESHIP_DOWN;
1419     case 0x0b:  return EL_SPACESHIP_LEFT;
1420     case 0x0c:  return EL_SPACESHIP_UP;
1421     case 0x0d:  return EL_SPACESHIP_RIGHT;
1422     case 0x0e:  return EL_SPACESHIP_DOWN;
1423     case 0x0f:  return EL_SPACESHIP_LEFT;
1424
1425     case 0x10:  return EL_BOMB;
1426     case 0x11:  return EL_BOMB;                         /* EMC */
1427     case 0x12:  return EL_EMERALD;
1428     case 0x13:  return EL_EMERALD;
1429     case 0x14:  return EL_BUG_UP;
1430     case 0x15:  return EL_BUG_RIGHT;
1431     case 0x16:  return EL_BUG_DOWN;
1432     case 0x17:  return EL_BUG_LEFT;
1433     case 0x18:  return EL_BUG_UP;
1434     case 0x19:  return EL_BUG_RIGHT;
1435     case 0x1a:  return EL_BUG_DOWN;
1436     case 0x1b:  return EL_BUG_LEFT;
1437     case 0x1c:  return EL_AMOEBA_DROP;
1438     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
1439     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
1440     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
1441
1442     case 0x20:  return EL_ROCK;
1443     case 0x21:  return EL_BOMB;                         /* EMC */
1444     case 0x22:  return EL_DIAMOND;                      /* EMC */
1445     case 0x23:  return EL_EMERALD;                      /* EMC */
1446     case 0x24:  return EL_MAGIC_WALL;
1447     case 0x25:  return EL_NUT;
1448     case 0x26:  return EL_NUT;                          /* EMC */
1449     case 0x27:  return EL_NUT;                          /* EMC */
1450
1451       /* looks like magic wheel, but is _always_ activated */
1452     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
1453
1454     case 0x29:  return EL_YAMYAM;       /* up */
1455     case 0x2a:  return EL_YAMYAM;       /* down */
1456     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
1457     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
1458     case 0x2d:  return EL_QUICKSAND_FULL;
1459     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
1460     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
1461
1462     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
1463     case 0x31:  return EL_SAND;                         /* EMC */
1464     case 0x32:  return EL_SAND;                         /* EMC */
1465     case 0x33:  return EL_SAND;                         /* EMC */
1466     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
1467     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
1468     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
1469     case 0x37:  return EL_SAND;                         /* EMC */
1470     case 0x38:  return EL_ROCK;                         /* EMC */
1471     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
1472     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
1473     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
1474     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
1475     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
1476     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
1477     case 0x3f:  return EL_ACID_POOL_BOTTOM;
1478
1479     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
1480     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
1481     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
1482     case 0x43:  return EL_BALLOON;                      /* EMC */
1483     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
1484     case 0x45:  return EL_SPRING;                       /* EMC */
1485     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
1486     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
1487     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
1488     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
1489     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
1490     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
1491     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
1492     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
1493     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
1494     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
1495
1496     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
1497     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
1498     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
1499     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
1500     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
1501     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
1502     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
1503     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
1504     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
1505     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
1506     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
1507     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
1508     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
1509     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
1510     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
1511     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
1512
1513     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
1514     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
1515     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
1516     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
1517     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
1518     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
1519     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
1520     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
1521     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
1522     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
1523     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
1524     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
1525     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
1526     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
1527     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
1528     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
1529
1530     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
1531     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
1532     case 0x72:  return EL_NUT;          /* left */      /* EMC */
1533     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
1534     case 0x74:  return EL_STEELWALL;
1535     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
1536     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
1537     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
1538     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
1539     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
1540     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
1541     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
1542     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
1543     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
1544     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
1545     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
1546
1547     case 0x80:  return EL_EMPTY;
1548     case 0x81:  return EL_WALL_SLIPPERY;
1549     case 0x82:  return EL_SAND;
1550     case 0x83:  return EL_STEELWALL;
1551     case 0x84:  return EL_WALL;
1552     case 0x85:  return EL_EM_KEY_1;
1553     case 0x86:  return EL_EM_KEY_2;
1554     case 0x87:  return EL_EM_KEY_4;
1555     case 0x88:  return EL_EM_KEY_3;
1556     case 0x89:  return EL_EM_GATE_1;
1557     case 0x8a:  return EL_EM_GATE_2;
1558     case 0x8b:  return EL_EM_GATE_4;
1559     case 0x8c:  return EL_EM_GATE_3;
1560     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
1561     case 0x8e:  return EL_EM_GATE_1_GRAY;
1562     case 0x8f:  return EL_EM_GATE_2_GRAY;
1563
1564     case 0x90:  return EL_EM_GATE_4_GRAY;
1565     case 0x91:  return EL_EM_GATE_3_GRAY;
1566     case 0x92:  return EL_MAGIC_WALL;
1567     case 0x93:  return EL_ROBOT_WHEEL;
1568     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
1569     case 0x95:  return EL_ACID_POOL_TOPLEFT;
1570     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
1571     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
1572     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
1573     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
1574     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
1575     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
1576     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
1577     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
1578     case 0x9e:  return EL_EXIT_CLOSED;
1579     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
1580
1581       /* looks like normal sand, but behaves like wall */
1582     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1583     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
1584     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
1585     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
1586     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
1587     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
1588     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
1589     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
1590     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
1591     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
1592     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
1593     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
1594     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
1595     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
1596     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
1597     case 0xaf:  return EL_DYNAMITE;
1598
1599     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
1600     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
1601     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
1602     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
1603     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
1604     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
1605     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
1606     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
1607     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
1608     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
1609     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
1610     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
1611     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
1612     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
1613     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
1614     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
1615
1616     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
1617     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
1618     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
1619     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
1620     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
1621     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
1622     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
1623     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
1624
1625       /* characters: see above */
1626
1627     case 0xec:  return EL_CHAR_PERIOD;
1628     case 0xed:  return EL_CHAR_EXCLAM;
1629     case 0xee:  return EL_CHAR_COLON;
1630     case 0xef:  return EL_CHAR_QUESTION;
1631
1632     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
1633     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
1634     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
1635     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
1636     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
1637     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
1638     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
1639     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
1640
1641     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
1642     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
1643     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
1644     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
1645     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
1646     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
1647
1648     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
1649     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
1650
1651     default:
1652       /* should never happen (all 8-bit value cases should be handled) */
1653       Error(ERR_WARN, "invalid level element %d", element);
1654       return EL_UNKNOWN;
1655   }
1656 }
1657
1658 #define EM_LEVEL_SIZE                   2106
1659 #define EM_LEVEL_XSIZE                  64
1660 #define EM_LEVEL_YSIZE                  32
1661
1662 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1663                                          struct LevelFileInfo *level_file_info)
1664 {
1665   char *filename = level_file_info->filename;
1666   FILE *file;
1667   unsigned char leveldata[EM_LEVEL_SIZE];
1668   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1669   int nr = level_file_info->nr;
1670   int i, x, y;
1671
1672   if (!(file = fopen(filename, MODE_READ)))
1673   {
1674     level->no_valid_file = TRUE;
1675
1676     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1677
1678     return;
1679   }
1680
1681   for (i = 0; i < EM_LEVEL_SIZE; i++)
1682     leveldata[i] = fgetc(file);
1683
1684   fclose(file);
1685
1686   /* check if level data is crypted by testing against known starting bytes
1687      of the few existing crypted level files (from Emerald Mine 1 + 2) */
1688
1689   if ((leveldata[0] == 0xf1 ||
1690        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1691   {
1692     unsigned char code0 = 0x65;
1693     unsigned char code1 = 0x11;
1694
1695     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
1696       leveldata[0] = 0xf1;
1697
1698     /* decode crypted level data */
1699
1700     for (i = 0; i < EM_LEVEL_SIZE; i++)
1701     {
1702       leveldata[i] ^= code0;
1703       leveldata[i] -= code1;
1704
1705       code0 = (code0 + 7) & 0xff;
1706     }
1707   }
1708
1709   level->fieldx = EM_LEVEL_XSIZE;
1710   level->fieldy = EM_LEVEL_YSIZE;
1711
1712   level->time           = header[46] * 10;
1713   level->gems_needed    = header[47];
1714
1715   /* The original Emerald Mine levels have their level number stored
1716      at the second byte of the level file...
1717      Do not trust this information at other level files, e.g. EMC,
1718      but correct it anyway (normally the first row is completely
1719      steel wall, so the correction does not hurt anyway). */
1720
1721   if (leveldata[1] == nr)
1722     leveldata[1] = leveldata[2];        /* correct level number field */
1723
1724   sprintf(level->name, "Level %d", nr);         /* set level name */
1725
1726   level->score[SC_EMERALD]      = header[36];
1727   level->score[SC_DIAMOND]      = header[37];
1728   level->score[SC_ROBOT]        = header[38];
1729   level->score[SC_SPACESHIP]    = header[39];
1730   level->score[SC_BUG]          = header[40];
1731   level->score[SC_YAMYAM]       = header[41];
1732   level->score[SC_NUT]          = header[42];
1733   level->score[SC_DYNAMITE]     = header[43];
1734   level->score[SC_TIME_BONUS]   = header[44];
1735
1736   level->num_yamyam_contents = 4;
1737
1738   for (i = 0; i < level->num_yamyam_contents; i++)
1739     for (y = 0; y < 3; y++)
1740       for (x = 0; x < 3; x++)
1741         level->yamyam_content[i][x][y] =
1742           map_em_element_yam(header[i * 9 + y * 3 + x]);
1743
1744   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
1745   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
1746   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
1747   level->amoeba_content         = EL_DIAMOND;
1748
1749   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1750   {
1751     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1752
1753     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1754       new_element = EL_AMOEBA_WET;
1755
1756     level->field[x][y] = new_element;
1757   }
1758
1759   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1760   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1761   level->field[x][y] = EL_PLAYER_1;
1762
1763   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1764   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1765   level->field[x][y] = EL_PLAYER_2;
1766 }
1767
1768 #else
1769
1770 static int map_element_RND_to_EM(int element_rnd)
1771 {
1772   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
1773   static boolean mapping_initialized = FALSE;
1774
1775   struct
1776   {
1777     int element_em;
1778     int element_rnd;
1779   }
1780   mapping_RND_to_EM_list[] =
1781   {
1782     { Xblank,                   EL_EMPTY                        },
1783     { Xstone,                   EL_ROCK                         },
1784     { Xnut,                     EL_NUT                          },
1785     { Xbug_n,                   EL_BUG_UP                       },
1786     { Xbug_e,                   EL_BUG_RIGHT                    },
1787     { Xbug_s,                   EL_BUG_DOWN                     },
1788     { Xbug_w,                   EL_BUG_LEFT                     },
1789     { Xtank_n,                  EL_SPACESHIP_UP                 },
1790     { Xtank_e,                  EL_SPACESHIP_RIGHT              },
1791     { Xtank_s,                  EL_SPACESHIP_DOWN               },
1792     { Xtank_w,                  EL_SPACESHIP_LEFT               },
1793     { Xandroid,                 EL_EMC_ANDROID                  },
1794     { Xandroid_1_n,             EL_EMC_ANDROID_UP               },
1795     { Xandroid_1_e,             EL_EMC_ANDROID_RIGHT            },
1796     { Xandroid_1_w,             EL_EMC_ANDROID_LEFT             },
1797     { Xandroid_1_s,             EL_EMC_ANDROID_DOWN             },
1798     { Xspring,                  EL_SPRING                       },
1799     { Xeater_n,                 EL_YAMYAM                       },
1800     { Xalien,                   EL_ROBOT                        },
1801     { Xemerald,                 EL_EMERALD                      },
1802     { Xdiamond,                 EL_DIAMOND                      },
1803     { Xdrip_fall,               EL_AMOEBA_DROP                  },
1804     { Xbomb,                    EL_BOMB                         },
1805     { Xballoon,                 EL_BALLOON                      },
1806     { Xgrass,                   EL_EMC_GRASS                    },
1807     { Xdirt,                    EL_SAND                         },
1808     { Xacid_ne,                 EL_ACID_POOL_TOPRIGHT           },
1809     { Xacid_se,                 EL_ACID_POOL_BOTTOMRIGHT        },
1810     { Xacid_s,                  EL_ACID_POOL_BOTTOM             },
1811     { Xacid_sw,                 EL_ACID_POOL_BOTTOMLEFT         },
1812     { Xacid_nw,                 EL_ACID_POOL_TOPLEFT            },
1813     { Xacid_1,                  EL_ACID                         },
1814     { Xball_1,                  EL_EMC_GENERATOR_BALL           },
1815     { Xgrow_ns,                 EL_EMC_GROW                     },
1816     { Xwonderwall,              EL_MAGIC_WALL                   },
1817     { Xameuba_1,                EL_AMOEBA_WET                   },
1818     { Xdoor_1,                  EL_EM_GATE_1                    },
1819     { Xdoor_2,                  EL_EM_GATE_2                    },
1820     { Xdoor_3,                  EL_EM_GATE_3                    },
1821     { Xdoor_4,                  EL_EM_GATE_4                    },
1822     { Xdoor_5,                  EL_EMC_GATE_5                   },
1823     { Xdoor_6,                  EL_EMC_GATE_6                   },
1824     { Xdoor_7,                  EL_EMC_GATE_7                   },
1825     { Xdoor_8,                  EL_EMC_GATE_8                   },
1826     { Xkey_1,                   EL_EM_KEY_1                     },
1827     { Xkey_2,                   EL_EM_KEY_2                     },
1828     { Xkey_3,                   EL_EM_KEY_3                     },
1829     { Xkey_4,                   EL_EM_KEY_4                     },
1830     { Xkey_5,                   EL_EMC_KEY_5                    },
1831     { Xkey_6,                   EL_EMC_KEY_6                    },
1832     { Xkey_7,                   EL_EMC_KEY_7                    },
1833     { Xkey_8,                   EL_EMC_KEY_8                    },
1834     { Xwind_n,                  EL_BALLOON_SWITCH_UP            },
1835     { Xwind_e,                  EL_BALLOON_SWITCH_RIGHT         },
1836     { Xwind_s,                  EL_BALLOON_SWITCH_DOWN          },
1837     { Xwind_w,                  EL_BALLOON_SWITCH_LEFT          },
1838     { Xwind_nesw,               EL_BALLOON_SWITCH_ANY           },
1839     { Xwind_stop,               EL_BALLOON_SWITCH_NONE          },
1840     { Xexit,                    EL_EXIT_CLOSED                  },
1841     { Xexit_1,                  EL_EXIT_OPEN                    },
1842     { Xdynamite,                EL_DYNAMITE                     },
1843     { Xdynamite_1,              EL_DYNAMITE_ACTIVE              },
1844     { Xbumper,                  EL_EMC_BUMPER                   },
1845     { Xwheel,                   EL_ROBOT_WHEEL                  },
1846     { Xswitch,                  EL_UNKNOWN                      },
1847     { Xsand,                    EL_QUICKSAND_EMPTY              },
1848     { Xsand_stone,              EL_QUICKSAND_FULL               },
1849     { Xplant,                   EL_EMC_PLANT                    },
1850     { Xlenses,                  EL_EMC_LENSES                   },
1851     { Xmagnify,                 EL_EMC_MAGNIFY                  },
1852     { Xdripper,                 EL_UNKNOWN                      },
1853     { Xfake_blank,              EL_INVISIBLE_WALL               },
1854     { Xfake_grass,              EL_INVISIBLE_SAND               },
1855     { Xfake_door_1,             EL_EM_GATE_1_GRAY               },
1856     { Xfake_door_2,             EL_EM_GATE_2_GRAY               },
1857     { Xfake_door_3,             EL_EM_GATE_3_GRAY               },
1858     { Xfake_door_4,             EL_EM_GATE_4_GRAY               },
1859     { Xfake_door_5,             EL_EMC_GATE_5_GRAY              },
1860     { Xfake_door_6,             EL_EMC_GATE_6_GRAY              },
1861     { Xfake_door_7,             EL_EMC_GATE_7_GRAY              },
1862     { Xfake_door_8,             EL_EMC_GATE_8_GRAY              },
1863     { Xsteel_1,                 EL_STEELWALL                    },
1864     { Xsteel_2,                 EL_UNKNOWN                      },
1865     { Xsteel_3,                 EL_EMC_STEELWALL_1              },
1866     { Xsteel_4,                 EL_UNKNOWN                      },
1867     { Xwall_1,                  EL_WALL                         },
1868     { Xwall_2,                  EL_UNKNOWN                      },
1869     { Xwall_3,                  EL_UNKNOWN                      },
1870     { Xwall_4,                  EL_UNKNOWN                      },
1871     { Xround_wall_1,            EL_WALL_SLIPPERY                },
1872     { Xround_wall_2,            EL_UNKNOWN                      },
1873     { Xround_wall_3,            EL_UNKNOWN                      },
1874     { Xround_wall_4,            EL_UNKNOWN                      },
1875     { Xdecor_1,                 EL_UNKNOWN                      },
1876     { Xdecor_2,                 EL_EMC_WALL_6                   },
1877     { Xdecor_3,                 EL_EMC_WALL_4                   },
1878     { Xdecor_4,                 EL_EMC_WALL_5                   },
1879     { Xdecor_5,                 EL_EMC_WALL_7                   },
1880     { Xdecor_6,                 EL_EMC_WALL_8                   },
1881     { Xdecor_7,                 EL_UNKNOWN                      },
1882     { Xdecor_8,                 EL_EMC_WALL_1                   },
1883     { Xdecor_9,                 EL_EMC_WALL_2                   },
1884     { Xdecor_10,                EL_EMC_WALL_3                   },
1885     { Xdecor_11,                EL_UNKNOWN                      },
1886     { Xdecor_12,                EL_UNKNOWN                      },
1887     { Xalpha_0,                 EL_CHAR('0')                    },
1888     { Xalpha_1,                 EL_CHAR('1')                    },
1889     { Xalpha_2,                 EL_CHAR('2')                    },
1890     { Xalpha_3,                 EL_CHAR('3')                    },
1891     { Xalpha_4,                 EL_CHAR('4')                    },
1892     { Xalpha_5,                 EL_CHAR('5')                    },
1893     { Xalpha_6,                 EL_CHAR('6')                    },
1894     { Xalpha_7,                 EL_CHAR('7')                    },
1895     { Xalpha_8,                 EL_CHAR('8')                    },
1896     { Xalpha_9,                 EL_CHAR('9')                    },
1897     { Xalpha_excla,             EL_CHAR('!')                    },
1898     { Xalpha_quote,             EL_CHAR('"')                    },
1899     { Xalpha_comma,             EL_CHAR(',')                    },
1900     { Xalpha_minus,             EL_CHAR('-')                    },
1901     { Xalpha_perio,             EL_CHAR('.')                    },
1902     { Xalpha_colon,             EL_CHAR(':')                    },
1903     { Xalpha_quest,             EL_CHAR('?')                    },
1904     { Xalpha_a,                 EL_CHAR('A')                    },
1905     { Xalpha_b,                 EL_CHAR('B')                    },
1906     { Xalpha_c,                 EL_CHAR('C')                    },
1907     { Xalpha_d,                 EL_CHAR('D')                    },
1908     { Xalpha_e,                 EL_CHAR('E')                    },
1909     { Xalpha_f,                 EL_CHAR('F')                    },
1910     { Xalpha_g,                 EL_CHAR('G')                    },
1911     { Xalpha_h,                 EL_CHAR('H')                    },
1912     { Xalpha_i,                 EL_CHAR('I')                    },
1913     { Xalpha_j,                 EL_CHAR('J')                    },
1914     { Xalpha_k,                 EL_CHAR('K')                    },
1915     { Xalpha_l,                 EL_CHAR('L')                    },
1916     { Xalpha_m,                 EL_CHAR('M')                    },
1917     { Xalpha_n,                 EL_CHAR('N')                    },
1918     { Xalpha_o,                 EL_CHAR('O')                    },
1919     { Xalpha_p,                 EL_CHAR('P')                    },
1920     { Xalpha_q,                 EL_CHAR('Q')                    },
1921     { Xalpha_r,                 EL_CHAR('R')                    },
1922     { Xalpha_s,                 EL_CHAR('S')                    },
1923     { Xalpha_t,                 EL_CHAR('T')                    },
1924     { Xalpha_u,                 EL_CHAR('U')                    },
1925     { Xalpha_v,                 EL_CHAR('V')                    },
1926     { Xalpha_w,                 EL_CHAR('W')                    },
1927     { Xalpha_x,                 EL_CHAR('X')                    },
1928     { Xalpha_y,                 EL_CHAR('Y')                    },
1929     { Xalpha_z,                 EL_CHAR('Z')                    },
1930     { Xalpha_arrow_e,           EL_CHAR('>')                    },
1931     { Xalpha_arrow_w,           EL_CHAR('<')                    },
1932     { Xalpha_copyr,             EL_CHAR('©')                    },
1933
1934     { Zplayer,                  EL_PLAYER_1                     },
1935     { Zplayer,                  EL_PLAYER_2                     },
1936     { Zplayer,                  EL_PLAYER_3                     },
1937     { Zplayer,                  EL_PLAYER_4                     },
1938
1939     { ZBORDER,                  EL_EMC_LEVEL_BORDER             },
1940
1941     { -1,                       -1                              }
1942   };
1943
1944   if (!mapping_initialized)
1945   {
1946     int i;
1947
1948     /* return "Xalpha_quest" for all undefined elements in mapping array */
1949     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
1950       mapping_RND_to_EM[i] = Xalpha_quest;
1951
1952     for (i = 0; mapping_RND_to_EM_list[i].element_rnd != -1; i++)
1953       mapping_RND_to_EM[mapping_RND_to_EM_list[i].element_rnd] =
1954         mapping_RND_to_EM_list[i].element_em;
1955
1956     mapping_initialized = TRUE;
1957   }
1958
1959   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
1960     return mapping_RND_to_EM[element_rnd];
1961
1962   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
1963
1964   return EL_UNKNOWN;
1965 }
1966
1967 static int map_element_EM_to_RND(int element_em)
1968 {
1969   static unsigned short mapping_EM_to_RND[TILE_MAX];
1970   static boolean mapping_initialized = FALSE;
1971
1972   struct
1973   {
1974     int element_em;
1975     int element_rnd;
1976   }
1977   mapping_EM_to_RND_list[] =
1978   {
1979     { Xblank,                   EL_EMPTY                        },
1980     { Yacid_splash_eB,          EL_EMPTY                        },
1981     { Yacid_splash_wB,          EL_EMPTY                        },
1982
1983 #ifdef EM_ENGINE_BAD_ROLL
1984     { Xstone_force_e,           EL_ROCK                         },
1985     { Xstone_force_w,           EL_ROCK                         },
1986     { Xnut_force_e,             EL_NUT                          },
1987     { Xnut_force_w,             EL_NUT                          },
1988     { Xspring_force_e,          EL_SPRING                       },
1989     { Xspring_force_w,          EL_SPRING                       },
1990     { Xemerald_force_e,         EL_EMERALD                      },
1991     { Xemerald_force_w,         EL_EMERALD                      },
1992     { Xdiamond_force_e,         EL_DIAMOND                      },
1993     { Xdiamond_force_w,         EL_DIAMOND                      },
1994     { Xbomb_force_e,            EL_BOMB                         },
1995     { Xbomb_force_w,            EL_BOMB                         },
1996 #endif
1997
1998     { Xstone,                   EL_ROCK                         },
1999     { Xstone_pause,             EL_ROCK                         },
2000     { Xstone_fall,              EL_ROCK                         },
2001     { Ystone_s,                 EL_ROCK                         },
2002     { Ystone_sB,                EL_ROCK                         },
2003     { Ystone_e,                 EL_ROCK                         },
2004     { Ystone_eB,                EL_ROCK                         },
2005     { Ystone_w,                 EL_ROCK                         },
2006     { Ystone_wB,                EL_ROCK                         },
2007     { Xnut,                     EL_NUT                          },
2008     { Xnut_pause,               EL_NUT                          },
2009     { Xnut_fall,                EL_NUT                          },
2010     { Ynut_s,                   EL_NUT                          },
2011     { Ynut_sB,                  EL_NUT                          },
2012     { Ynut_e,                   EL_NUT                          },
2013     { Ynut_eB,                  EL_NUT                          },
2014     { Ynut_w,                   EL_NUT                          },
2015     { Ynut_wB,                  EL_NUT                          },
2016     { Xbug_n,                   EL_BUG_UP                       },
2017     { Xbug_e,                   EL_BUG_RIGHT                    },
2018     { Xbug_s,                   EL_BUG_DOWN                     },
2019     { Xbug_w,                   EL_BUG_LEFT                     },
2020     { Xbug_gon,                 EL_BUG_UP                       },
2021     { Xbug_goe,                 EL_BUG_RIGHT                    },
2022     { Xbug_gos,                 EL_BUG_DOWN                     },
2023     { Xbug_gow,                 EL_BUG_LEFT                     },
2024     { Ybug_n,                   EL_BUG_UP                       },
2025     { Ybug_nB,                  EL_BUG_UP                       },
2026     { Ybug_e,                   EL_BUG_RIGHT                    },
2027     { Ybug_eB,                  EL_BUG_RIGHT                    },
2028     { Ybug_s,                   EL_BUG_DOWN                     },
2029     { Ybug_sB,                  EL_BUG_DOWN                     },
2030     { Ybug_w,                   EL_BUG_LEFT                     },
2031     { Ybug_wB,                  EL_BUG_LEFT                     },
2032     { Ybug_w_n,                 EL_BUG_UP                       },
2033     { Ybug_n_e,                 EL_BUG_RIGHT                    },
2034     { Ybug_e_s,                 EL_BUG_DOWN                     },
2035     { Ybug_s_w,                 EL_BUG_LEFT                     },
2036     { Ybug_e_n,                 EL_BUG_UP                       },
2037     { Ybug_s_e,                 EL_BUG_RIGHT                    },
2038     { Ybug_w_s,                 EL_BUG_DOWN                     },
2039     { Ybug_n_w,                 EL_BUG_LEFT                     },
2040     { Ybug_stone,               EL_ROCK                         },
2041     { Ybug_spring,              EL_SPRING                       },
2042     { Xtank_n,                  EL_SPACESHIP_UP                 },
2043     { Xtank_e,                  EL_SPACESHIP_RIGHT              },
2044     { Xtank_s,                  EL_SPACESHIP_DOWN               },
2045     { Xtank_w,                  EL_SPACESHIP_LEFT               },
2046     { Xtank_gon,                EL_SPACESHIP_UP                 },
2047     { Xtank_goe,                EL_SPACESHIP_RIGHT              },
2048     { Xtank_gos,                EL_SPACESHIP_DOWN               },
2049     { Xtank_gow,                EL_SPACESHIP_LEFT               },
2050     { Ytank_n,                  EL_SPACESHIP_UP                 },
2051     { Ytank_nB,                 EL_SPACESHIP_UP                 },
2052     { Ytank_e,                  EL_SPACESHIP_RIGHT              },
2053     { Ytank_eB,                 EL_SPACESHIP_RIGHT              },
2054     { Ytank_s,                  EL_SPACESHIP_DOWN               },
2055     { Ytank_sB,                 EL_SPACESHIP_DOWN               },
2056     { Ytank_w,                  EL_SPACESHIP_LEFT               },
2057     { Ytank_wB,                 EL_SPACESHIP_LEFT               },
2058     { Ytank_w_n,                EL_SPACESHIP_UP                 },
2059     { Ytank_n_e,                EL_SPACESHIP_RIGHT              },
2060     { Ytank_e_s,                EL_SPACESHIP_DOWN               },
2061     { Ytank_s_w,                EL_SPACESHIP_LEFT               },
2062     { Ytank_e_n,                EL_SPACESHIP_UP                 },
2063     { Ytank_s_e,                EL_SPACESHIP_RIGHT              },
2064     { Ytank_w_s,                EL_SPACESHIP_DOWN               },
2065     { Ytank_n_w,                EL_SPACESHIP_LEFT               },
2066     { Ytank_stone,              EL_ROCK                         },
2067     { Ytank_spring,             EL_SPRING                       },
2068     { Xandroid,                 EL_EMC_ANDROID                  },
2069     { Xandroid_1_n,             EL_EMC_ANDROID_UP               },
2070     { Xandroid_2_n,             EL_EMC_ANDROID_UP               },
2071     { Xandroid_1_e,             EL_EMC_ANDROID_RIGHT            },
2072     { Xandroid_2_e,             EL_EMC_ANDROID_RIGHT            },
2073     { Xandroid_1_w,             EL_EMC_ANDROID_LEFT             },
2074     { Xandroid_2_w,             EL_EMC_ANDROID_LEFT             },
2075     { Xandroid_1_s,             EL_EMC_ANDROID_DOWN             },
2076     { Xandroid_2_s,             EL_EMC_ANDROID_DOWN             },
2077     { Yandroid_n,               EL_EMC_ANDROID_UP               },
2078     { Yandroid_nB,              EL_EMC_ANDROID_UP               },
2079     { Yandroid_ne,              EL_EMC_ANDROID_RIGHT_UP         },
2080     { Yandroid_neB,             EL_EMC_ANDROID_RIGHT_UP         },
2081     { Yandroid_e,               EL_EMC_ANDROID_RIGHT            },
2082     { Yandroid_eB,              EL_EMC_ANDROID_RIGHT            },
2083     { Yandroid_se,              EL_EMC_ANDROID_RIGHT_DOWN       },
2084     { Yandroid_seB,             EL_EMC_ANDROID_RIGHT_DOWN       },
2085     { Yandroid_s,               EL_EMC_ANDROID_DOWN             },
2086     { Yandroid_sB,              EL_EMC_ANDROID_DOWN             },
2087     { Yandroid_sw,              EL_EMC_ANDROID_LEFT_DOWN        },
2088     { Yandroid_swB,             EL_EMC_ANDROID_LEFT_DOWN        },
2089     { Yandroid_w,               EL_EMC_ANDROID_LEFT             },
2090     { Yandroid_wB,              EL_EMC_ANDROID_LEFT             },
2091     { Yandroid_nw,              EL_EMC_ANDROID_LEFT_UP          },
2092     { Yandroid_nwB,             EL_EMC_ANDROID_LEFT_UP          },
2093     { Xspring,                  EL_SPRING                       },
2094     { Xspring_pause,            EL_SPRING                       },
2095     { Xspring_e,                EL_SPRING                       },
2096     { Xspring_w,                EL_SPRING                       },
2097     { Xspring_fall,             EL_SPRING                       },
2098     { Yspring_s,                EL_SPRING                       },
2099     { Yspring_sB,               EL_SPRING                       },
2100     { Yspring_e,                EL_SPRING                       },
2101     { Yspring_eB,               EL_SPRING                       },
2102     { Yspring_w,                EL_SPRING                       },
2103     { Yspring_wB,               EL_SPRING                       },
2104     { Yspring_kill_e,           EL_SPRING                       },
2105     { Yspring_kill_eB,          EL_SPRING                       },
2106     { Yspring_kill_w,           EL_SPRING                       },
2107     { Yspring_kill_wB,          EL_SPRING                       },
2108     { Xeater_n,                 EL_YAMYAM                       },
2109     { Xeater_e,                 EL_YAMYAM                       },
2110     { Xeater_w,                 EL_YAMYAM                       },
2111     { Xeater_s,                 EL_YAMYAM                       },
2112     { Yeater_n,                 EL_YAMYAM                       },
2113     { Yeater_nB,                EL_YAMYAM                       },
2114     { Yeater_e,                 EL_YAMYAM                       },
2115     { Yeater_eB,                EL_YAMYAM                       },
2116     { Yeater_s,                 EL_YAMYAM                       },
2117     { Yeater_sB,                EL_YAMYAM                       },
2118     { Yeater_w,                 EL_YAMYAM                       },
2119     { Yeater_wB,                EL_YAMYAM                       },
2120     { Yeater_stone,             EL_ROCK                         },
2121     { Yeater_spring,            EL_SPRING                       },
2122     { Xalien,                   EL_ROBOT                        },
2123     { Xalien_pause,             EL_ROBOT                        },
2124     { Yalien_n,                 EL_ROBOT                        },
2125     { Yalien_nB,                EL_ROBOT                        },
2126     { Yalien_e,                 EL_ROBOT                        },
2127     { Yalien_eB,                EL_ROBOT                        },
2128     { Yalien_s,                 EL_ROBOT                        },
2129     { Yalien_sB,                EL_ROBOT                        },
2130     { Yalien_w,                 EL_ROBOT                        },
2131     { Yalien_wB,                EL_ROBOT                        },
2132     { Yalien_stone,             EL_ROCK                         },
2133     { Yalien_spring,            EL_SPRING                       },
2134     { Xemerald,                 EL_EMERALD                      },
2135     { Xemerald_pause,           EL_EMERALD                      },
2136     { Xemerald_fall,            EL_EMERALD                      },
2137     { Xemerald_shine,           EL_EMERALD                      },
2138     { Yemerald_s,               EL_EMERALD                      },
2139     { Yemerald_sB,              EL_EMERALD                      },
2140     { Yemerald_e,               EL_EMERALD                      },
2141     { Yemerald_eB,              EL_EMERALD                      },
2142     { Yemerald_w,               EL_EMERALD                      },
2143     { Yemerald_wB,              EL_EMERALD                      },
2144     { Yemerald_eat,             EL_EMERALD                      },
2145     { Yemerald_stone,           EL_ROCK                         },
2146     { Xdiamond,                 EL_DIAMOND                      },
2147     { Xdiamond_pause,           EL_DIAMOND                      },
2148     { Xdiamond_fall,            EL_DIAMOND                      },
2149     { Xdiamond_shine,           EL_DIAMOND                      },
2150     { Ydiamond_s,               EL_DIAMOND                      },
2151     { Ydiamond_sB,              EL_DIAMOND                      },
2152     { Ydiamond_e,               EL_DIAMOND                      },
2153     { Ydiamond_eB,              EL_DIAMOND                      },
2154     { Ydiamond_w,               EL_DIAMOND                      },
2155     { Ydiamond_wB,              EL_DIAMOND                      },
2156     { Ydiamond_eat,             EL_DIAMOND                      },
2157     { Ydiamond_stone,           EL_ROCK                         },
2158     { Xdrip_fall,               EL_AMOEBA_DROP                  },
2159     { Xdrip_stretch,            EL_AMOEBA_DROP                  },
2160     { Xdrip_stretchB,           EL_AMOEBA_DROP                  },
2161     { Xdrip_eat,                EL_AMOEBA_DROP                  },
2162     { Ydrip_s1,                 EL_AMOEBA_DROP                  },
2163     { Ydrip_s1B,                EL_AMOEBA_DROP                  },
2164     { Ydrip_s2,                 EL_AMOEBA_DROP                  },
2165     { Ydrip_s2B,                EL_AMOEBA_DROP                  },
2166     { Xbomb,                    EL_BOMB                         },
2167     { Xbomb_pause,              EL_BOMB                         },
2168     { Xbomb_fall,               EL_BOMB                         },
2169     { Ybomb_s,                  EL_BOMB                         },
2170     { Ybomb_sB,                 EL_BOMB                         },
2171     { Ybomb_e,                  EL_BOMB                         },
2172     { Ybomb_eB,                 EL_BOMB                         },
2173     { Ybomb_w,                  EL_BOMB                         },
2174     { Ybomb_wB,                 EL_BOMB                         },
2175     { Ybomb_eat,                EL_BOMB                         },
2176     { Xballoon,                 EL_BALLOON                      },
2177     { Yballoon_n,               EL_BALLOON                      },
2178     { Yballoon_nB,              EL_BALLOON                      },
2179     { Yballoon_e,               EL_BALLOON                      },
2180     { Yballoon_eB,              EL_BALLOON                      },
2181     { Yballoon_s,               EL_BALLOON                      },
2182     { Yballoon_sB,              EL_BALLOON                      },
2183     { Yballoon_w,               EL_BALLOON                      },
2184     { Yballoon_wB,              EL_BALLOON                      },
2185     { Xgrass,                   EL_SAND                         },
2186     { Ygrass_nB,                EL_SAND                         },
2187     { Ygrass_eB,                EL_SAND                         },
2188     { Ygrass_sB,                EL_SAND                         },
2189     { Ygrass_wB,                EL_SAND                         },
2190     { Xdirt,                    EL_SAND                         },
2191     { Ydirt_nB,                 EL_SAND                         },
2192     { Ydirt_eB,                 EL_SAND                         },
2193     { Ydirt_sB,                 EL_SAND                         },
2194     { Ydirt_wB,                 EL_SAND                         },
2195     { Xacid_ne,                 EL_ACID_POOL_TOPRIGHT           },
2196     { Xacid_se,                 EL_ACID_POOL_BOTTOMRIGHT        },
2197     { Xacid_s,                  EL_ACID_POOL_BOTTOM             },
2198     { Xacid_sw,                 EL_ACID_POOL_BOTTOMLEFT         },
2199     { Xacid_nw,                 EL_ACID_POOL_TOPLEFT            },
2200     { Xacid_1,                  EL_ACID                         },
2201     { Xacid_2,                  EL_ACID                         },
2202     { Xacid_3,                  EL_ACID                         },
2203     { Xacid_4,                  EL_ACID                         },
2204     { Xacid_5,                  EL_ACID                         },
2205     { Xacid_6,                  EL_ACID                         },
2206     { Xacid_7,                  EL_ACID                         },
2207     { Xacid_8,                  EL_ACID                         },
2208     { Xball_1,                  EL_EMC_GENERATOR_BALL           },
2209     { Xball_1B,                 EL_EMC_GENERATOR_BALL           },
2210     { Xball_2,                  EL_EMC_GENERATOR_BALL           },
2211     { Xball_2B,                 EL_EMC_GENERATOR_BALL           },
2212     { Yball_eat,                EL_EMC_GENERATOR_BALL           },
2213     { Xgrow_ns,                 EL_EMC_GROW                     },
2214     { Ygrow_ns_eat,             EL_EMC_GROW                     },
2215     { Xgrow_ew,                 EL_EMC_GROW                     },
2216     { Ygrow_ew_eat,             EL_EMC_GROW                     },
2217     { Xwonderwall,              EL_MAGIC_WALL                   },
2218     { XwonderwallB,             EL_MAGIC_WALL                   },
2219     { Xameuba_1,                EL_AMOEBA_WET                   },
2220     { Xameuba_2,                EL_AMOEBA_WET                   },
2221     { Xameuba_3,                EL_AMOEBA_WET                   },
2222     { Xameuba_4,                EL_AMOEBA_WET                   },
2223     { Xameuba_5,                EL_AMOEBA_WET                   },
2224     { Xameuba_6,                EL_AMOEBA_WET                   },
2225     { Xameuba_7,                EL_AMOEBA_WET                   },
2226     { Xameuba_8,                EL_AMOEBA_WET                   },
2227     { Xdoor_1,                  EL_EM_GATE_1                    },
2228     { Xdoor_2,                  EL_EM_GATE_2                    },
2229     { Xdoor_3,                  EL_EM_GATE_3                    },
2230     { Xdoor_4,                  EL_EM_GATE_4                    },
2231     { Xdoor_5,                  EL_EMC_GATE_5                   },
2232     { Xdoor_6,                  EL_EMC_GATE_6                   },
2233     { Xdoor_7,                  EL_EMC_GATE_7                   },
2234     { Xdoor_8,                  EL_EMC_GATE_8                   },
2235     { Xkey_1,                   EL_EM_KEY_1                     },
2236     { Xkey_2,                   EL_EM_KEY_2                     },
2237     { Xkey_3,                   EL_EM_KEY_3                     },
2238     { Xkey_4,                   EL_EM_KEY_4                     },
2239     { Xkey_5,                   EL_EMC_KEY_5                    },
2240     { Xkey_6,                   EL_EMC_KEY_6                    },
2241     { Xkey_7,                   EL_EMC_KEY_7                    },
2242     { Xkey_8,                   EL_EMC_KEY_8                    },
2243     { Xwind_n,                  EL_BALLOON_SWITCH_UP            },
2244     { Xwind_e,                  EL_BALLOON_SWITCH_RIGHT         },
2245     { Xwind_s,                  EL_BALLOON_SWITCH_DOWN          },
2246     { Xwind_w,                  EL_BALLOON_SWITCH_LEFT          },
2247     { Xwind_nesw,               EL_BALLOON_SWITCH_ANY           },
2248     { Xwind_stop,               EL_BALLOON_SWITCH_NONE          },
2249     { Xexit,                    EL_EXIT_CLOSED                  },
2250     { Xexit_1,                  EL_EXIT_OPEN                    },
2251     { Xexit_2,                  EL_EXIT_OPEN                    },
2252     { Xexit_3,                  EL_EXIT_OPEN                    },
2253     { Xdynamite,                EL_DYNAMITE                     },
2254     { Ydynamite_eat,            EL_DYNAMITE                     },
2255     { Xdynamite_1,              EL_DYNAMITE_ACTIVE              },
2256     { Xdynamite_2,              EL_DYNAMITE_ACTIVE              },
2257     { Xdynamite_3,              EL_DYNAMITE_ACTIVE              },
2258     { Xdynamite_4,              EL_DYNAMITE_ACTIVE              },
2259     { Xbumper,                  EL_EMC_BUMPER                   },
2260     { XbumperB,                 EL_EMC_BUMPER                   },
2261     { Xwheel,                   EL_ROBOT_WHEEL                  },
2262     { XwheelB,                  EL_ROBOT_WHEEL                  },
2263     { Xswitch,                  EL_UNKNOWN                      },
2264     { XswitchB,                 EL_UNKNOWN                      },
2265     { Xsand,                    EL_QUICKSAND_EMPTY              },
2266     { Xsand_stone,              EL_QUICKSAND_FULL               },
2267     { Xsand_stonein_1,          EL_QUICKSAND_FULL               },
2268     { Xsand_stonein_2,          EL_QUICKSAND_FULL               },
2269     { Xsand_stonein_3,          EL_QUICKSAND_FULL               },
2270     { Xsand_stonein_4,          EL_QUICKSAND_FULL               },
2271     { Xsand_stonesand_1,        EL_QUICKSAND_FULL               },
2272     { Xsand_stonesand_2,        EL_QUICKSAND_FULL               },
2273     { Xsand_stonesand_3,        EL_QUICKSAND_FULL               },
2274     { Xsand_stonesand_4,        EL_QUICKSAND_FULL               },
2275     { Xsand_stoneout_1,         EL_QUICKSAND_FULL               },
2276     { Xsand_stoneout_2,         EL_QUICKSAND_FULL               },
2277     { Xsand_sandstone_1,        EL_QUICKSAND_FULL               },
2278     { Xsand_sandstone_2,        EL_QUICKSAND_FULL               },
2279     { Xsand_sandstone_3,        EL_QUICKSAND_FULL               },
2280     { Xsand_sandstone_4,        EL_QUICKSAND_FULL               },
2281     { Xplant,                   EL_EMC_PLANT                    },
2282     { Yplant,                   EL_EMC_PLANT                    },
2283     { Xlenses,                  EL_EMC_LENSES                   },
2284     { Xmagnify,                 EL_EMC_MAGNIFY                  },
2285     { Xdripper,                 EL_UNKNOWN                      },
2286     { XdripperB,                EL_UNKNOWN                      },
2287     { Xfake_blank,              EL_INVISIBLE_WALL               },
2288     { Xfake_blankB,             EL_INVISIBLE_WALL               },
2289     { Xfake_grass,              EL_INVISIBLE_SAND               },
2290     { Xfake_grassB,             EL_INVISIBLE_SAND               },
2291     { Xfake_door_1,             EL_EM_GATE_1_GRAY               },
2292     { Xfake_door_2,             EL_EM_GATE_2_GRAY               },
2293     { Xfake_door_3,             EL_EM_GATE_3_GRAY               },
2294     { Xfake_door_4,             EL_EM_GATE_4_GRAY               },
2295     { Xfake_door_5,             EL_EMC_GATE_5_GRAY              },
2296     { Xfake_door_6,             EL_EMC_GATE_6_GRAY              },
2297     { Xfake_door_7,             EL_EMC_GATE_7_GRAY              },
2298     { Xfake_door_8,             EL_EMC_GATE_8_GRAY              },
2299     { Xsteel_1,                 EL_STEELWALL                    },
2300     { Xsteel_2,                 EL_UNKNOWN                      },
2301     { Xsteel_3,                 EL_EMC_STEELWALL_1              },
2302     { Xsteel_4,                 EL_UNKNOWN                      },
2303     { Xwall_1,                  EL_WALL                         },
2304     { Xwall_2,                  EL_UNKNOWN                      },
2305     { Xwall_3,                  EL_UNKNOWN                      },
2306     { Xwall_4,                  EL_UNKNOWN                      },
2307     { Xround_wall_1,            EL_WALL_SLIPPERY                },
2308     { Xround_wall_2,            EL_UNKNOWN                      },
2309     { Xround_wall_3,            EL_UNKNOWN                      },
2310     { Xround_wall_4,            EL_UNKNOWN                      },
2311     { Xdecor_1,                 EL_UNKNOWN                      },
2312     { Xdecor_2,                 EL_EMC_WALL_6                   },
2313     { Xdecor_3,                 EL_EMC_WALL_4                   },
2314     { Xdecor_4,                 EL_EMC_WALL_5                   },
2315     { Xdecor_5,                 EL_EMC_WALL_7                   },
2316     { Xdecor_6,                 EL_EMC_WALL_8                   },
2317     { Xdecor_7,                 EL_UNKNOWN                      },
2318     { Xdecor_8,                 EL_EMC_WALL_1                   },
2319     { Xdecor_9,                 EL_EMC_WALL_2                   },
2320     { Xdecor_10,                EL_EMC_WALL_3                   },
2321     { Xdecor_11,                EL_UNKNOWN                      },
2322     { Xdecor_12,                EL_UNKNOWN                      },
2323     { Xalpha_0,                 EL_CHAR('0')                    },
2324     { Xalpha_1,                 EL_CHAR('1')                    },
2325     { Xalpha_2,                 EL_CHAR('2')                    },
2326     { Xalpha_3,                 EL_CHAR('3')                    },
2327     { Xalpha_4,                 EL_CHAR('4')                    },
2328     { Xalpha_5,                 EL_CHAR('5')                    },
2329     { Xalpha_6,                 EL_CHAR('6')                    },
2330     { Xalpha_7,                 EL_CHAR('7')                    },
2331     { Xalpha_8,                 EL_CHAR('8')                    },
2332     { Xalpha_9,                 EL_CHAR('9')                    },
2333     { Xalpha_excla,             EL_CHAR('!')                    },
2334     { Xalpha_quote,             EL_CHAR('"')                    },
2335     { Xalpha_comma,             EL_CHAR(',')                    },
2336     { Xalpha_minus,             EL_CHAR('-')                    },
2337     { Xalpha_perio,             EL_CHAR('.')                    },
2338     { Xalpha_colon,             EL_CHAR(':')                    },
2339     { Xalpha_quest,             EL_CHAR('?')                    },
2340     { Xalpha_a,                 EL_CHAR('A')                    },
2341     { Xalpha_b,                 EL_CHAR('B')                    },
2342     { Xalpha_c,                 EL_CHAR('C')                    },
2343     { Xalpha_d,                 EL_CHAR('D')                    },
2344     { Xalpha_e,                 EL_CHAR('E')                    },
2345     { Xalpha_f,                 EL_CHAR('F')                    },
2346     { Xalpha_g,                 EL_CHAR('G')                    },
2347     { Xalpha_h,                 EL_CHAR('H')                    },
2348     { Xalpha_i,                 EL_CHAR('I')                    },
2349     { Xalpha_j,                 EL_CHAR('J')                    },
2350     { Xalpha_k,                 EL_CHAR('K')                    },
2351     { Xalpha_l,                 EL_CHAR('L')                    },
2352     { Xalpha_m,                 EL_CHAR('M')                    },
2353     { Xalpha_n,                 EL_CHAR('N')                    },
2354     { Xalpha_o,                 EL_CHAR('O')                    },
2355     { Xalpha_p,                 EL_CHAR('P')                    },
2356     { Xalpha_q,                 EL_CHAR('Q')                    },
2357     { Xalpha_r,                 EL_CHAR('R')                    },
2358     { Xalpha_s,                 EL_CHAR('S')                    },
2359     { Xalpha_t,                 EL_CHAR('T')                    },
2360     { Xalpha_u,                 EL_CHAR('U')                    },
2361     { Xalpha_v,                 EL_CHAR('V')                    },
2362     { Xalpha_w,                 EL_CHAR('W')                    },
2363     { Xalpha_x,                 EL_CHAR('X')                    },
2364     { Xalpha_y,                 EL_CHAR('Y')                    },
2365     { Xalpha_z,                 EL_CHAR('Z')                    },
2366     { Xalpha_arrow_e,           EL_CHAR('>')                    },
2367     { Xalpha_arrow_w,           EL_CHAR('<')                    },
2368     { Xalpha_copyr,             EL_CHAR('©')                    },
2369
2370     { Zplayer,                  EL_PLAYER_1                     },
2371
2372     { ZBORDER,                  EL_EMC_LEVEL_BORDER             },
2373
2374     { -1,                       -1                              }
2375   };
2376
2377   if (!mapping_initialized)
2378   {
2379     int i;
2380
2381     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
2382     for (i = 0; i < TILE_MAX; i++)
2383       mapping_EM_to_RND[i] = EL_UNKNOWN;
2384
2385     for (i = 0; mapping_EM_to_RND_list[i].element_em != -1; i++)
2386       mapping_EM_to_RND[mapping_EM_to_RND_list[i].element_em] =
2387         mapping_EM_to_RND_list[i].element_rnd;
2388
2389     mapping_initialized = TRUE;
2390   }
2391
2392   if (element_em >= 0 && element_em < TILE_MAX)
2393     return mapping_EM_to_RND[element_em];
2394
2395   Error(ERR_WARN, "invalid EM level element %d", element_em);
2396
2397   return EL_UNKNOWN;
2398 }
2399
2400 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
2401 {
2402   struct LevelInfo_EM *level_em = level->native_em_level;
2403   struct LEVEL *lev = level_em->lev;
2404   struct PLAYER *ply1 = level_em->ply1;
2405   struct PLAYER *ply2 = level_em->ply2;
2406   int i, x, y;
2407
2408   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
2409   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
2410
2411   lev->time_initial = level->time;
2412   lev->required_initial = level->gems_needed;
2413
2414   lev->emerald_score    = level->score[SC_EMERALD];
2415   lev->diamond_score    = level->score[SC_DIAMOND];
2416   lev->alien_score      = level->score[SC_ROBOT];
2417   lev->tank_score       = level->score[SC_SPACESHIP];
2418   lev->bug_score        = level->score[SC_BUG];
2419   lev->eater_score      = level->score[SC_YAMYAM];
2420   lev->nut_score        = level->score[SC_NUT];
2421   lev->dynamite_score   = level->score[SC_DYNAMITE];
2422   lev->key_score        = level->score[SC_TIME_BONUS];  /* ??? CHECK THIS */
2423
2424   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2425     for (y = 0; y < 3; y++)
2426       for (x = 0; x < 3; x++)
2427         lev->eater_array[i][y * 3 + x] =
2428           map_element_RND_to_EM(level->yamyam_content[i][x][y]);
2429
2430   lev->ameuba_time              = level->amoeba_speed;
2431   lev->wonderwall_time_initial  = level->time_magic_wall;
2432   lev->wheel_time               = level->time_wheel;
2433
2434   /* first fill the complete playfield with the default border element */
2435   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
2436     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
2437       level_em->cave[x][y] = ZBORDER;
2438
2439   /* then copy the real level contents from level file into the playfield */
2440   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2441   {
2442     int new_element = map_element_RND_to_EM(level->field[x][y]);
2443
2444     level_em->cave[x + 1][y + 1] = new_element;
2445   }
2446
2447   ply1->x_initial = 0;
2448   ply1->y_initial = 0;
2449
2450   ply2->x_initial = 0;
2451   ply2->y_initial = 0;
2452
2453   /* at last, set the two players at their positions in the playfield */
2454   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
2455   {
2456     if (level->field[x][y] == EL_PLAYER_1)
2457     {
2458       ply1->x_initial = x + 1;
2459       ply1->y_initial = y + 1;
2460     }
2461     else if (level->field[x][y] == EL_PLAYER_2)
2462     {
2463       ply2->x_initial = x + 1;
2464       ply2->y_initial = y + 1;
2465     }
2466   }
2467 }
2468
2469 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
2470 {
2471   struct LevelInfo_EM *level_em = level->native_em_level;
2472   struct LEVEL *lev = level_em->lev;
2473   struct PLAYER *ply1 = level_em->ply1;
2474   struct PLAYER *ply2 = level_em->ply2;
2475   int i, x, y;
2476
2477   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
2478   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
2479
2480   level->time = lev->time_initial;
2481   level->gems_needed = lev->required_initial;
2482
2483   sprintf(level->name, "Level %d", level->file_info.nr);
2484
2485   level->score[SC_EMERALD]      = lev->emerald_score;
2486   level->score[SC_DIAMOND]      = lev->diamond_score;
2487   level->score[SC_ROBOT]        = lev->alien_score;
2488   level->score[SC_SPACESHIP]    = lev->tank_score;
2489   level->score[SC_BUG]          = lev->bug_score;
2490   level->score[SC_YAMYAM]       = lev->eater_score;
2491   level->score[SC_NUT]          = lev->nut_score;
2492   level->score[SC_DYNAMITE]     = lev->dynamite_score;
2493   level->score[SC_TIME_BONUS]   = lev->key_score;       /* ??? CHECK THIS */
2494
2495   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
2496
2497   for (i = 0; i < level->num_yamyam_contents; i++)
2498     for (y = 0; y < 3; y++)
2499       for (x = 0; x < 3; x++)
2500         level->yamyam_content[i][x][y] =
2501           map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
2502
2503   level->amoeba_speed           = lev->ameuba_time;
2504   level->time_magic_wall        = lev->wonderwall_time_initial;
2505   level->time_wheel             = lev->wheel_time;
2506
2507   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
2508   {
2509     int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
2510
2511     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
2512       new_element = EL_AMOEBA_DEAD;
2513
2514     level->field[x][y] = new_element;
2515   }
2516
2517   level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
2518   level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
2519 }
2520
2521 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
2522                                      struct LevelFileInfo *level_file_info)
2523 {
2524   if (!LoadNativeLevel_EM(level_file_info->filename))
2525     level->no_valid_file = TRUE;
2526 }
2527
2528 #endif
2529
2530 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
2531 {
2532   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2533     CopyNativeLevel_RND_to_EM(level);
2534 }
2535
2536 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
2537 {
2538   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
2539     CopyNativeLevel_EM_to_RND(level);
2540 }
2541
2542
2543 /* ------------------------------------------------------------------------- */
2544 /* functions for loading SP level                                            */
2545 /* ------------------------------------------------------------------------- */
2546
2547 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2548 #define SP_LEVEL_SIZE                   1536
2549 #define SP_LEVEL_XSIZE                  60
2550 #define SP_LEVEL_YSIZE                  24
2551 #define SP_LEVEL_NAME_LEN               23
2552
2553 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2554                                        int nr)
2555 {
2556   int num_special_ports;
2557   int i, x, y;
2558
2559   /* for details of the Supaplex level format, see Herman Perk's Supaplex
2560      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2561
2562   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2563   for (y = 0; y < SP_LEVEL_YSIZE; y++)
2564   {
2565     for (x = 0; x < SP_LEVEL_XSIZE; x++)
2566     {
2567       int element_old = fgetc(file);
2568       int element_new;
2569
2570       if (element_old <= 0x27)
2571         element_new = getMappedElement(EL_SP_START + element_old);
2572       else if (element_old == 0x28)
2573         element_new = EL_INVISIBLE_WALL;
2574       else
2575       {
2576         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2577         Error(ERR_WARN, "invalid level element %d", element_old);
2578
2579         element_new = EL_UNKNOWN;
2580       }
2581
2582       level->field[x][y] = element_new;
2583     }
2584   }
2585
2586   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
2587
2588   /* initial gravity: 1 == "on", anything else (0) == "off" */
2589   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2590
2591   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
2592
2593   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2594   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2595     level->name[i] = fgetc(file);
2596   level->name[SP_LEVEL_NAME_LEN] = '\0';
2597
2598   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2599   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
2600
2601   /* number of infotrons needed; 0 means that Supaplex will count the total
2602      amount of infotrons in the level and use the low byte of that number
2603      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2604   level->gems_needed = fgetc(file);
2605
2606   /* number of special ("gravity") port entries below (maximum 10 allowed) */
2607   num_special_ports = fgetc(file);
2608
2609   /* database of properties of up to 10 special ports (6 bytes per port) */
2610   for (i = 0; i < 10; i++)
2611   {
2612     int port_location, port_x, port_y, port_element;
2613     int gravity;
2614
2615     /* high and low byte of the location of a special port; if (x, y) are the
2616        coordinates of a port in the field and (0, 0) is the top-left corner,
2617        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2618        of what may be expected: Supaplex works with a game field in memory
2619        which is 2 bytes per tile) */
2620     port_location = getFile16BitBE(file);
2621
2622     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2623     gravity = fgetc(file);
2624
2625     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2626     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
2627
2628     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2629     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
2630
2631     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
2632
2633     if (i >= num_special_ports)
2634       continue;
2635
2636     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2637     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2638
2639     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2640         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2641     {
2642       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2643             port_x, port_y);
2644
2645       continue;
2646     }
2647
2648     port_element = level->field[port_x][port_y];
2649
2650     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2651         port_element > EL_SP_GRAVITY_PORT_UP)
2652     {
2653       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2654
2655       continue;
2656     }
2657
2658     /* change previous (wrong) gravity inverting special port to either
2659        gravity enabling special port or gravity disabling special port */
2660     level->field[port_x][port_y] +=
2661       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2662        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2663   }
2664
2665   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
2666
2667   /* change special gravity ports without database entries to normal ports */
2668   for (y = 0; y < SP_LEVEL_YSIZE; y++)
2669     for (x = 0; x < SP_LEVEL_XSIZE; x++)
2670       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2671           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2672         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2673
2674   /* auto-determine number of infotrons if it was stored as "0" -- see above */
2675   if (level->gems_needed == 0)
2676   {
2677     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2678       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2679         if (level->field[x][y] == EL_SP_INFOTRON)
2680           level->gems_needed++;
2681
2682     level->gems_needed &= 0xff;         /* only use low byte -- see above */
2683   }
2684
2685   level->fieldx = SP_LEVEL_XSIZE;
2686   level->fieldy = SP_LEVEL_YSIZE;
2687
2688   level->time = 0;                      /* no time limit */
2689   level->amoeba_speed = 0;
2690   level->time_magic_wall = 0;
2691   level->time_wheel = 0;
2692   level->amoeba_content = EL_EMPTY;
2693
2694   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2695     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
2696
2697   /* there are no yamyams in supaplex levels */
2698   for (i = 0; i < level->num_yamyam_contents; i++)
2699     for (y = 0; y < 3; y++)
2700       for (x = 0; x < 3; x++)
2701         level->yamyam_content[i][x][y] = EL_EMPTY;
2702 }
2703
2704 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2705                                      struct LevelFileInfo *level_file_info)
2706 {
2707   char *filename = level_file_info->filename;
2708   FILE *file;
2709   int nr = level_file_info->nr - leveldir_current->first_level;
2710   int i, l, x, y;
2711   char name_first, name_last;
2712   struct LevelInfo multipart_level;
2713   int multipart_xpos, multipart_ypos;
2714   boolean is_multipart_level;
2715   boolean is_first_part;
2716   boolean reading_multipart_level = FALSE;
2717   boolean use_empty_level = FALSE;
2718
2719   if (!(file = fopen(filename, MODE_READ)))
2720   {
2721     level->no_valid_file = TRUE;
2722
2723     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2724
2725     return;
2726   }
2727
2728   /* position file stream to the requested level inside the level package */
2729   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2730   {
2731     level->no_valid_file = TRUE;
2732
2733     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2734
2735     return;
2736   }
2737
2738   /* there exist Supaplex level package files with multi-part levels which
2739      can be detected as follows: instead of leading and trailing dashes ('-')
2740      to pad the level name, they have leading and trailing numbers which are
2741      the x and y coordinations of the current part of the multi-part level;
2742      if there are '?' characters instead of numbers on the left or right side
2743      of the level name, the multi-part level consists of only horizontal or
2744      vertical parts */
2745
2746   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2747   {
2748     LoadLevelFromFileStream_SP(file, level, l);
2749
2750     /* check if this level is a part of a bigger multi-part level */
2751
2752     name_first = level->name[0];
2753     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
2754
2755     is_multipart_level =
2756       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2757        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
2758
2759     is_first_part =
2760       ((name_first == '?' || name_first == '1') &&
2761        (name_last  == '?' || name_last  == '1'));
2762
2763     /* correct leading multipart level meta information in level name */
2764     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2765       level->name[i] = '-';
2766
2767     /* correct trailing multipart level meta information in level name */
2768     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2769       level->name[i] = '-';
2770
2771     /* ---------- check for normal single level ---------- */
2772
2773     if (!reading_multipart_level && !is_multipart_level)
2774     {
2775       /* the current level is simply a normal single-part level, and we are
2776          not reading a multi-part level yet, so return the level as it is */
2777
2778       break;
2779     }
2780
2781     /* ---------- check for empty level (unused multi-part) ---------- */
2782
2783     if (!reading_multipart_level && is_multipart_level && !is_first_part)
2784     {
2785       /* this is a part of a multi-part level, but not the first part
2786          (and we are not already reading parts of a multi-part level);
2787          in this case, use an empty level instead of the single part */
2788
2789       use_empty_level = TRUE;
2790
2791       break;
2792     }
2793
2794     /* ---------- check for finished multi-part level ---------- */
2795
2796     if (reading_multipart_level &&
2797         (!is_multipart_level ||
2798          strcmp(level->name, multipart_level.name) != 0))
2799     {
2800       /* we are already reading parts of a multi-part level, but this level is
2801          either not a multi-part level, or a part of a different multi-part
2802          level; in both cases, the multi-part level seems to be complete */
2803
2804       break;
2805     }
2806
2807     /* ---------- here we have one part of a multi-part level ---------- */
2808
2809     reading_multipart_level = TRUE;
2810
2811     if (is_first_part)  /* start with first part of new multi-part level */
2812     {
2813       /* copy level info structure from first part */
2814       multipart_level = *level;
2815
2816       /* clear playfield of new multi-part level */
2817       for (y = 0; y < MAX_LEV_FIELDY; y++)
2818         for (x = 0; x < MAX_LEV_FIELDX; x++)
2819           multipart_level.field[x][y] = EL_EMPTY;
2820     }
2821
2822     if (name_first == '?')
2823       name_first = '1';
2824     if (name_last == '?')
2825       name_last = '1';
2826
2827     multipart_xpos = (int)(name_first - '0');
2828     multipart_ypos = (int)(name_last  - '0');
2829
2830 #if 0
2831     printf("----------> part (%d/%d) of multi-part level '%s'\n",
2832            multipart_xpos, multipart_ypos, multipart_level.name);
2833 #endif
2834
2835     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2836         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2837     {
2838       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2839
2840       break;
2841     }
2842
2843     multipart_level.fieldx = MAX(multipart_level.fieldx,
2844                                  multipart_xpos * SP_LEVEL_XSIZE);
2845     multipart_level.fieldy = MAX(multipart_level.fieldy,
2846                                  multipart_ypos * SP_LEVEL_YSIZE);
2847
2848     /* copy level part at the right position of multi-part level */
2849     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2850     {
2851       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2852       {
2853         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2854         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2855
2856         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2857       }
2858     }
2859   }
2860
2861   fclose(file);
2862
2863   if (use_empty_level)
2864   {
2865     setLevelInfoToDefaults(level);
2866
2867     level->fieldx = SP_LEVEL_XSIZE;
2868     level->fieldy = SP_LEVEL_YSIZE;
2869
2870     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2871       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2872         level->field[x][y] = EL_EMPTY;
2873
2874     strcpy(level->name, "-------- EMPTY --------");
2875
2876     Error(ERR_WARN, "single part of multi-part level -- using empty level");
2877   }
2878
2879   if (reading_multipart_level)
2880     *level = multipart_level;
2881 }
2882
2883 /* ------------------------------------------------------------------------- */
2884 /* functions for loading generic level                                       */
2885 /* ------------------------------------------------------------------------- */
2886
2887 void LoadLevelFromFileInfo(struct LevelInfo *level,
2888                            struct LevelFileInfo *level_file_info)
2889 {
2890   /* always start with reliable default values */
2891   setLevelInfoToDefaults(level);
2892
2893   switch (level_file_info->type)
2894   {
2895     case LEVEL_FILE_TYPE_RND:
2896       LoadLevelFromFileInfo_RND(level, level_file_info);
2897       break;
2898
2899     case LEVEL_FILE_TYPE_EM:
2900       LoadLevelFromFileInfo_EM(level, level_file_info);
2901       level->game_engine_type = GAME_ENGINE_TYPE_EM;
2902       break;
2903
2904     case LEVEL_FILE_TYPE_SP:
2905       LoadLevelFromFileInfo_SP(level, level_file_info);
2906       break;
2907
2908     default:
2909       LoadLevelFromFileInfo_RND(level, level_file_info);
2910       break;
2911   }
2912
2913   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2914     level->game_engine_type = GAME_ENGINE_TYPE_RND;
2915
2916   CopyNativeLevel_Native_to_RND(level);
2917 }
2918
2919 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2920 {
2921   static struct LevelFileInfo level_file_info;
2922
2923   /* always start with reliable default values */
2924   setFileInfoToDefaults(&level_file_info);
2925
2926   level_file_info.nr = 0;                       /* unknown level number */
2927   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
2928   level_file_info.filename = filename;
2929
2930   LoadLevelFromFileInfo(level, &level_file_info);
2931 }
2932
2933 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2934 {
2935   if (leveldir_current == NULL)         /* only when dumping level */
2936     return;
2937
2938 #if 0
2939   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2940 #endif
2941
2942   /* determine correct game engine version of current level */
2943 #if 1
2944   if (!leveldir_current->latest_engine)
2945 #else
2946   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2947       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2948       IS_LEVELCLASS_UNDEFINED(leveldir_current))
2949 #endif
2950   {
2951 #if 0
2952     printf("\n::: This level is private or contributed: '%s'\n", filename);
2953 #endif
2954
2955 #if 0
2956     printf("\n::: Use the stored game engine version for this level\n");
2957 #endif
2958
2959     /* For all levels which are not forced to use the latest game engine
2960        version (normally user contributed, private and undefined levels),
2961        use the version of the game engine the levels were created for.
2962
2963        Since 2.0.1, the game engine version is now directly stored
2964        in the level file (chunk "VERS"), so there is no need anymore
2965        to set the game version from the file version (except for old,
2966        pre-2.0 levels, where the game version is still taken from the
2967        file format version used to store the level -- see above). */
2968
2969 #if 1
2970     /* player was faster than enemies in 1.0.0 and before */
2971     if (level->file_version == FILE_VERSION_1_0)
2972       level->double_speed = TRUE;
2973 #else
2974     /* do some special adjustments to support older level versions */
2975     if (level->file_version == FILE_VERSION_1_0)
2976     {
2977       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2978       Error(ERR_WARN, "using high speed movement for player");
2979
2980       /* player was faster than monsters in (pre-)1.0 levels */
2981       level->double_speed = TRUE;
2982     }
2983 #endif
2984
2985     /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2986     if (level->game_version == VERSION_IDENT(2,0,1,0))
2987       level->em_slippery_gems = TRUE;
2988
2989     /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2990     if (level->game_version < VERSION_IDENT(2,2,0,0))
2991       level->use_spring_bug = TRUE;
2992
2993     /* only few elements were able to actively move into acid before 3.1.0 */
2994     if (level->game_version < VERSION_IDENT(3,1,0,0))
2995     {
2996       int i, j;
2997
2998       level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2999       level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
3000
3001       setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
3002       setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
3003       setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
3004       setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
3005
3006       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3007         SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
3008
3009       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010       {
3011         int element = EL_CUSTOM_START + i;
3012         struct ElementInfo *ei = &element_info[element];
3013
3014         for (j = 0; j < ei->num_change_pages; j++)
3015         {
3016           struct ElementChangeInfo *change = &ei->change_page[j];
3017
3018           change->trigger_player = CH_PLAYER_ANY;
3019           change->trigger_page = CH_PAGE_ANY;
3020         }
3021       }
3022     }
3023
3024 #if 1   /* USE_NEW_BLOCK_STYLE */
3025     /* blocking the last field when moving was corrected in version 3.1.1 */
3026     if (level->game_version < VERSION_IDENT(3,1,1,0))
3027     {
3028 #if 0
3029       printf("::: %d\n", level->block_last_field);
3030 #endif
3031
3032       /* even "not blocking" was blocking the last field for one frame */
3033       level->block_delay    = (level->block_last_field    ? 7 : 1);
3034       level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
3035
3036       level->block_last_field = TRUE;
3037       level->sp_block_last_field = TRUE;
3038     }
3039 #endif
3040   }
3041   else          /* always use the latest game engine version */
3042   {
3043 #if 0
3044     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
3045            leveldir_current->sort_priority, filename);
3046 #endif
3047
3048 #if 0
3049     printf("\n::: Use latest game engine version for this level.\n");
3050 #endif
3051
3052     /* For all levels which are forced to use the latest game engine version
3053        (normally all but user contributed, private and undefined levels), set
3054        the game engine version to the actual version; this allows for actual
3055        corrections in the game engine to take effect for existing, converted
3056        levels (from "classic" or other existing games) to make the emulation
3057        of the corresponding game more accurate, while (hopefully) not breaking
3058        existing levels created from other players. */
3059
3060 #if 0
3061     printf("::: changing engine from %d to %d\n",
3062            level->game_version, GAME_VERSION_ACTUAL);
3063 #endif
3064
3065     level->game_version = GAME_VERSION_ACTUAL;
3066
3067     /* Set special EM style gems behaviour: EM style gems slip down from
3068        normal, steel and growing wall. As this is a more fundamental change,
3069        it seems better to set the default behaviour to "off" (as it is more
3070        natural) and make it configurable in the level editor (as a property
3071        of gem style elements). Already existing converted levels (neither
3072        private nor contributed levels) are changed to the new behaviour. */
3073
3074     if (level->file_version < FILE_VERSION_2_0)
3075       level->em_slippery_gems = TRUE;
3076   }
3077
3078 #if 0
3079   printf("::: => %d\n", level->game_version);
3080 #endif
3081 }
3082
3083 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
3084 {
3085   int i, j, x, y;
3086
3087   /* map custom element change events that have changed in newer versions
3088      (these following values were accidentally changed in version 3.0.1) */
3089   if (level->game_version <= VERSION_IDENT(3,0,0,0))
3090   {
3091     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3092     {
3093       int element = EL_CUSTOM_START + i;
3094
3095       /* order of checking and copying events to be mapped is important */
3096       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
3097       {
3098         if (HAS_CHANGE_EVENT(element, j - 2))
3099         {
3100           SET_CHANGE_EVENT(element, j - 2, FALSE);
3101           SET_CHANGE_EVENT(element, j, TRUE);
3102         }
3103       }
3104
3105       /* order of checking and copying events to be mapped is important */
3106       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
3107       {
3108         if (HAS_CHANGE_EVENT(element, j - 1))
3109         {
3110           SET_CHANGE_EVENT(element, j - 1, FALSE);
3111           SET_CHANGE_EVENT(element, j, TRUE);
3112         }
3113       }
3114     }
3115   }
3116
3117   /* some custom element change events get mapped since version 3.0.3 */
3118   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3119   {
3120     int element = EL_CUSTOM_START + i;
3121
3122     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
3123         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
3124     {
3125       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
3126       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
3127
3128       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
3129     }
3130   }
3131
3132   /* initialize "can_change" field for old levels with only one change page */
3133   if (level->game_version <= VERSION_IDENT(3,0,2,0))
3134   {
3135     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3136     {
3137       int element = EL_CUSTOM_START + i;
3138
3139       if (CAN_CHANGE(element))
3140         element_info[element].change->can_change = TRUE;
3141     }
3142   }
3143
3144   /* correct custom element values (for old levels without these options) */
3145   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3146   {
3147     int element = EL_CUSTOM_START + i;
3148     struct ElementInfo *ei = &element_info[element];
3149
3150     if (ei->access_direction == MV_NO_MOVING)
3151       ei->access_direction = MV_ALL_DIRECTIONS;
3152
3153     for (j = 0; j < ei->num_change_pages; j++)
3154     {
3155       struct ElementChangeInfo *change = &ei->change_page[j];
3156
3157       if (change->trigger_side == CH_SIDE_NONE)
3158         change->trigger_side = CH_SIDE_ANY;
3159     }
3160   }
3161
3162   /* initialize "can_explode" field for old levels which did not store this */
3163   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
3164   if (level->game_version <= VERSION_IDENT(3,1,0,0))
3165   {
3166     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3167     {
3168       int element = EL_CUSTOM_START + i;
3169
3170       if (EXPLODES_1X1_OLD(element))
3171         element_info[element].explosion_type = EXPLODES_1X1;
3172
3173       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
3174                                              EXPLODES_SMASHED(element) ||
3175                                              EXPLODES_IMPACT(element)));
3176     }
3177   }
3178
3179   /* correct previously hard-coded move delay values for maze runner style */
3180   if (level->game_version < VERSION_IDENT(3,1,1,0))
3181   {
3182     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3183     {
3184       int element = EL_CUSTOM_START + i;
3185
3186       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3187       {
3188         /* previously hard-coded and therefore ignored */
3189         element_info[element].move_delay_fixed = 9;
3190         element_info[element].move_delay_random = 0;
3191       }
3192     }
3193   }
3194
3195 #if 0
3196   /* set default push delay values (corrected since version 3.0.7-1) */
3197   if (level->game_version < VERSION_IDENT(3,0,7,1))
3198   {
3199     game.default_push_delay_fixed = 2;
3200     game.default_push_delay_random = 8;
3201   }
3202   else
3203   {
3204     game.default_push_delay_fixed = 8;
3205     game.default_push_delay_random = 8;
3206   }
3207
3208   /* set uninitialized push delay values of custom elements in older levels */
3209   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3210   {
3211     int element = EL_CUSTOM_START + i;
3212
3213     if (element_info[element].push_delay_fixed == -1)
3214       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
3215     if (element_info[element].push_delay_random == -1)
3216       element_info[element].push_delay_random = game.default_push_delay_random;
3217   }
3218 #endif
3219
3220   /* map elements that have changed in newer versions */
3221   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3222                                                     level->game_version);
3223   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3224     for (x = 0; x < 3; x++)
3225       for (y = 0; y < 3; y++)
3226         level->yamyam_content[i][x][y] =
3227           getMappedElementByVersion(level->yamyam_content[i][x][y],
3228                                     level->game_version);
3229
3230   /* initialize element properties for level editor etc. */
3231   InitElementPropertiesEngine(level->game_version);
3232 }
3233
3234 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3235 {
3236   int x, y;
3237
3238   /* map elements that have changed in newer versions */
3239   for (y = 0; y < level->fieldy; y++)
3240   {
3241     for (x = 0; x < level->fieldx; x++)
3242     {
3243       int element = level->field[x][y];
3244
3245 #if 1
3246       element = getMappedElementByVersion(element, level->game_version);
3247 #else
3248       if (level->game_version <= VERSION_IDENT(2,2,0,0))
3249       {
3250         /* map game font elements */
3251         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3252                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3253                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3254                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3255       }
3256
3257       if (level->game_version < VERSION_IDENT(3,0,0,0))
3258       {
3259         /* map Supaplex gravity tube elements */
3260         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3261                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3262                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3263                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3264                    element);
3265       }
3266 #endif
3267
3268       level->field[x][y] = element;
3269     }
3270   }
3271
3272   /* copy elements to runtime playfield array */
3273   for (x = 0; x < MAX_LEV_FIELDX; x++)
3274     for (y = 0; y < MAX_LEV_FIELDY; y++)
3275       Feld[x][y] = level->field[x][y];
3276
3277   /* initialize level size variables for faster access */
3278   lev_fieldx = level->fieldx;
3279   lev_fieldy = level->fieldy;
3280
3281   /* determine border element for this level */
3282   SetBorderElement();
3283 }
3284
3285 void LoadLevelTemplate(int nr)
3286 {
3287 #if 1
3288   char *filename;
3289
3290   setLevelFileInfo(&level_template.file_info, nr);
3291   filename = level_template.file_info.filename;
3292
3293   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3294
3295 #else
3296
3297 #if 1
3298   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3299   char *filename = level_file_info->filename;
3300
3301   LoadLevelFromFileInfo(&level_template, level_file_info);
3302 #else
3303   char *filename = getDefaultLevelFilename(nr);
3304
3305   LoadLevelFromFilename_RND(&level_template, filename);
3306 #endif
3307 #endif
3308
3309 #if 1
3310   LoadLevel_InitVersion(&level_template, filename);
3311   LoadLevel_InitElements(&level_template, filename);
3312 #else
3313   LoadLevel_InitVersion(&level, filename);
3314   LoadLevel_InitElements(&level, filename);
3315 #endif
3316
3317   ActivateLevelTemplate();
3318 }
3319
3320 void LoadLevel(int nr)
3321 {
3322 #if 1
3323   char *filename;
3324
3325   setLevelFileInfo(&level.file_info, nr);
3326   filename = level.file_info.filename;
3327
3328   LoadLevelFromFileInfo(&level, &level.file_info);
3329
3330 #else
3331
3332 #if 1
3333   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3334   char *filename = level_file_info->filename;
3335
3336   LoadLevelFromFileInfo(&level, level_file_info);
3337 #else
3338   char *filename = getLevelFilename(nr);
3339
3340   LoadLevelFromFilename_RND(&level, filename);
3341 #endif
3342 #endif
3343
3344   if (level.use_custom_template)
3345     LoadLevelTemplate(-1);
3346
3347 #if 1
3348   LoadLevel_InitVersion(&level, filename);
3349   LoadLevel_InitElements(&level, filename);
3350   LoadLevel_InitPlayfield(&level, filename);
3351 #else
3352   LoadLevel_InitLevel(&level, filename);
3353 #endif
3354 }
3355
3356 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3357 {
3358   putFileVersion(file, level->file_version);
3359   putFileVersion(file, level->game_version);
3360 }
3361
3362 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3363 {
3364   int i, x, y;
3365
3366   putFile8Bit(file, level->fieldx);
3367   putFile8Bit(file, level->fieldy);
3368
3369   putFile16BitBE(file, level->time);
3370   putFile16BitBE(file, level->gems_needed);
3371
3372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3373     putFile8Bit(file, level->name[i]);
3374
3375   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3376     putFile8Bit(file, level->score[i]);
3377
3378   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3379     for (y = 0; y < 3; y++)
3380       for (x = 0; x < 3; x++)
3381         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3382                            level->yamyam_content[i][x][y]));
3383   putFile8Bit(file, level->amoeba_speed);
3384   putFile8Bit(file, level->time_magic_wall);
3385   putFile8Bit(file, level->time_wheel);
3386   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3387                      level->amoeba_content));
3388   putFile8Bit(file, (level->double_speed ? 1 : 0));
3389   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3390   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3391   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3392
3393   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3394
3395   putFile8Bit(file, (level->block_last_field ? 1 : 0));
3396   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3397   putFile32BitBE(file, level->can_move_into_acid_bits);
3398   putFile8Bit(file, level->dont_collide_with_bits);
3399
3400   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3401   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3402
3403   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3404   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3405   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3406
3407   putFile8Bit(file, level->game_engine_type);
3408
3409   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3410 }
3411
3412 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3413 {
3414   int i;
3415
3416   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3417     putFile8Bit(file, level->author[i]);
3418 }
3419
3420 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3421 {
3422   int x, y;
3423
3424   for (y = 0; y < level->fieldy; y++) 
3425     for (x = 0; x < level->fieldx; x++) 
3426       if (level->encoding_16bit_field)
3427         putFile16BitBE(file, level->field[x][y]);
3428       else
3429         putFile8Bit(file, level->field[x][y]);
3430 }
3431
3432 #if 0
3433 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3434 {
3435   int i, x, y;
3436
3437   putFile8Bit(file, EL_YAMYAM);
3438   putFile8Bit(file, level->num_yamyam_contents);
3439   putFile8Bit(file, 0);
3440   putFile8Bit(file, 0);
3441
3442   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3443     for (y = 0; y < 3; y++)
3444       for (x = 0; x < 3; x++)
3445         if (level->encoding_16bit_field)
3446           putFile16BitBE(file, level->yamyam_content[i][x][y]);
3447         else
3448           putFile8Bit(file, level->yamyam_content[i][x][y]);
3449 }
3450 #endif
3451
3452 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3453 {
3454   int i, x, y;
3455   int num_contents, content_xsize, content_ysize;
3456   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3457
3458   if (element == EL_YAMYAM)
3459   {
3460     num_contents = level->num_yamyam_contents;
3461     content_xsize = 3;
3462     content_ysize = 3;
3463
3464     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3465       for (y = 0; y < 3; y++)
3466         for (x = 0; x < 3; x++)
3467           content_array[i][x][y] = level->yamyam_content[i][x][y];
3468   }
3469   else if (element == EL_BD_AMOEBA)
3470   {
3471     num_contents = 1;
3472     content_xsize = 1;
3473     content_ysize = 1;
3474
3475     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3476       for (y = 0; y < 3; y++)
3477         for (x = 0; x < 3; x++)
3478           content_array[i][x][y] = EL_EMPTY;
3479     content_array[0][0][0] = level->amoeba_content;
3480   }
3481   else
3482   {
3483     /* chunk header already written -- write empty chunk data */
3484     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3485
3486     Error(ERR_WARN, "cannot save content for element '%d'", element);
3487     return;
3488   }
3489
3490   putFile16BitBE(file, element);
3491   putFile8Bit(file, num_contents);
3492   putFile8Bit(file, content_xsize);
3493   putFile8Bit(file, content_ysize);
3494
3495   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3496
3497   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3498     for (y = 0; y < 3; y++)
3499       for (x = 0; x < 3; x++)
3500         putFile16BitBE(file, content_array[i][x][y]);
3501 }
3502
3503 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3504 {
3505   int i;
3506   int envelope_nr = element - EL_ENVELOPE_1;
3507   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3508
3509   putFile16BitBE(file, element);
3510   putFile16BitBE(file, envelope_len);
3511   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3512   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3513
3514   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3515
3516   for (i = 0; i < envelope_len; i++)
3517     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3518 }
3519
3520 #if 0
3521 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3522                            int num_changed_custom_elements)
3523 {
3524   int i, check = 0;
3525
3526   putFile16BitBE(file, num_changed_custom_elements);
3527
3528   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3529   {
3530     int element = EL_CUSTOM_START + i;
3531
3532     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3533     {
3534       if (check < num_changed_custom_elements)
3535       {
3536         putFile16BitBE(file, element);
3537         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3538       }
3539
3540       check++;
3541     }
3542   }
3543
3544   if (check != num_changed_custom_elements)     /* should not happen */
3545     Error(ERR_WARN, "inconsistent number of custom element properties");
3546 }
3547 #endif
3548
3549 #if 0
3550 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3551                            int num_changed_custom_elements)
3552 {
3553   int i, check = 0;
3554
3555   putFile16BitBE(file, num_changed_custom_elements);
3556
3557   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3558   {
3559     int element = EL_CUSTOM_START + i;
3560
3561     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3562     {
3563       if (check < num_changed_custom_elements)
3564       {
3565         putFile16BitBE(file, element);
3566         putFile16BitBE(file, element_info[element].change->target_element);
3567       }
3568
3569       check++;
3570     }
3571   }
3572
3573   if (check != num_changed_custom_elements)     /* should not happen */
3574     Error(ERR_WARN, "inconsistent number of custom target elements");
3575 }
3576 #endif
3577
3578 #if 0
3579 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3580                            int num_changed_custom_elements)
3581 {
3582   int i, j, x, y, check = 0;
3583
3584   putFile16BitBE(file, num_changed_custom_elements);
3585
3586   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3587   {
3588     int element = EL_CUSTOM_START + i;
3589
3590     if (element_info[element].modified_settings)
3591     {
3592       if (check < num_changed_custom_elements)
3593       {
3594         putFile16BitBE(file, element);
3595
3596         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3597           putFile8Bit(file, element_info[element].description[j]);
3598
3599         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3600
3601         /* some free bytes for future properties and padding */
3602         WriteUnusedBytesToFile(file, 7);
3603
3604         putFile8Bit(file, element_info[element].use_gfx_element);
3605         putFile16BitBE(file, element_info[element].gfx_element);
3606
3607         putFile8Bit(file, element_info[element].collect_score);
3608         putFile8Bit(file, element_info[element].collect_count);
3609
3610         putFile16BitBE(file, element_info[element].push_delay_fixed);
3611         putFile16BitBE(file, element_info[element].push_delay_random);
3612         putFile16BitBE(file, element_info[element].move_delay_fixed);
3613         putFile16BitBE(file, element_info[element].move_delay_random);
3614
3615         putFile16BitBE(file, element_info[element].move_pattern);
3616         putFile8Bit(file, element_info[element].move_direction_initial);
3617         putFile8Bit(file, element_info[element].move_stepsize);
3618
3619         for (y = 0; y < 3; y++)
3620           for (x = 0; x < 3; x++)
3621             putFile16BitBE(file, element_info[element].content[x][y]);
3622
3623         putFile32BitBE(file, element_info[element].change->events);
3624
3625         putFile16BitBE(file, element_info[element].change->target_element);
3626
3627         putFile16BitBE(file, element_info[element].change->delay_fixed);
3628         putFile16BitBE(file, element_info[element].change->delay_random);
3629         putFile16BitBE(file, element_info[element].change->delay_frames);
3630
3631         putFile16BitBE(file, element_info[element].change->trigger_element);
3632
3633         putFile8Bit(file, element_info[element].change->explode);
3634         putFile8Bit(file, element_info[element].change->use_target_content);
3635         putFile8Bit(file, element_info[element].change->only_if_complete);
3636         putFile8Bit(file, element_info[element].change->use_random_replace);
3637
3638         putFile8Bit(file, element_info[element].change->random_percentage);
3639         putFile8Bit(file, element_info[element].change->replace_when);
3640
3641         for (y = 0; y < 3; y++)
3642           for (x = 0; x < 3; x++)
3643             putFile16BitBE(file, element_info[element].change->content[x][y]);
3644
3645         putFile8Bit(file, element_info[element].slippery_type);
3646
3647         /* some free bytes for future properties and padding */
3648         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3649       }
3650
3651       check++;
3652     }
3653   }
3654
3655   if (check != num_changed_custom_elements)     /* should not happen */
3656     Error(ERR_WARN, "inconsistent number of custom element properties");
3657 }
3658 #endif
3659
3660 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3661 {
3662   struct ElementInfo *ei = &element_info[element];
3663   int i, x, y;
3664
3665   putFile16BitBE(file, element);
3666
3667   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3668     putFile8Bit(file, ei->description[i]);
3669
3670   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3671   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
3672
3673   putFile8Bit(file, ei->num_change_pages);
3674
3675   /* some free bytes for future base property values and padding */
3676   WriteUnusedBytesToFile(file, 5);
3677
3678   /* write custom property values */
3679
3680   putFile8Bit(file, ei->use_gfx_element);
3681   putFile16BitBE(file, ei->gfx_element);
3682
3683   putFile8Bit(file, ei->collect_score);
3684   putFile8Bit(file, ei->collect_count);
3685
3686   putFile8Bit(file, ei->drop_delay_fixed);
3687   putFile8Bit(file, ei->push_delay_fixed);
3688   putFile8Bit(file, ei->drop_delay_random);
3689   putFile8Bit(file, ei->push_delay_random);
3690   putFile16BitBE(file, ei->move_delay_fixed);
3691   putFile16BitBE(file, ei->move_delay_random);
3692
3693   /* bits 0 - 15 of "move_pattern" ... */
3694   putFile16BitBE(file, ei->move_pattern & 0xffff);
3695   putFile8Bit(file, ei->move_direction_initial);
3696   putFile8Bit(file, ei->move_stepsize);
3697
3698   putFile8Bit(file, ei->slippery_type);
3699
3700   for (y = 0; y < 3; y++)
3701     for (x = 0; x < 3; x++)
3702       putFile16BitBE(file, ei->content[x][y]);
3703
3704   putFile16BitBE(file, ei->move_enter_element);
3705   putFile16BitBE(file, ei->move_leave_element);
3706   putFile8Bit(file, ei->move_leave_type);
3707
3708   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3709   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3710
3711   putFile8Bit(file, ei->access_direction);
3712
3713   putFile8Bit(file, ei->explosion_delay);
3714   putFile8Bit(file, ei->ignition_delay);
3715   putFile8Bit(file, ei->explosion_type);
3716
3717   /* some free bytes for future custom property values and padding */
3718   WriteUnusedBytesToFile(file, 1);
3719
3720   /* write change property values */
3721
3722   for (i = 0; i < ei->num_change_pages; i++)
3723   {
3724     struct ElementChangeInfo *change = &ei->change_page[i];
3725
3726     putFile32BitBE(file, change->events);
3727
3728     putFile16BitBE(file, change->target_element);
3729
3730     putFile16BitBE(file, change->delay_fixed);
3731     putFile16BitBE(file, change->delay_random);
3732     putFile16BitBE(file, change->delay_frames);
3733
3734     putFile16BitBE(file, change->trigger_element);
3735
3736     putFile8Bit(file, change->explode);
3737     putFile8Bit(file, change->use_target_content);
3738     putFile8Bit(file, change->only_if_complete);
3739     putFile8Bit(file, change->use_random_replace);
3740
3741     putFile8Bit(file, change->random_percentage);
3742     putFile8Bit(file, change->replace_when);
3743
3744     for (y = 0; y < 3; y++)
3745       for (x = 0; x < 3; x++)
3746         putFile16BitBE(file, change->target_content[x][y]);
3747
3748     putFile8Bit(file, change->can_change);
3749
3750     putFile8Bit(file, change->trigger_side);
3751
3752 #if 1
3753     putFile8Bit(file, change->trigger_player);
3754     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3755                        log_2(change->trigger_page)));
3756
3757     /* some free bytes for future change property values and padding */
3758     WriteUnusedBytesToFile(file, 6);
3759
3760 #else
3761
3762     /* some free bytes for future change property values and padding */
3763     WriteUnusedBytesToFile(file, 8);
3764 #endif
3765   }
3766 }
3767
3768 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3769 {
3770   struct ElementInfo *ei = &element_info[element];
3771   struct ElementGroupInfo *group = ei->group;
3772   int i;
3773
3774   putFile16BitBE(file, element);
3775
3776   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3777     putFile8Bit(file, ei->description[i]);
3778
3779   putFile8Bit(file, group->num_elements);
3780
3781   putFile8Bit(file, ei->use_gfx_element);
3782   putFile16BitBE(file, ei->gfx_element);
3783
3784   putFile8Bit(file, group->choice_mode);
3785
3786   /* some free bytes for future values and padding */
3787   WriteUnusedBytesToFile(file, 3);
3788
3789   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3790     putFile16BitBE(file, group->element[i]);
3791 }
3792
3793 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3794 {
3795   int body_chunk_size;
3796   int i, x, y;
3797   FILE *file;
3798
3799   if (!(file = fopen(filename, MODE_WRITE)))
3800   {
3801     Error(ERR_WARN, "cannot save level file '%s'", filename);
3802     return;
3803   }
3804
3805   level->file_version = FILE_VERSION_ACTUAL;
3806   level->game_version = GAME_VERSION_ACTUAL;
3807
3808   /* check level field for 16-bit elements */
3809   level->encoding_16bit_field = FALSE;
3810   for (y = 0; y < level->fieldy; y++) 
3811     for (x = 0; x < level->fieldx; x++) 
3812       if (level->field[x][y] > 255)
3813         level->encoding_16bit_field = TRUE;
3814
3815   /* check yamyam content for 16-bit elements */
3816   level->encoding_16bit_yamyam = FALSE;
3817   for (i = 0; i < level->num_yamyam_contents; i++)
3818     for (y = 0; y < 3; y++)
3819       for (x = 0; x < 3; x++)
3820         if (level->yamyam_content[i][x][y] > 255)
3821           level->encoding_16bit_yamyam = TRUE;
3822
3823   /* check amoeba content for 16-bit elements */
3824   level->encoding_16bit_amoeba = FALSE;
3825   if (level->amoeba_content > 255)
3826     level->encoding_16bit_amoeba = TRUE;
3827
3828   /* calculate size of "BODY" chunk */
3829   body_chunk_size =
3830     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3831
3832   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3833   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3834
3835   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3836   SaveLevel_VERS(file, level);
3837
3838   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3839   SaveLevel_HEAD(file, level);
3840
3841   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3842   SaveLevel_AUTH(file, level);
3843
3844   putFileChunkBE(file, "BODY", body_chunk_size);
3845   SaveLevel_BODY(file, level);
3846
3847   if (level->encoding_16bit_yamyam ||
3848       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3849   {
3850     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3851     SaveLevel_CNT2(file, level, EL_YAMYAM);
3852   }
3853
3854   if (level->encoding_16bit_amoeba)
3855   {
3856     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3857     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3858   }
3859
3860   /* check for envelope content */
3861   for (i = 0; i < 4; i++)
3862   {
3863     if (strlen(level->envelope_text[i]) > 0)
3864     {
3865       int envelope_len = strlen(level->envelope_text[i]) + 1;
3866
3867       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3868       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3869     }
3870   }
3871
3872   /* check for non-default custom elements (unless using template level) */
3873   if (!level->use_custom_template)
3874   {
3875     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3876     {
3877       int element = EL_CUSTOM_START + i;
3878
3879       if (element_info[element].modified_settings)
3880       {
3881         int num_change_pages = element_info[element].num_change_pages;
3882
3883         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3884         SaveLevel_CUS4(file, level, element);
3885       }
3886     }
3887   }
3888
3889   /* check for non-default group elements (unless using template level) */
3890   if (!level->use_custom_template)
3891   {
3892     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3893     {
3894       int element = EL_GROUP_START + i;
3895
3896       if (element_info[element].modified_settings)
3897       {
3898         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3899         SaveLevel_GRP1(file, level, element);
3900       }
3901     }
3902   }
3903
3904   fclose(file);
3905
3906   SetFilePermissions(filename, PERMS_PRIVATE);
3907 }
3908
3909 void SaveLevel(int nr)
3910 {
3911   char *filename = getDefaultLevelFilename(nr);
3912
3913   SaveLevelFromFilename(&level, filename);
3914 }
3915
3916 void SaveLevelTemplate()
3917 {
3918   char *filename = getDefaultLevelFilename(-1);
3919
3920   SaveLevelFromFilename(&level, filename);
3921 }
3922
3923 void DumpLevel(struct LevelInfo *level)
3924 {
3925   if (level->no_valid_file)
3926   {
3927     Error(ERR_WARN, "cannot dump -- no valid level file found");
3928
3929     return;
3930   }
3931
3932   printf_line("-", 79);
3933   printf("Level xxx (file version %08d, game version %08d)\n",
3934          level->file_version, level->game_version);
3935   printf_line("-", 79);
3936
3937   printf("Level author: '%s'\n", level->author);
3938   printf("Level title:  '%s'\n", level->name);
3939   printf("\n");
3940   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3941   printf("\n");
3942   printf("Level time:  %d seconds\n", level->time);
3943   printf("Gems needed: %d\n", level->gems_needed);
3944   printf("\n");
3945   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3946   printf("Time for wheel:      %d seconds\n", level->time_wheel);
3947   printf("Time for light:      %d seconds\n", level->time_light);
3948   printf("Time for timegate:   %d seconds\n", level->time_timegate);
3949   printf("\n");
3950   printf("Amoeba speed: %d\n", level->amoeba_speed);
3951   printf("\n");
3952   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
3953   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
3954   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
3955   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
3956   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3957   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3958   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3959
3960   printf_line("-", 79);
3961 }
3962
3963
3964 /* ========================================================================= */
3965 /* tape file functions                                                       */
3966 /* ========================================================================= */
3967
3968 static void setTapeInfoToDefaults()
3969 {
3970   int i;
3971
3972   /* always start with reliable default values (empty tape) */
3973   TapeErase();
3974
3975   /* default values (also for pre-1.2 tapes) with only the first player */
3976   tape.player_participates[0] = TRUE;
3977   for (i = 1; i < MAX_PLAYERS; i++)
3978     tape.player_participates[i] = FALSE;
3979
3980   /* at least one (default: the first) player participates in every tape */
3981   tape.num_participating_players = 1;
3982
3983   tape.level_nr = level_nr;
3984   tape.counter = 0;
3985   tape.changed = FALSE;
3986
3987   tape.recording = FALSE;
3988   tape.playing = FALSE;
3989   tape.pausing = FALSE;
3990
3991   tape.no_valid_file = FALSE;
3992 }
3993
3994 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3995 {
3996   tape->file_version = getFileVersion(file);
3997   tape->game_version = getFileVersion(file);
3998
3999   return chunk_size;
4000 }
4001
4002 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
4003 {
4004   int i;
4005
4006   tape->random_seed = getFile32BitBE(file);
4007   tape->date        = getFile32BitBE(file);
4008   tape->length      = getFile32BitBE(file);
4009
4010   /* read header fields that are new since version 1.2 */
4011   if (tape->file_version >= FILE_VERSION_1_2)
4012   {
4013     byte store_participating_players = getFile8Bit(file);
4014     int engine_version;
4015
4016     /* since version 1.2, tapes store which players participate in the tape */
4017     tape->num_participating_players = 0;
4018     for (i = 0; i < MAX_PLAYERS; i++)
4019     {
4020       tape->player_participates[i] = FALSE;
4021
4022       if (store_participating_players & (1 << i))
4023       {
4024         tape->player_participates[i] = TRUE;
4025         tape->num_participating_players++;
4026       }
4027     }
4028
4029     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
4030
4031     engine_version = getFileVersion(file);
4032     if (engine_version > 0)
4033       tape->engine_version = engine_version;
4034     else
4035       tape->engine_version = tape->game_version;
4036   }
4037
4038   return chunk_size;
4039 }
4040
4041 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
4042 {
4043   int level_identifier_size;
4044   int i;
4045
4046   level_identifier_size = getFile16BitBE(file);
4047
4048   tape->level_identifier =
4049     checked_realloc(tape->level_identifier, level_identifier_size);
4050
4051   for (i = 0; i < level_identifier_size; i++)
4052     tape->level_identifier[i] = getFile8Bit(file);
4053
4054   tape->level_nr = getFile16BitBE(file);
4055
4056   chunk_size = 2 + level_identifier_size + 2;
4057
4058   return chunk_size;
4059 }
4060
4061 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
4062 {
4063   int i, j;
4064   int chunk_size_expected =
4065     (tape->num_participating_players + 1) * tape->length;
4066
4067   if (chunk_size_expected != chunk_size)
4068   {
4069     ReadUnusedBytesFromFile(file, chunk_size);
4070     return chunk_size_expected;
4071   }
4072
4073   for (i = 0; i < tape->length; i++)
4074   {
4075     if (i >= MAX_TAPELEN)
4076       break;
4077
4078     for (j = 0; j < MAX_PLAYERS; j++)
4079     {
4080       tape->pos[i].action[j] = MV_NO_MOVING;
4081
4082       if (tape->player_participates[j])
4083         tape->pos[i].action[j] = getFile8Bit(file);
4084     }
4085
4086     tape->pos[i].delay = getFile8Bit(file);
4087
4088     if (tape->file_version == FILE_VERSION_1_0)
4089     {
4090       /* eliminate possible diagonal moves in old tapes */
4091       /* this is only for backward compatibility */
4092
4093       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
4094       byte action = tape->pos[i].action[0];
4095       int k, num_moves = 0;
4096
4097       for (k = 0; k<4; k++)
4098       {
4099         if (action & joy_dir[k])
4100         {
4101           tape->pos[i + num_moves].action[0] = joy_dir[k];
4102           if (num_moves > 0)
4103             tape->pos[i + num_moves].delay = 0;
4104           num_moves++;
4105         }
4106       }
4107
4108       if (num_moves > 1)
4109       {
4110         num_moves--;
4111         i += num_moves;
4112         tape->length += num_moves;
4113       }
4114     }
4115     else if (tape->file_version < FILE_VERSION_2_0)
4116     {
4117       /* convert pre-2.0 tapes to new tape format */
4118
4119       if (tape->pos[i].delay > 1)
4120       {
4121         /* action part */
4122         tape->pos[i + 1] = tape->pos[i];
4123         tape->pos[i + 1].delay = 1;
4124
4125         /* delay part */
4126         for (j = 0; j < MAX_PLAYERS; j++)
4127           tape->pos[i].action[j] = MV_NO_MOVING;
4128         tape->pos[i].delay--;
4129
4130         i++;
4131         tape->length++;
4132       }
4133     }
4134
4135     if (feof(file))
4136       break;
4137   }
4138
4139   if (i != tape->length)
4140     chunk_size = (tape->num_participating_players + 1) * i;
4141
4142   return chunk_size;
4143 }
4144
4145 void LoadTapeFromFilename(char *filename)
4146 {
4147   char cookie[MAX_LINE_LEN];
4148   char chunk_name[CHUNK_ID_LEN + 1];
4149   FILE *file;
4150   int chunk_size;
4151
4152   /* always start with reliable default values */
4153   setTapeInfoToDefaults();
4154
4155   if (!(file = fopen(filename, MODE_READ)))
4156   {
4157     tape.no_valid_file = TRUE;
4158
4159 #if 0
4160     Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
4161 #endif
4162
4163     return;
4164   }
4165
4166   getFileChunkBE(file, chunk_name, NULL);
4167   if (strcmp(chunk_name, "RND1") == 0)
4168   {
4169     getFile32BitBE(file);               /* not used */
4170
4171     getFileChunkBE(file, chunk_name, NULL);
4172     if (strcmp(chunk_name, "TAPE") != 0)
4173     {
4174       tape.no_valid_file = TRUE;
4175
4176       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4177       fclose(file);
4178       return;
4179     }
4180   }
4181   else  /* check for pre-2.0 file format with cookie string */
4182   {
4183     strcpy(cookie, chunk_name);
4184     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4185     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4186       cookie[strlen(cookie) - 1] = '\0';
4187
4188     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4189     {
4190       tape.no_valid_file = TRUE;
4191
4192       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4193       fclose(file);
4194       return;
4195     }
4196
4197     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4198     {
4199       tape.no_valid_file = TRUE;
4200
4201       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4202       fclose(file);
4203       return;
4204     }
4205
4206     /* pre-2.0 tape files have no game version, so use file version here */
4207     tape.game_version = tape.file_version;
4208   }
4209
4210   if (tape.file_version < FILE_VERSION_1_2)
4211   {
4212     /* tape files from versions before 1.2.0 without chunk structure */
4213     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4214     LoadTape_BODY(file, 2 * tape.length,  &tape);
4215   }
4216   else
4217   {
4218     static struct
4219     {
4220       char *name;
4221       int size;
4222       int (*loader)(FILE *, int, struct TapeInfo *);
4223     }
4224     chunk_info[] =
4225     {
4226       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
4227       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
4228       { "INFO", -1,                     LoadTape_INFO },
4229       { "BODY", -1,                     LoadTape_BODY },
4230       {  NULL,  0,                      NULL }
4231     };
4232
4233     while (getFileChunkBE(file, chunk_name, &chunk_size))
4234     {
4235       int i = 0;
4236
4237       while (chunk_info[i].name != NULL &&
4238              strcmp(chunk_name, chunk_info[i].name) != 0)
4239         i++;
4240
4241       if (chunk_info[i].name == NULL)
4242       {
4243         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4244               chunk_name, filename);
4245         ReadUnusedBytesFromFile(file, chunk_size);
4246       }
4247       else if (chunk_info[i].size != -1 &&
4248                chunk_info[i].size != chunk_size)
4249       {
4250         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4251               chunk_size, chunk_name, filename);
4252         ReadUnusedBytesFromFile(file, chunk_size);
4253       }
4254       else
4255       {
4256         /* call function to load this tape chunk */
4257         int chunk_size_expected =
4258           (chunk_info[i].loader)(file, chunk_size, &tape);
4259
4260         /* the size of some chunks cannot be checked before reading other
4261            chunks first (like "HEAD" and "BODY") that contain some header
4262            information, so check them here */
4263         if (chunk_size_expected != chunk_size)
4264         {
4265           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4266                 chunk_size, chunk_name, filename);
4267         }
4268       }
4269     }
4270   }
4271
4272   fclose(file);
4273
4274   tape.length_seconds = GetTapeLength();
4275
4276 #if 0
4277   printf("::: tape game version: %d\n", tape.game_version);
4278   printf("::: tape engine version: %d\n", tape.engine_version);
4279 #endif
4280 }
4281
4282 void LoadTape(int nr)
4283 {
4284   char *filename = getTapeFilename(nr);
4285
4286   LoadTapeFromFilename(filename);
4287 }
4288
4289 void LoadSolutionTape(int nr)
4290 {
4291   char *filename = getSolutionTapeFilename(nr);
4292
4293   LoadTapeFromFilename(filename);
4294 }
4295
4296 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4297 {
4298   putFileVersion(file, tape->file_version);
4299   putFileVersion(file, tape->game_version);
4300 }
4301
4302 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4303 {
4304   int i;
4305   byte store_participating_players = 0;
4306
4307   /* set bits for participating players for compact storage */
4308   for (i = 0; i < MAX_PLAYERS; i++)
4309     if (tape->player_participates[i])
4310       store_participating_players |= (1 << i);
4311
4312   putFile32BitBE(file, tape->random_seed);
4313   putFile32BitBE(file, tape->date);
4314   putFile32BitBE(file, tape->length);
4315
4316   putFile8Bit(file, store_participating_players);
4317
4318   /* unused bytes not at the end here for 4-byte alignment of engine_version */
4319   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4320
4321   putFileVersion(file, tape->engine_version);
4322 }
4323
4324 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4325 {
4326   int level_identifier_size = strlen(tape->level_identifier) + 1;
4327   int i;
4328
4329   putFile16BitBE(file, level_identifier_size);
4330
4331   for (i = 0; i < level_identifier_size; i++)
4332     putFile8Bit(file, tape->level_identifier[i]);
4333
4334   putFile16BitBE(file, tape->level_nr);
4335 }
4336
4337 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4338 {
4339   int i, j;
4340
4341   for (i = 0; i < tape->length; i++)
4342   {
4343     for (j = 0; j < MAX_PLAYERS; j++)
4344       if (tape->player_participates[j])
4345         putFile8Bit(file, tape->pos[i].action[j]);
4346
4347     putFile8Bit(file, tape->pos[i].delay);
4348   }
4349 }
4350
4351 void SaveTape(int nr)
4352 {
4353   char *filename = getTapeFilename(nr);
4354   FILE *file;
4355   boolean new_tape = TRUE;
4356   int num_participating_players = 0;
4357   int info_chunk_size;
4358   int body_chunk_size;
4359   int i;
4360
4361   InitTapeDirectory(leveldir_current->subdir);
4362
4363   /* if a tape still exists, ask to overwrite it */
4364   if (access(filename, F_OK) == 0)
4365   {
4366     new_tape = FALSE;
4367     if (!Request("Replace old tape ?", REQ_ASK))
4368       return;
4369   }
4370
4371   if (!(file = fopen(filename, MODE_WRITE)))
4372   {
4373     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4374     return;
4375   }
4376
4377   tape.file_version = FILE_VERSION_ACTUAL;
4378   tape.game_version = GAME_VERSION_ACTUAL;
4379
4380   /* count number of participating players  */
4381   for (i = 0; i < MAX_PLAYERS; i++)
4382     if (tape.player_participates[i])
4383       num_participating_players++;
4384
4385   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4386   body_chunk_size = (num_participating_players + 1) * tape.length;
4387
4388   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4389   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4390
4391   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4392   SaveTape_VERS(file, &tape);
4393
4394   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4395   SaveTape_HEAD(file, &tape);
4396
4397   putFileChunkBE(file, "INFO", info_chunk_size);
4398   SaveTape_INFO(file, &tape);
4399
4400   putFileChunkBE(file, "BODY", body_chunk_size);
4401   SaveTape_BODY(file, &tape);
4402
4403   fclose(file);
4404
4405   SetFilePermissions(filename, PERMS_PRIVATE);
4406
4407   tape.changed = FALSE;
4408
4409   if (new_tape)
4410     Request("tape saved !", REQ_CONFIRM);
4411 }
4412
4413 void DumpTape(struct TapeInfo *tape)
4414 {
4415   int i, j;
4416
4417 #if 1
4418   if (tape->no_valid_file)
4419   {
4420     Error(ERR_WARN, "cannot dump -- no valid tape file found");
4421
4422     return;
4423   }
4424 #else
4425   if (TAPE_IS_EMPTY(*tape))
4426   {
4427     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
4428
4429     return;
4430   }
4431 #endif
4432
4433   printf_line("-", 79);
4434   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4435          tape->level_nr, tape->file_version, tape->game_version);
4436   printf("                  (effective engine version %08d)\n",
4437          tape->engine_version);
4438   printf("Level series identifier: '%s'\n", tape->level_identifier);
4439   printf_line("-", 79);
4440
4441   for (i = 0; i < tape->length; i++)
4442   {
4443     if (i >= MAX_TAPELEN)
4444       break;
4445
4446     printf("%03d: ", i);
4447
4448     for (j = 0; j < MAX_PLAYERS; j++)
4449     {
4450       if (tape->player_participates[j])
4451       {
4452         int action = tape->pos[i].action[j];
4453
4454         printf("%d:%02x ", j, action);
4455         printf("[%c%c%c%c|%c%c] - ",
4456                (action & JOY_LEFT ? '<' : ' '),
4457                (action & JOY_RIGHT ? '>' : ' '),
4458                (action & JOY_UP ? '^' : ' '),
4459                (action & JOY_DOWN ? 'v' : ' '),
4460                (action & JOY_BUTTON_1 ? '1' : ' '),
4461                (action & JOY_BUTTON_2 ? '2' : ' '));
4462       }
4463     }
4464
4465     printf("(%03d)\n", tape->pos[i].delay);
4466   }
4467
4468   printf_line("-", 79);
4469 }
4470
4471
4472 /* ========================================================================= */
4473 /* score file functions                                                      */
4474 /* ========================================================================= */
4475
4476 void LoadScore(int nr)
4477 {
4478   int i;
4479   char *filename = getScoreFilename(nr);
4480   char cookie[MAX_LINE_LEN];
4481   char line[MAX_LINE_LEN];
4482   char *line_ptr;
4483   FILE *file;
4484
4485   /* always start with reliable default values */
4486   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4487   {
4488     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4489     highscore[i].Score = 0;
4490   }
4491
4492   if (!(file = fopen(filename, MODE_READ)))
4493     return;
4494
4495   /* check file identifier */
4496   fgets(cookie, MAX_LINE_LEN, file);
4497   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4498     cookie[strlen(cookie) - 1] = '\0';
4499
4500   if (!checkCookieString(cookie, SCORE_COOKIE))
4501   {
4502     Error(ERR_WARN, "unknown format of score file '%s'", filename);
4503     fclose(file);
4504     return;
4505   }
4506
4507   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4508   {
4509     fscanf(file, "%d", &highscore[i].Score);
4510     fgets(line, MAX_LINE_LEN, file);
4511
4512     if (line[strlen(line) - 1] == '\n')
4513       line[strlen(line) - 1] = '\0';
4514
4515     for (line_ptr = line; *line_ptr; line_ptr++)
4516     {
4517       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4518       {
4519         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4520         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4521         break;
4522       }
4523     }
4524   }
4525
4526   fclose(file);
4527 }
4528
4529 void SaveScore(int nr)
4530 {
4531   int i;
4532   char *filename = getScoreFilename(nr);
4533   FILE *file;
4534
4535   InitScoreDirectory(leveldir_current->subdir);
4536
4537   if (!(file = fopen(filename, MODE_WRITE)))
4538   {
4539     Error(ERR_WARN, "cannot save score for level %d", nr);
4540     return;
4541   }
4542
4543   fprintf(file, "%s\n\n", SCORE_COOKIE);
4544
4545   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4546     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4547
4548   fclose(file);
4549
4550   SetFilePermissions(filename, PERMS_PUBLIC);
4551 }
4552
4553
4554 /* ========================================================================= */
4555 /* setup file functions                                                      */
4556 /* ========================================================================= */
4557
4558 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
4559
4560 /* global setup */
4561 #define SETUP_TOKEN_PLAYER_NAME                 0
4562 #define SETUP_TOKEN_SOUND                       1
4563 #define SETUP_TOKEN_SOUND_LOOPS                 2
4564 #define SETUP_TOKEN_SOUND_MUSIC                 3
4565 #define SETUP_TOKEN_SOUND_SIMPLE                4
4566 #define SETUP_TOKEN_TOONS                       5
4567 #define SETUP_TOKEN_SCROLL_DELAY                6
4568 #define SETUP_TOKEN_SOFT_SCROLLING              7
4569 #define SETUP_TOKEN_FADING                      8
4570 #define SETUP_TOKEN_AUTORECORD                  9
4571 #define SETUP_TOKEN_QUICK_DOORS                 10
4572 #define SETUP_TOKEN_TEAM_MODE                   11
4573 #define SETUP_TOKEN_HANDICAP                    12
4574 #define SETUP_TOKEN_TIME_LIMIT                  13
4575 #define SETUP_TOKEN_FULLSCREEN                  14
4576 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
4577 #define SETUP_TOKEN_GRAPHICS_SET                16
4578 #define SETUP_TOKEN_SOUNDS_SET                  17
4579 #define SETUP_TOKEN_MUSIC_SET                   18
4580 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
4581 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
4582 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
4583
4584 #define NUM_GLOBAL_SETUP_TOKENS                 22
4585
4586 /* editor setup */
4587 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
4588 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
4589 #define SETUP_TOKEN_EDITOR_EL_MORE              2
4590 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
4591 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
4592 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
4593 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
4594 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
4595 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
4596 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
4597 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
4598 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
4599
4600 #define NUM_EDITOR_SETUP_TOKENS                 12
4601
4602 /* shortcut setup */
4603 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
4604 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
4605 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
4606
4607 #define NUM_SHORTCUT_SETUP_TOKENS               3
4608
4609 /* player setup */
4610 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
4611 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
4612 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
4613 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
4614 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
4615 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
4616 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
4617 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
4618 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
4619 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
4620 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
4621 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
4622 #define SETUP_TOKEN_PLAYER_KEY_UP               12
4623 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
4624 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
4625 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
4626
4627 #define NUM_PLAYER_SETUP_TOKENS                 16
4628
4629 /* system setup */
4630 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
4631 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
4632
4633 #define NUM_SYSTEM_SETUP_TOKENS                 2
4634
4635 /* options setup */
4636 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
4637
4638 #define NUM_OPTIONS_SETUP_TOKENS                1
4639
4640
4641 static struct SetupInfo si;
4642 static struct SetupEditorInfo sei;
4643 static struct SetupShortcutInfo ssi;
4644 static struct SetupInputInfo sii;
4645 static struct SetupSystemInfo syi;
4646 static struct OptionInfo soi;
4647
4648 static struct TokenInfo global_setup_tokens[] =
4649 {
4650   { TYPE_STRING, &si.player_name,       "player_name"                   },
4651   { TYPE_SWITCH, &si.sound,             "sound"                         },
4652   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
4653   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
4654   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
4655   { TYPE_SWITCH, &si.toons,             "toons"                         },
4656   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
4657   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
4658   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
4659   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
4660   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
4661   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
4662   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
4663   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
4664   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
4665   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
4666   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
4667   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
4668   { TYPE_STRING, &si.music_set,         "music_set"                     },
4669   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4670   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
4671   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
4672 };
4673
4674 static struct TokenInfo editor_setup_tokens[] =
4675 {
4676   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
4677   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
4678   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
4679   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
4680   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
4681   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
4682   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
4683   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
4684   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
4685   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
4686   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
4687   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
4688 };
4689
4690 static struct TokenInfo shortcut_setup_tokens[] =
4691 {
4692   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
4693   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
4694   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
4695 };
4696
4697 static struct TokenInfo player_setup_tokens[] =
4698 {
4699   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
4700   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
4701   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
4702   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
4703   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
4704   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
4705   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
4706   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
4707   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
4708   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
4709   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
4710   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
4711   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
4712   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
4713   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
4714   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
4715 };
4716
4717 static struct TokenInfo system_setup_tokens[] =
4718 {
4719   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
4720   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4721 };
4722
4723 static struct TokenInfo options_setup_tokens[] =
4724 {
4725   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
4726 };
4727
4728 static char *get_corrected_login_name(char *login_name)
4729 {
4730   /* needed because player name must be a fixed length string */
4731   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4732
4733   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4734   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4735
4736   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
4737     if (strchr(login_name_new, ' '))
4738       *strchr(login_name_new, ' ') = '\0';
4739
4740   return login_name_new;
4741 }
4742
4743 static void setSetupInfoToDefaults(struct SetupInfo *si)
4744 {
4745   int i;
4746
4747   si->player_name = get_corrected_login_name(getLoginName());
4748
4749   si->sound = TRUE;
4750   si->sound_loops = TRUE;
4751   si->sound_music = TRUE;
4752   si->sound_simple = TRUE;
4753   si->toons = TRUE;
4754   si->double_buffering = TRUE;
4755   si->direct_draw = !si->double_buffering;
4756   si->scroll_delay = TRUE;
4757   si->soft_scrolling = TRUE;
4758   si->fading = FALSE;
4759   si->autorecord = TRUE;
4760   si->quick_doors = FALSE;
4761   si->team_mode = FALSE;
4762   si->handicap = TRUE;
4763   si->time_limit = TRUE;
4764   si->fullscreen = FALSE;
4765   si->ask_on_escape = TRUE;
4766
4767   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4768   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4769   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4770   si->override_level_graphics = FALSE;
4771   si->override_level_sounds = FALSE;
4772   si->override_level_music = FALSE;
4773
4774   si->editor.el_boulderdash = TRUE;
4775   si->editor.el_emerald_mine = TRUE;
4776   si->editor.el_more = TRUE;
4777   si->editor.el_sokoban = TRUE;
4778   si->editor.el_supaplex = TRUE;
4779   si->editor.el_diamond_caves = TRUE;
4780   si->editor.el_dx_boulderdash = TRUE;
4781   si->editor.el_chars = TRUE;
4782   si->editor.el_custom = TRUE;
4783   si->editor.el_custom_more = FALSE;
4784
4785   si->editor.el_headlines = TRUE;
4786   si->editor.el_user_defined = FALSE;
4787
4788   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4789   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4790   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4791
4792   for (i = 0; i < MAX_PLAYERS; i++)
4793   {
4794     si->input[i].use_joystick = FALSE;
4795     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4796     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
4797     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4798     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
4799     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
4800     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4801     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
4802     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
4803     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
4804     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
4805     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4806     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
4807     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
4808     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
4809     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
4810   }
4811
4812   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4813   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4814
4815   si->options.verbose = FALSE;
4816 }
4817
4818 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4819 {
4820   int i, pnr;
4821
4822   if (!setup_file_hash)
4823     return;
4824
4825   /* global setup */
4826   si = setup;
4827   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4828     setSetupInfo(global_setup_tokens, i,
4829                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4830   setup = si;
4831
4832   /* editor setup */
4833   sei = setup.editor;
4834   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4835     setSetupInfo(editor_setup_tokens, i,
4836                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4837   setup.editor = sei;
4838
4839   /* shortcut setup */
4840   ssi = setup.shortcut;
4841   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4842     setSetupInfo(shortcut_setup_tokens, i,
4843                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4844   setup.shortcut = ssi;
4845
4846   /* player setup */
4847   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4848   {
4849     char prefix[30];
4850
4851     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4852
4853     sii = setup.input[pnr];
4854     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4855     {
4856       char full_token[100];
4857
4858       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4859       setSetupInfo(player_setup_tokens, i,
4860                    getHashEntry(setup_file_hash, full_token));
4861     }
4862     setup.input[pnr] = sii;
4863   }
4864
4865   /* system setup */
4866   syi = setup.system;
4867   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4868     setSetupInfo(system_setup_tokens, i,
4869                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4870   setup.system = syi;
4871
4872   /* options setup */
4873   soi = setup.options;
4874   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4875     setSetupInfo(options_setup_tokens, i,
4876                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4877   setup.options = soi;
4878 }
4879
4880 void LoadSetup()
4881 {
4882   char *filename = getSetupFilename();
4883   SetupFileHash *setup_file_hash = NULL;
4884
4885   /* always start with reliable default values */
4886   setSetupInfoToDefaults(&setup);
4887
4888   setup_file_hash = loadSetupFileHash(filename);
4889
4890   if (setup_file_hash)
4891   {
4892     char *player_name_new;
4893
4894     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4895     decodeSetupFileHash(setup_file_hash);
4896
4897     setup.direct_draw = !setup.double_buffering;
4898
4899     freeSetupFileHash(setup_file_hash);
4900
4901     /* needed to work around problems with fixed length strings */
4902     player_name_new = get_corrected_login_name(setup.player_name);
4903     free(setup.player_name);
4904     setup.player_name = player_name_new;
4905   }
4906   else
4907     Error(ERR_WARN, "using default setup values");
4908 }
4909
4910 void SaveSetup()
4911 {
4912   char *filename = getSetupFilename();
4913   FILE *file;
4914   int i, pnr;
4915
4916   InitUserDataDirectory();
4917
4918   if (!(file = fopen(filename, MODE_WRITE)))
4919   {
4920     Error(ERR_WARN, "cannot write setup file '%s'", filename);
4921     return;
4922   }
4923
4924   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4925                                                getCookie("SETUP")));
4926   fprintf(file, "\n");
4927
4928   /* global setup */
4929   si = setup;
4930   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4931   {
4932     /* just to make things nicer :) */
4933     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4934         i == SETUP_TOKEN_GRAPHICS_SET)
4935       fprintf(file, "\n");
4936
4937     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4938   }
4939
4940   /* editor setup */
4941   sei = setup.editor;
4942   fprintf(file, "\n");
4943   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4944     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4945
4946   /* shortcut setup */
4947   ssi = setup.shortcut;
4948   fprintf(file, "\n");
4949   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4950     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4951
4952   /* player setup */
4953   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4954   {
4955     char prefix[30];
4956
4957     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4958     fprintf(file, "\n");
4959
4960     sii = setup.input[pnr];
4961     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4962       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4963   }
4964
4965   /* system setup */
4966   syi = setup.system;
4967   fprintf(file, "\n");
4968   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4969     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4970
4971   /* options setup */
4972   soi = setup.options;
4973   fprintf(file, "\n");
4974   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4975     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4976
4977   fclose(file);
4978
4979   SetFilePermissions(filename, PERMS_PRIVATE);
4980 }
4981
4982 void LoadCustomElementDescriptions()
4983 {
4984   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4985   SetupFileHash *setup_file_hash;
4986   int i;
4987
4988   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4989   {
4990     if (element_info[i].custom_description != NULL)
4991     {
4992       free(element_info[i].custom_description);
4993       element_info[i].custom_description = NULL;
4994     }
4995   }
4996
4997   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4998     return;
4999
5000   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5001   {
5002     char *token = getStringCat2(element_info[i].token_name, ".name");
5003     char *value = getHashEntry(setup_file_hash, token);
5004
5005     if (value != NULL)
5006       element_info[i].custom_description = getStringCopy(value);
5007
5008     free(token);
5009   }
5010
5011   freeSetupFileHash(setup_file_hash);
5012 }
5013
5014 void LoadSpecialMenuDesignSettings()
5015 {
5016   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
5017   SetupFileHash *setup_file_hash;
5018   int i, j;
5019
5020   /* always start with reliable default values from default config */
5021   for (i = 0; image_config_vars[i].token != NULL; i++)
5022     for (j = 0; image_config[j].token != NULL; j++)
5023       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
5024         *image_config_vars[i].value =
5025           get_auto_parameter_value(image_config_vars[i].token,
5026                                    image_config[j].value);
5027
5028   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5029     return;
5030
5031   /* special case: initialize with default values that may be overwritten */
5032   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5033   {
5034     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
5035     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
5036     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
5037
5038     if (value_x != NULL)
5039       menu.draw_xoffset[i] = get_integer_from_string(value_x);
5040     if (value_y != NULL)
5041       menu.draw_yoffset[i] = get_integer_from_string(value_y);
5042     if (list_size != NULL)
5043       menu.list_size[i] = get_integer_from_string(list_size);
5044   }
5045
5046   /* read (and overwrite with) values that may be specified in config file */
5047   for (i = 0; image_config_vars[i].token != NULL; i++)
5048   {
5049     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
5050
5051     if (value != NULL)
5052       *image_config_vars[i].value =
5053         get_auto_parameter_value(image_config_vars[i].token, value);
5054   }
5055
5056   freeSetupFileHash(setup_file_hash);
5057 }
5058
5059 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
5060 {
5061   char *filename = getEditorSetupFilename();
5062   SetupFileList *setup_file_list, *list;
5063   SetupFileHash *element_hash;
5064   int num_unknown_tokens = 0;
5065   int i;
5066
5067   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
5068     return;
5069
5070   element_hash = newSetupFileHash();
5071
5072   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5073     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5074
5075   /* determined size may be larger than needed (due to unknown elements) */
5076   *num_elements = 0;
5077   for (list = setup_file_list; list != NULL; list = list->next)
5078     (*num_elements)++;
5079
5080   /* add space for up to 3 more elements for padding that may be needed */
5081   *num_elements += 3;
5082
5083   *elements = checked_malloc(*num_elements * sizeof(int));
5084
5085   *num_elements = 0;
5086   for (list = setup_file_list; list != NULL; list = list->next)
5087   {
5088     char *value = getHashEntry(element_hash, list->token);
5089
5090     if (value)
5091     {
5092       (*elements)[(*num_elements)++] = atoi(value);
5093     }
5094     else
5095     {
5096       if (num_unknown_tokens == 0)
5097       {
5098         Error(ERR_RETURN_LINE, "-");
5099         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5100         Error(ERR_RETURN, "- config file: '%s'", filename);
5101
5102         num_unknown_tokens++;
5103       }
5104
5105       Error(ERR_RETURN, "- token: '%s'", list->token);
5106     }
5107   }
5108
5109   if (num_unknown_tokens > 0)
5110     Error(ERR_RETURN_LINE, "-");
5111
5112   while (*num_elements % 4)     /* pad with empty elements, if needed */
5113     (*elements)[(*num_elements)++] = EL_EMPTY;
5114
5115   freeSetupFileList(setup_file_list);
5116   freeSetupFileHash(element_hash);
5117
5118 #if 0
5119   /* TEST-ONLY */
5120   for (i = 0; i < *num_elements; i++)
5121     printf("editor: element '%s' [%d]\n",
5122            element_info[(*elements)[i]].token_name, (*elements)[i]);
5123 #endif
5124 }
5125
5126 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
5127                                                      boolean is_sound)
5128 {
5129   SetupFileHash *setup_file_hash = NULL;
5130   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
5131   char *filename_music, *filename_prefix, *filename_info;
5132   struct
5133   {
5134     char *token;
5135     char **value_ptr;
5136   }
5137   token_to_value_ptr[] =
5138   {
5139     { "title_header",   &tmp_music_file_info.title_header       },
5140     { "artist_header",  &tmp_music_file_info.artist_header      },
5141     { "album_header",   &tmp_music_file_info.album_header       },
5142     { "year_header",    &tmp_music_file_info.year_header        },
5143
5144     { "title",          &tmp_music_file_info.title              },
5145     { "artist",         &tmp_music_file_info.artist             },
5146     { "album",          &tmp_music_file_info.album              },
5147     { "year",           &tmp_music_file_info.year               },
5148
5149     { NULL,             NULL                                    },
5150   };
5151   int i;
5152
5153   filename_music = (is_sound ? getCustomSoundFilename(basename) :
5154                     getCustomMusicFilename(basename));
5155
5156   if (filename_music == NULL)
5157     return NULL;
5158
5159   /* ---------- try to replace file extension ---------- */
5160
5161   filename_prefix = getStringCopy(filename_music);
5162   if (strrchr(filename_prefix, '.') != NULL)
5163     *strrchr(filename_prefix, '.') = '\0';
5164   filename_info = getStringCat2(filename_prefix, ".txt");
5165
5166 #if 0
5167   printf("trying to load file '%s'...\n", filename_info);
5168 #endif
5169
5170   if (fileExists(filename_info))
5171     setup_file_hash = loadSetupFileHash(filename_info);
5172
5173   free(filename_prefix);
5174   free(filename_info);
5175
5176   if (setup_file_hash == NULL)
5177   {
5178     /* ---------- try to add file extension ---------- */
5179
5180     filename_prefix = getStringCopy(filename_music);
5181     filename_info = getStringCat2(filename_prefix, ".txt");
5182
5183 #if 0
5184     printf("trying to load file '%s'...\n", filename_info);
5185 #endif
5186
5187     if (fileExists(filename_info))
5188       setup_file_hash = loadSetupFileHash(filename_info);
5189
5190     free(filename_prefix);
5191     free(filename_info);
5192   }
5193
5194   if (setup_file_hash == NULL)
5195     return NULL;
5196
5197   /* ---------- music file info found ---------- */
5198
5199   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5200
5201   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5202   {
5203     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5204
5205     *token_to_value_ptr[i].value_ptr =
5206       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5207   }
5208
5209   tmp_music_file_info.basename = getStringCopy(basename);
5210   tmp_music_file_info.music = music;
5211   tmp_music_file_info.is_sound = is_sound;
5212
5213   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5214   *new_music_file_info = tmp_music_file_info;
5215
5216   return new_music_file_info;
5217 }
5218
5219 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5220 {
5221   return get_music_file_info_ext(basename, music, FALSE);
5222 }
5223
5224 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5225 {
5226   return get_music_file_info_ext(basename, sound, TRUE);
5227 }
5228
5229 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5230                                      char *basename, boolean is_sound)
5231 {
5232   for (; list != NULL; list = list->next)
5233     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5234       return TRUE;
5235
5236   return FALSE;
5237 }
5238
5239 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5240 {
5241   return music_info_listed_ext(list, basename, FALSE);
5242 }
5243
5244 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5245 {
5246   return music_info_listed_ext(list, basename, TRUE);
5247 }
5248
5249 void LoadMusicInfo()
5250 {
5251   char *music_directory = getCustomMusicDirectory();
5252   int num_music = getMusicListSize();
5253   int num_music_noconf = 0;
5254   int num_sounds = getSoundListSize();
5255   DIR *dir;
5256   struct dirent *dir_entry;
5257   struct FileInfo *music, *sound;
5258   struct MusicFileInfo *next, **new;
5259   int i;
5260
5261   while (music_file_info != NULL)
5262   {
5263     next = music_file_info->next;
5264
5265     checked_free(music_file_info->basename);
5266
5267     checked_free(music_file_info->title_header);
5268     checked_free(music_file_info->artist_header);
5269     checked_free(music_file_info->album_header);
5270     checked_free(music_file_info->year_header);
5271
5272     checked_free(music_file_info->title);
5273     checked_free(music_file_info->artist);
5274     checked_free(music_file_info->album);
5275     checked_free(music_file_info->year);
5276
5277     free(music_file_info);
5278
5279     music_file_info = next;
5280   }
5281
5282   new = &music_file_info;
5283
5284 #if 0
5285   printf("::: num_music == %d\n", num_music);
5286 #endif
5287
5288   for (i = 0; i < num_music; i++)
5289   {
5290     music = getMusicListEntry(i);
5291
5292 #if 0
5293     printf("::: %d [%08x]\n", i, music->filename);
5294 #endif
5295
5296     if (music->filename == NULL)
5297       continue;
5298
5299     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5300       continue;
5301
5302     /* a configured file may be not recognized as music */
5303     if (!FileIsMusic(music->filename))
5304       continue;
5305
5306 #if 0
5307     printf("::: -> '%s' (configured)\n", music->filename);
5308 #endif
5309
5310     if (!music_info_listed(music_file_info, music->filename))
5311     {
5312       *new = get_music_file_info(music->filename, i);
5313       if (*new != NULL)
5314         new = &(*new)->next;
5315     }
5316   }
5317
5318   if ((dir = opendir(music_directory)) == NULL)
5319   {
5320     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5321     return;
5322   }
5323
5324   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
5325   {
5326     char *basename = dir_entry->d_name;
5327     boolean music_already_used = FALSE;
5328     int i;
5329
5330     /* skip all music files that are configured in music config file */
5331     for (i = 0; i < num_music; i++)
5332     {
5333       music = getMusicListEntry(i);
5334
5335       if (music->filename == NULL)
5336         continue;
5337
5338       if (strcmp(basename, music->filename) == 0)
5339       {
5340         music_already_used = TRUE;
5341         break;
5342       }
5343     }
5344
5345     if (music_already_used)
5346       continue;
5347
5348     if (!FileIsMusic(basename))
5349       continue;
5350
5351 #if 0
5352     printf("::: -> '%s' (found in directory)\n", basename);
5353 #endif
5354
5355     if (!music_info_listed(music_file_info, basename))
5356     {
5357       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5358       if (*new != NULL)
5359         new = &(*new)->next;
5360     }
5361
5362     num_music_noconf++;
5363   }
5364
5365   closedir(dir);
5366
5367   for (i = 0; i < num_sounds; i++)
5368   {
5369     sound = getSoundListEntry(i);
5370
5371     if (sound->filename == NULL)
5372       continue;
5373
5374     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5375       continue;
5376
5377     /* a configured file may be not recognized as sound */
5378     if (!FileIsSound(sound->filename))
5379       continue;
5380
5381 #if 0
5382     printf("::: -> '%s' (configured)\n", sound->filename);
5383 #endif
5384
5385     if (!sound_info_listed(music_file_info, sound->filename))
5386     {
5387       *new = get_sound_file_info(sound->filename, i);
5388       if (*new != NULL)
5389         new = &(*new)->next;
5390     }
5391   }
5392
5393 #if 0
5394   /* TEST-ONLY */
5395   for (next = music_file_info; next != NULL; next = next->next)
5396     printf("::: title == '%s'\n", next->title);
5397 #endif
5398 }
5399
5400 void add_helpanim_entry(int element, int action, int direction, int delay,
5401                         int *num_list_entries)
5402 {
5403   struct HelpAnimInfo *new_list_entry;
5404   (*num_list_entries)++;
5405
5406   helpanim_info =
5407     checked_realloc(helpanim_info,
5408                     *num_list_entries * sizeof(struct HelpAnimInfo));
5409   new_list_entry = &helpanim_info[*num_list_entries - 1];
5410
5411   new_list_entry->element = element;
5412   new_list_entry->action = action;
5413   new_list_entry->direction = direction;
5414   new_list_entry->delay = delay;
5415 }
5416
5417 void print_unknown_token(char *filename, char *token, int token_nr)
5418 {
5419   if (token_nr == 0)
5420   {
5421     Error(ERR_RETURN_LINE, "-");
5422     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5423     Error(ERR_RETURN, "- config file: '%s'", filename);
5424   }
5425
5426   Error(ERR_RETURN, "- token: '%s'", token);
5427 }
5428
5429 void print_unknown_token_end(int token_nr)
5430 {
5431   if (token_nr > 0)
5432     Error(ERR_RETURN_LINE, "-");
5433 }
5434
5435 void LoadHelpAnimInfo()
5436 {
5437   char *filename = getHelpAnimFilename();
5438   SetupFileList *setup_file_list = NULL, *list;
5439   SetupFileHash *element_hash, *action_hash, *direction_hash;
5440   int num_list_entries = 0;
5441   int num_unknown_tokens = 0;
5442   int i;
5443
5444   if (fileExists(filename))
5445     setup_file_list = loadSetupFileList(filename);
5446
5447   if (setup_file_list == NULL)
5448   {
5449     /* use reliable default values from static configuration */
5450     SetupFileList *insert_ptr;
5451
5452     insert_ptr = setup_file_list =
5453       newSetupFileList(helpanim_config[0].token,
5454                        helpanim_config[0].value);
5455
5456     for (i = 1; helpanim_config[i].token; i++)
5457       insert_ptr = addListEntry(insert_ptr,
5458                                 helpanim_config[i].token,
5459                                 helpanim_config[i].value);
5460   }
5461
5462   element_hash   = newSetupFileHash();
5463   action_hash    = newSetupFileHash();
5464   direction_hash = newSetupFileHash();
5465
5466   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5467     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5468
5469   for (i = 0; i < NUM_ACTIONS; i++)
5470     setHashEntry(action_hash, element_action_info[i].suffix,
5471                  i_to_a(element_action_info[i].value));
5472
5473   /* do not store direction index (bit) here, but direction value! */
5474   for (i = 0; i < NUM_DIRECTIONS; i++)
5475     setHashEntry(direction_hash, element_direction_info[i].suffix,
5476                  i_to_a(1 << element_direction_info[i].value));
5477
5478   for (list = setup_file_list; list != NULL; list = list->next)
5479   {
5480     char *element_token, *action_token, *direction_token;
5481     char *element_value, *action_value, *direction_value;
5482     int delay = atoi(list->value);
5483
5484     if (strcmp(list->token, "end") == 0)
5485     {
5486       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5487
5488       continue;
5489     }
5490
5491     /* first try to break element into element/action/direction parts;
5492        if this does not work, also accept combined "element[.act][.dir]"
5493        elements (like "dynamite.active"), which are unique elements */
5494
5495     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
5496     {
5497       element_value = getHashEntry(element_hash, list->token);
5498       if (element_value != NULL)        /* element found */
5499         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5500                            &num_list_entries);
5501       else
5502       {
5503         /* no further suffixes found -- this is not an element */
5504         print_unknown_token(filename, list->token, num_unknown_tokens++);
5505       }
5506
5507       continue;
5508     }
5509
5510     /* token has format "<prefix>.<something>" */
5511
5512     action_token = strchr(list->token, '.');    /* suffix may be action ... */
5513     direction_token = action_token;             /* ... or direction */
5514
5515     element_token = getStringCopy(list->token);
5516     *strchr(element_token, '.') = '\0';
5517
5518     element_value = getHashEntry(element_hash, element_token);
5519
5520     if (element_value == NULL)          /* this is no element */
5521     {
5522       element_value = getHashEntry(element_hash, list->token);
5523       if (element_value != NULL)        /* combined element found */
5524         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5525                            &num_list_entries);
5526       else
5527         print_unknown_token(filename, list->token, num_unknown_tokens++);
5528
5529       free(element_token);
5530
5531       continue;
5532     }
5533
5534     action_value = getHashEntry(action_hash, action_token);
5535
5536     if (action_value != NULL)           /* action found */
5537     {
5538       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5539                     &num_list_entries);
5540
5541       free(element_token);
5542
5543       continue;
5544     }
5545
5546     direction_value = getHashEntry(direction_hash, direction_token);
5547
5548     if (direction_value != NULL)        /* direction found */
5549     {
5550       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5551                          &num_list_entries);
5552
5553       free(element_token);
5554
5555       continue;
5556     }
5557
5558     if (strchr(action_token + 1, '.') == NULL)
5559     {
5560       /* no further suffixes found -- this is not an action nor direction */
5561
5562       element_value = getHashEntry(element_hash, list->token);
5563       if (element_value != NULL)        /* combined element found */
5564         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5565                            &num_list_entries);
5566       else
5567         print_unknown_token(filename, list->token, num_unknown_tokens++);
5568
5569       free(element_token);
5570
5571       continue;
5572     }
5573
5574     /* token has format "<prefix>.<suffix>.<something>" */
5575
5576     direction_token = strchr(action_token + 1, '.');
5577
5578     action_token = getStringCopy(action_token);
5579     *strchr(action_token + 1, '.') = '\0';
5580
5581     action_value = getHashEntry(action_hash, action_token);
5582
5583     if (action_value == NULL)           /* this is no action */
5584     {
5585       element_value = getHashEntry(element_hash, list->token);
5586       if (element_value != NULL)        /* combined element found */
5587         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5588                            &num_list_entries);
5589       else
5590         print_unknown_token(filename, list->token, num_unknown_tokens++);
5591
5592       free(element_token);
5593       free(action_token);
5594
5595       continue;
5596     }
5597
5598     direction_value = getHashEntry(direction_hash, direction_token);
5599
5600     if (direction_value != NULL)        /* direction found */
5601     {
5602       add_helpanim_entry(atoi(element_value), atoi(action_value),
5603                          atoi(direction_value), delay, &num_list_entries);
5604
5605       free(element_token);
5606       free(action_token);
5607
5608       continue;
5609     }
5610
5611     /* this is no direction */
5612
5613     element_value = getHashEntry(element_hash, list->token);
5614     if (element_value != NULL)          /* combined element found */
5615       add_helpanim_entry(atoi(element_value), -1, -1, delay,
5616                          &num_list_entries);
5617     else
5618       print_unknown_token(filename, list->token, num_unknown_tokens++);
5619
5620     free(element_token);
5621     free(action_token);
5622   }
5623
5624   print_unknown_token_end(num_unknown_tokens);
5625
5626   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5627   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
5628
5629   freeSetupFileList(setup_file_list);
5630   freeSetupFileHash(element_hash);
5631   freeSetupFileHash(action_hash);
5632   freeSetupFileHash(direction_hash);
5633
5634 #if 0
5635   /* TEST ONLY */
5636   for (i = 0; i < num_list_entries; i++)
5637     printf("::: %d, %d, %d => %d\n",
5638            helpanim_info[i].element,
5639            helpanim_info[i].action,
5640            helpanim_info[i].direction,
5641            helpanim_info[i].delay);
5642 #endif
5643 }
5644
5645 void LoadHelpTextInfo()
5646 {
5647   char *filename = getHelpTextFilename();
5648   int i;
5649
5650   if (helptext_info != NULL)
5651   {
5652     freeSetupFileHash(helptext_info);
5653     helptext_info = NULL;
5654   }
5655
5656   if (fileExists(filename))
5657     helptext_info = loadSetupFileHash(filename);
5658
5659   if (helptext_info == NULL)
5660   {
5661     /* use reliable default values from static configuration */
5662     helptext_info = newSetupFileHash();
5663
5664     for (i = 0; helptext_config[i].token; i++)
5665       setHashEntry(helptext_info,
5666                    helptext_config[i].token,
5667                    helptext_config[i].value);
5668   }
5669
5670 #if 0
5671   /* TEST ONLY */
5672   BEGIN_HASH_ITERATION(helptext_info, itr)
5673   {
5674     printf("::: '%s' => '%s'\n",
5675            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5676   }
5677   END_HASH_ITERATION(hash, itr)
5678 #endif
5679 }
5680
5681
5682 /* ------------------------------------------------------------------------- *
5683  * convert levels
5684  * ------------------------------------------------------------------------- */
5685
5686 #define MAX_NUM_CONVERT_LEVELS          1000
5687
5688 void ConvertLevels()
5689 {
5690   static LevelDirTree *convert_leveldir = NULL;
5691   static int convert_level_nr = -1;
5692   static int num_levels_handled = 0;
5693   static int num_levels_converted = 0;
5694   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5695   int i;
5696
5697   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5698                                                global.convert_leveldir);
5699
5700   if (convert_leveldir == NULL)
5701     Error(ERR_EXIT, "no such level identifier: '%s'",
5702           global.convert_leveldir);
5703
5704   leveldir_current = convert_leveldir;
5705
5706   if (global.convert_level_nr != -1)
5707   {
5708     convert_leveldir->first_level = global.convert_level_nr;
5709     convert_leveldir->last_level  = global.convert_level_nr;
5710   }
5711
5712   convert_level_nr = convert_leveldir->first_level;
5713
5714   printf_line("=", 79);
5715   printf("Converting levels\n");
5716   printf_line("-", 79);
5717   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5718   printf("Level series name:       '%s'\n", convert_leveldir->name);
5719   printf("Level series author:     '%s'\n", convert_leveldir->author);
5720   printf("Number of levels:        %d\n",   convert_leveldir->levels);
5721   printf_line("=", 79);
5722   printf("\n");
5723
5724   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5725     levels_failed[i] = FALSE;
5726
5727   while (convert_level_nr <= convert_leveldir->last_level)
5728   {
5729     char *level_filename;
5730     boolean new_level;
5731
5732     level_nr = convert_level_nr++;
5733
5734     printf("Level %03d: ", level_nr);
5735
5736     LoadLevel(level_nr);
5737     if (level.no_valid_file)
5738     {
5739       printf("(no level)\n");
5740       continue;
5741     }
5742
5743     printf("converting level ... ");
5744
5745     level_filename = getDefaultLevelFilename(level_nr);
5746     new_level = !fileExists(level_filename);
5747
5748     if (new_level)
5749     {
5750       SaveLevel(level_nr);
5751
5752       num_levels_converted++;
5753
5754       printf("converted.\n");
5755     }
5756     else
5757     {
5758       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5759         levels_failed[level_nr] = TRUE;
5760
5761       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5762     }
5763
5764     num_levels_handled++;
5765   }
5766
5767   printf("\n");
5768   printf_line("=", 79);
5769   printf("Number of levels handled: %d\n", num_levels_handled);
5770   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5771          (num_levels_handled ?
5772           num_levels_converted * 100 / num_levels_handled : 0));
5773   printf_line("-", 79);
5774   printf("Summary (for automatic parsing by scripts):\n");
5775   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5776          convert_leveldir->identifier, num_levels_converted,
5777          num_levels_handled,
5778          (num_levels_handled ?
5779           num_levels_converted * 100 / num_levels_handled : 0));
5780
5781   if (num_levels_handled != num_levels_converted)
5782   {
5783     printf(", FAILED:");
5784     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5785       if (levels_failed[i])
5786         printf(" %03d", i);
5787   }
5788
5789   printf("\n");
5790   printf_line("=", 79);
5791
5792   CloseAllAndExit(0);
5793 }