rnd-20040918-1-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16 #include <dirent.h>
17 #include <math.h>
18
19 #include "libgame/libgame.h"
20
21 #include "files.h"
22 #include "init.h"
23 #include "tools.h"
24 #include "tape.h"
25
26
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
28 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
29 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
30 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
32 #define LEVEL_HEADER_UNUSED     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_UNKNOWN                      },
1864     { Xsteel_2,                 EL_UNKNOWN                      },
1865     { Xsteel_3,                 EL_UNKNOWN                      },
1866     { Xsteel_4,                 EL_UNKNOWN                      },
1867     { Xwall_1,                  EL_UNKNOWN                      },
1868     { Xwall_2,                  EL_UNKNOWN                      },
1869     { Xwall_3,                  EL_UNKNOWN                      },
1870     { Xwall_4,                  EL_UNKNOWN                      },
1871     { Xround_wall_1,            EL_UNKNOWN                      },
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_UNKNOWN                      },
1877     { Xdecor_3,                 EL_UNKNOWN                      },
1878     { Xdecor_4,                 EL_UNKNOWN                      },
1879     { Xdecor_5,                 EL_UNKNOWN                      },
1880     { Xdecor_6,                 EL_UNKNOWN                      },
1881     { Xdecor_7,                 EL_UNKNOWN                      },
1882     { Xdecor_8,                 EL_UNKNOWN                      },
1883     { Xdecor_9,                 EL_UNKNOWN                      },
1884     { Xdecor_10,                EL_UNKNOWN                      },
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   CopyNativeLevel_EM_to_RND(level);
2528 }
2529
2530 #endif
2531
2532
2533 /* ------------------------------------------------------------------------- */
2534 /* functions for loading SP level                                            */
2535 /* ------------------------------------------------------------------------- */
2536
2537 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
2538 #define SP_LEVEL_SIZE                   1536
2539 #define SP_LEVEL_XSIZE                  60
2540 #define SP_LEVEL_YSIZE                  24
2541 #define SP_LEVEL_NAME_LEN               23
2542
2543 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
2544                                        int nr)
2545 {
2546   int num_special_ports;
2547   int i, x, y;
2548
2549   /* for details of the Supaplex level format, see Herman Perk's Supaplex
2550      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
2551
2552   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
2553   for (y = 0; y < SP_LEVEL_YSIZE; y++)
2554   {
2555     for (x = 0; x < SP_LEVEL_XSIZE; x++)
2556     {
2557       int element_old = fgetc(file);
2558       int element_new;
2559
2560       if (element_old <= 0x27)
2561         element_new = getMappedElement(EL_SP_START + element_old);
2562       else if (element_old == 0x28)
2563         element_new = EL_INVISIBLE_WALL;
2564       else
2565       {
2566         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
2567         Error(ERR_WARN, "invalid level element %d", element_old);
2568
2569         element_new = EL_UNKNOWN;
2570       }
2571
2572       level->field[x][y] = element_new;
2573     }
2574   }
2575
2576   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
2577
2578   /* initial gravity: 1 == "on", anything else (0) == "off" */
2579   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
2580
2581   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
2582
2583   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
2584   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
2585     level->name[i] = fgetc(file);
2586   level->name[SP_LEVEL_NAME_LEN] = '\0';
2587
2588   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
2589   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
2590
2591   /* number of infotrons needed; 0 means that Supaplex will count the total
2592      amount of infotrons in the level and use the low byte of that number
2593      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
2594   level->gems_needed = fgetc(file);
2595
2596   /* number of special ("gravity") port entries below (maximum 10 allowed) */
2597   num_special_ports = fgetc(file);
2598
2599   /* database of properties of up to 10 special ports (6 bytes per port) */
2600   for (i = 0; i < 10; i++)
2601   {
2602     int port_location, port_x, port_y, port_element;
2603     int gravity;
2604
2605     /* high and low byte of the location of a special port; if (x, y) are the
2606        coordinates of a port in the field and (0, 0) is the top-left corner,
2607        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
2608        of what may be expected: Supaplex works with a game field in memory
2609        which is 2 bytes per tile) */
2610     port_location = getFile16BitBE(file);
2611
2612     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
2613     gravity = fgetc(file);
2614
2615     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
2616     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
2617
2618     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
2619     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
2620
2621     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
2622
2623     if (i >= num_special_ports)
2624       continue;
2625
2626     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2627     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2628
2629     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2630         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2631     {
2632       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2633             port_x, port_y);
2634
2635       continue;
2636     }
2637
2638     port_element = level->field[port_x][port_y];
2639
2640     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2641         port_element > EL_SP_GRAVITY_PORT_UP)
2642     {
2643       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2644
2645       continue;
2646     }
2647
2648     /* change previous (wrong) gravity inverting special port to either
2649        gravity enabling special port or gravity disabling special port */
2650     level->field[port_x][port_y] +=
2651       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2652        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2653   }
2654
2655   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
2656
2657   /* change special gravity ports without database entries to normal ports */
2658   for (y = 0; y < SP_LEVEL_YSIZE; y++)
2659     for (x = 0; x < SP_LEVEL_XSIZE; x++)
2660       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2661           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2662         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2663
2664   /* auto-determine number of infotrons if it was stored as "0" -- see above */
2665   if (level->gems_needed == 0)
2666   {
2667     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2668       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2669         if (level->field[x][y] == EL_SP_INFOTRON)
2670           level->gems_needed++;
2671
2672     level->gems_needed &= 0xff;         /* only use low byte -- see above */
2673   }
2674
2675   level->fieldx = SP_LEVEL_XSIZE;
2676   level->fieldy = SP_LEVEL_YSIZE;
2677
2678   level->time = 0;                      /* no time limit */
2679   level->amoeba_speed = 0;
2680   level->time_magic_wall = 0;
2681   level->time_wheel = 0;
2682   level->amoeba_content = EL_EMPTY;
2683
2684   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2685     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
2686
2687   /* there are no yamyams in supaplex levels */
2688   for (i = 0; i < level->num_yamyam_contents; i++)
2689     for (y = 0; y < 3; y++)
2690       for (x = 0; x < 3; x++)
2691         level->yamyam_content[i][x][y] = EL_EMPTY;
2692 }
2693
2694 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2695                                      struct LevelFileInfo *level_file_info)
2696 {
2697   char *filename = level_file_info->filename;
2698   FILE *file;
2699   int nr = level_file_info->nr - leveldir_current->first_level;
2700   int i, l, x, y;
2701   char name_first, name_last;
2702   struct LevelInfo multipart_level;
2703   int multipart_xpos, multipart_ypos;
2704   boolean is_multipart_level;
2705   boolean is_first_part;
2706   boolean reading_multipart_level = FALSE;
2707   boolean use_empty_level = FALSE;
2708
2709   if (!(file = fopen(filename, MODE_READ)))
2710   {
2711     level->no_valid_file = TRUE;
2712
2713     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2714
2715     return;
2716   }
2717
2718   /* position file stream to the requested level inside the level package */
2719   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2720   {
2721     level->no_valid_file = TRUE;
2722
2723     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2724
2725     return;
2726   }
2727
2728   /* there exist Supaplex level package files with multi-part levels which
2729      can be detected as follows: instead of leading and trailing dashes ('-')
2730      to pad the level name, they have leading and trailing numbers which are
2731      the x and y coordinations of the current part of the multi-part level;
2732      if there are '?' characters instead of numbers on the left or right side
2733      of the level name, the multi-part level consists of only horizontal or
2734      vertical parts */
2735
2736   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2737   {
2738     LoadLevelFromFileStream_SP(file, level, l);
2739
2740     /* check if this level is a part of a bigger multi-part level */
2741
2742     name_first = level->name[0];
2743     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
2744
2745     is_multipart_level =
2746       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2747        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
2748
2749     is_first_part =
2750       ((name_first == '?' || name_first == '1') &&
2751        (name_last  == '?' || name_last  == '1'));
2752
2753     /* correct leading multipart level meta information in level name */
2754     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2755       level->name[i] = '-';
2756
2757     /* correct trailing multipart level meta information in level name */
2758     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2759       level->name[i] = '-';
2760
2761     /* ---------- check for normal single level ---------- */
2762
2763     if (!reading_multipart_level && !is_multipart_level)
2764     {
2765       /* the current level is simply a normal single-part level, and we are
2766          not reading a multi-part level yet, so return the level as it is */
2767
2768       break;
2769     }
2770
2771     /* ---------- check for empty level (unused multi-part) ---------- */
2772
2773     if (!reading_multipart_level && is_multipart_level && !is_first_part)
2774     {
2775       /* this is a part of a multi-part level, but not the first part
2776          (and we are not already reading parts of a multi-part level);
2777          in this case, use an empty level instead of the single part */
2778
2779       use_empty_level = TRUE;
2780
2781       break;
2782     }
2783
2784     /* ---------- check for finished multi-part level ---------- */
2785
2786     if (reading_multipart_level &&
2787         (!is_multipart_level ||
2788          strcmp(level->name, multipart_level.name) != 0))
2789     {
2790       /* we are already reading parts of a multi-part level, but this level is
2791          either not a multi-part level, or a part of a different multi-part
2792          level; in both cases, the multi-part level seems to be complete */
2793
2794       break;
2795     }
2796
2797     /* ---------- here we have one part of a multi-part level ---------- */
2798
2799     reading_multipart_level = TRUE;
2800
2801     if (is_first_part)  /* start with first part of new multi-part level */
2802     {
2803       /* copy level info structure from first part */
2804       multipart_level = *level;
2805
2806       /* clear playfield of new multi-part level */
2807       for (y = 0; y < MAX_LEV_FIELDY; y++)
2808         for (x = 0; x < MAX_LEV_FIELDX; x++)
2809           multipart_level.field[x][y] = EL_EMPTY;
2810     }
2811
2812     if (name_first == '?')
2813       name_first = '1';
2814     if (name_last == '?')
2815       name_last = '1';
2816
2817     multipart_xpos = (int)(name_first - '0');
2818     multipart_ypos = (int)(name_last  - '0');
2819
2820 #if 0
2821     printf("----------> part (%d/%d) of multi-part level '%s'\n",
2822            multipart_xpos, multipart_ypos, multipart_level.name);
2823 #endif
2824
2825     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2826         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2827     {
2828       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2829
2830       break;
2831     }
2832
2833     multipart_level.fieldx = MAX(multipart_level.fieldx,
2834                                  multipart_xpos * SP_LEVEL_XSIZE);
2835     multipart_level.fieldy = MAX(multipart_level.fieldy,
2836                                  multipart_ypos * SP_LEVEL_YSIZE);
2837
2838     /* copy level part at the right position of multi-part level */
2839     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2840     {
2841       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2842       {
2843         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2844         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2845
2846         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2847       }
2848     }
2849   }
2850
2851   fclose(file);
2852
2853   if (use_empty_level)
2854   {
2855     setLevelInfoToDefaults(level);
2856
2857     level->fieldx = SP_LEVEL_XSIZE;
2858     level->fieldy = SP_LEVEL_YSIZE;
2859
2860     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2861       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2862         level->field[x][y] = EL_EMPTY;
2863
2864     strcpy(level->name, "-------- EMPTY --------");
2865
2866     Error(ERR_WARN, "single part of multi-part level -- using empty level");
2867   }
2868
2869   if (reading_multipart_level)
2870     *level = multipart_level;
2871 }
2872
2873 /* ------------------------------------------------------------------------- */
2874 /* functions for loading generic level                                       */
2875 /* ------------------------------------------------------------------------- */
2876
2877 void LoadLevelFromFileInfo(struct LevelInfo *level,
2878                            struct LevelFileInfo *level_file_info)
2879 {
2880   /* always start with reliable default values */
2881   setLevelInfoToDefaults(level);
2882
2883   switch (level_file_info->type)
2884   {
2885     case LEVEL_FILE_TYPE_RND:
2886       LoadLevelFromFileInfo_RND(level, level_file_info);
2887       break;
2888
2889     case LEVEL_FILE_TYPE_EM:
2890       LoadLevelFromFileInfo_EM(level, level_file_info);
2891       level->game_engine_type = GAME_ENGINE_TYPE_EM;
2892       break;
2893
2894     case LEVEL_FILE_TYPE_SP:
2895       LoadLevelFromFileInfo_SP(level, level_file_info);
2896       break;
2897
2898     default:
2899       LoadLevelFromFileInfo_RND(level, level_file_info);
2900       break;
2901   }
2902
2903   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2904     level->game_engine_type = GAME_ENGINE_TYPE_RND;
2905 }
2906
2907 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2908 {
2909   static struct LevelFileInfo level_file_info;
2910
2911   /* always start with reliable default values */
2912   setFileInfoToDefaults(&level_file_info);
2913
2914   level_file_info.nr = 0;                       /* unknown level number */
2915   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
2916   level_file_info.filename = filename;
2917
2918   LoadLevelFromFileInfo(level, &level_file_info);
2919 }
2920
2921 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2922 {
2923   if (leveldir_current == NULL)         /* only when dumping level */
2924     return;
2925
2926 #if 0
2927   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2928 #endif
2929
2930   /* determine correct game engine version of current level */
2931 #if 1
2932   if (!leveldir_current->latest_engine)
2933 #else
2934   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2935       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2936       IS_LEVELCLASS_UNDEFINED(leveldir_current))
2937 #endif
2938   {
2939 #if 0
2940     printf("\n::: This level is private or contributed: '%s'\n", filename);
2941 #endif
2942
2943 #if 0
2944     printf("\n::: Use the stored game engine version for this level\n");
2945 #endif
2946
2947     /* For all levels which are not forced to use the latest game engine
2948        version (normally user contributed, private and undefined levels),
2949        use the version of the game engine the levels were created for.
2950
2951        Since 2.0.1, the game engine version is now directly stored
2952        in the level file (chunk "VERS"), so there is no need anymore
2953        to set the game version from the file version (except for old,
2954        pre-2.0 levels, where the game version is still taken from the
2955        file format version used to store the level -- see above). */
2956
2957 #if 1
2958     /* player was faster than enemies in 1.0.0 and before */
2959     if (level->file_version == FILE_VERSION_1_0)
2960       level->double_speed = TRUE;
2961 #else
2962     /* do some special adjustments to support older level versions */
2963     if (level->file_version == FILE_VERSION_1_0)
2964     {
2965       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2966       Error(ERR_WARN, "using high speed movement for player");
2967
2968       /* player was faster than monsters in (pre-)1.0 levels */
2969       level->double_speed = TRUE;
2970     }
2971 #endif
2972
2973     /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2974     if (level->game_version == VERSION_IDENT(2,0,1,0))
2975       level->em_slippery_gems = TRUE;
2976
2977     /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2978     if (level->game_version < VERSION_IDENT(2,2,0,0))
2979       level->use_spring_bug = TRUE;
2980
2981     /* only few elements were able to actively move into acid before 3.1.0 */
2982     if (level->game_version < VERSION_IDENT(3,1,0,0))
2983     {
2984       int i, j;
2985
2986       level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2987       level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2988
2989       setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
2990       setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2991       setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
2992       setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
2993
2994       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2995         SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2996
2997       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2998       {
2999         int element = EL_CUSTOM_START + i;
3000         struct ElementInfo *ei = &element_info[element];
3001
3002         for (j = 0; j < ei->num_change_pages; j++)
3003         {
3004           struct ElementChangeInfo *change = &ei->change_page[j];
3005
3006           change->trigger_player = CH_PLAYER_ANY;
3007           change->trigger_page = CH_PAGE_ANY;
3008         }
3009       }
3010     }
3011
3012 #if 1   /* USE_NEW_BLOCK_STYLE */
3013     /* blocking the last field when moving was corrected in version 3.1.1 */
3014     if (level->game_version < VERSION_IDENT(3,1,1,0))
3015     {
3016 #if 0
3017       printf("::: %d\n", level->block_last_field);
3018 #endif
3019
3020       /* even "not blocking" was blocking the last field for one frame */
3021       level->block_delay    = (level->block_last_field    ? 7 : 1);
3022       level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
3023
3024       level->block_last_field = TRUE;
3025       level->sp_block_last_field = TRUE;
3026     }
3027 #endif
3028   }
3029   else          /* always use the latest game engine version */
3030   {
3031 #if 0
3032     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
3033            leveldir_current->sort_priority, filename);
3034 #endif
3035
3036 #if 0
3037     printf("\n::: Use latest game engine version for this level.\n");
3038 #endif
3039
3040     /* For all levels which are forced to use the latest game engine version
3041        (normally all but user contributed, private and undefined levels), set
3042        the game engine version to the actual version; this allows for actual
3043        corrections in the game engine to take effect for existing, converted
3044        levels (from "classic" or other existing games) to make the emulation
3045        of the corresponding game more accurate, while (hopefully) not breaking
3046        existing levels created from other players. */
3047
3048 #if 0
3049     printf("::: changing engine from %d to %d\n",
3050            level->game_version, GAME_VERSION_ACTUAL);
3051 #endif
3052
3053     level->game_version = GAME_VERSION_ACTUAL;
3054
3055     /* Set special EM style gems behaviour: EM style gems slip down from
3056        normal, steel and growing wall. As this is a more fundamental change,
3057        it seems better to set the default behaviour to "off" (as it is more
3058        natural) and make it configurable in the level editor (as a property
3059        of gem style elements). Already existing converted levels (neither
3060        private nor contributed levels) are changed to the new behaviour. */
3061
3062     if (level->file_version < FILE_VERSION_2_0)
3063       level->em_slippery_gems = TRUE;
3064   }
3065
3066 #if 0
3067   printf("::: => %d\n", level->game_version);
3068 #endif
3069 }
3070
3071 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
3072 {
3073   int i, j, x, y;
3074
3075   /* map custom element change events that have changed in newer versions
3076      (these following values were accidentally changed in version 3.0.1) */
3077   if (level->game_version <= VERSION_IDENT(3,0,0,0))
3078   {
3079     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3080     {
3081       int element = EL_CUSTOM_START + i;
3082
3083       /* order of checking and copying events to be mapped is important */
3084       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
3085       {
3086         if (HAS_CHANGE_EVENT(element, j - 2))
3087         {
3088           SET_CHANGE_EVENT(element, j - 2, FALSE);
3089           SET_CHANGE_EVENT(element, j, TRUE);
3090         }
3091       }
3092
3093       /* order of checking and copying events to be mapped is important */
3094       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
3095       {
3096         if (HAS_CHANGE_EVENT(element, j - 1))
3097         {
3098           SET_CHANGE_EVENT(element, j - 1, FALSE);
3099           SET_CHANGE_EVENT(element, j, TRUE);
3100         }
3101       }
3102     }
3103   }
3104
3105   /* some custom element change events get mapped since version 3.0.3 */
3106   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3107   {
3108     int element = EL_CUSTOM_START + i;
3109
3110     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
3111         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
3112     {
3113       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
3114       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
3115
3116       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
3117     }
3118   }
3119
3120   /* initialize "can_change" field for old levels with only one change page */
3121   if (level->game_version <= VERSION_IDENT(3,0,2,0))
3122   {
3123     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3124     {
3125       int element = EL_CUSTOM_START + i;
3126
3127       if (CAN_CHANGE(element))
3128         element_info[element].change->can_change = TRUE;
3129     }
3130   }
3131
3132   /* correct custom element values (for old levels without these options) */
3133   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3134   {
3135     int element = EL_CUSTOM_START + i;
3136     struct ElementInfo *ei = &element_info[element];
3137
3138     if (ei->access_direction == MV_NO_MOVING)
3139       ei->access_direction = MV_ALL_DIRECTIONS;
3140
3141     for (j = 0; j < ei->num_change_pages; j++)
3142     {
3143       struct ElementChangeInfo *change = &ei->change_page[j];
3144
3145       if (change->trigger_side == CH_SIDE_NONE)
3146         change->trigger_side = CH_SIDE_ANY;
3147     }
3148   }
3149
3150   /* initialize "can_explode" field for old levels which did not store this */
3151   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
3152   if (level->game_version <= VERSION_IDENT(3,1,0,0))
3153   {
3154     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3155     {
3156       int element = EL_CUSTOM_START + i;
3157
3158       if (EXPLODES_1X1_OLD(element))
3159         element_info[element].explosion_type = EXPLODES_1X1;
3160
3161       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
3162                                              EXPLODES_SMASHED(element) ||
3163                                              EXPLODES_IMPACT(element)));
3164     }
3165   }
3166
3167   /* correct previously hard-coded move delay values for maze runner style */
3168   if (level->game_version < VERSION_IDENT(3,1,1,0))
3169   {
3170     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3171     {
3172       int element = EL_CUSTOM_START + i;
3173
3174       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
3175       {
3176         /* previously hard-coded and therefore ignored */
3177         element_info[element].move_delay_fixed = 9;
3178         element_info[element].move_delay_random = 0;
3179       }
3180     }
3181   }
3182
3183 #if 0
3184   /* set default push delay values (corrected since version 3.0.7-1) */
3185   if (level->game_version < VERSION_IDENT(3,0,7,1))
3186   {
3187     game.default_push_delay_fixed = 2;
3188     game.default_push_delay_random = 8;
3189   }
3190   else
3191   {
3192     game.default_push_delay_fixed = 8;
3193     game.default_push_delay_random = 8;
3194   }
3195
3196   /* set uninitialized push delay values of custom elements in older levels */
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200
3201     if (element_info[element].push_delay_fixed == -1)
3202       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
3203     if (element_info[element].push_delay_random == -1)
3204       element_info[element].push_delay_random = game.default_push_delay_random;
3205   }
3206 #endif
3207
3208   /* map elements that have changed in newer versions */
3209   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
3210                                                     level->game_version);
3211   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3212     for (x = 0; x < 3; x++)
3213       for (y = 0; y < 3; y++)
3214         level->yamyam_content[i][x][y] =
3215           getMappedElementByVersion(level->yamyam_content[i][x][y],
3216                                     level->game_version);
3217
3218   /* initialize element properties for level editor etc. */
3219   InitElementPropertiesEngine(level->game_version);
3220 }
3221
3222 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
3223 {
3224   int x, y;
3225
3226   /* map elements that have changed in newer versions */
3227   for (y = 0; y < level->fieldy; y++)
3228   {
3229     for (x = 0; x < level->fieldx; x++)
3230     {
3231       int element = level->field[x][y];
3232
3233 #if 1
3234       element = getMappedElementByVersion(element, level->game_version);
3235 #else
3236       if (level->game_version <= VERSION_IDENT(2,2,0,0))
3237       {
3238         /* map game font elements */
3239         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3240                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3241                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3242                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3243       }
3244
3245       if (level->game_version < VERSION_IDENT(3,0,0,0))
3246       {
3247         /* map Supaplex gravity tube elements */
3248         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3249                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3250                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3251                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3252                    element);
3253       }
3254 #endif
3255
3256       level->field[x][y] = element;
3257     }
3258   }
3259
3260   /* copy elements to runtime playfield array */
3261   for (x = 0; x < MAX_LEV_FIELDX; x++)
3262     for (y = 0; y < MAX_LEV_FIELDY; y++)
3263       Feld[x][y] = level->field[x][y];
3264
3265   /* initialize level size variables for faster access */
3266   lev_fieldx = level->fieldx;
3267   lev_fieldy = level->fieldy;
3268
3269   /* determine border element for this level */
3270   SetBorderElement();
3271 }
3272
3273 void LoadLevelTemplate(int nr)
3274 {
3275 #if 1
3276   char *filename;
3277
3278   setLevelFileInfo(&level_template.file_info, nr);
3279   filename = level_template.file_info.filename;
3280
3281   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
3282
3283 #else
3284
3285 #if 1
3286   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3287   char *filename = level_file_info->filename;
3288
3289   LoadLevelFromFileInfo(&level_template, level_file_info);
3290 #else
3291   char *filename = getDefaultLevelFilename(nr);
3292
3293   LoadLevelFromFilename_RND(&level_template, filename);
3294 #endif
3295 #endif
3296
3297 #if 1
3298   LoadLevel_InitVersion(&level_template, filename);
3299   LoadLevel_InitElements(&level_template, filename);
3300 #else
3301   LoadLevel_InitVersion(&level, filename);
3302   LoadLevel_InitElements(&level, filename);
3303 #endif
3304
3305   ActivateLevelTemplate();
3306 }
3307
3308 void LoadLevel(int nr)
3309 {
3310 #if 1
3311   char *filename;
3312
3313   setLevelFileInfo(&level.file_info, nr);
3314   filename = level.file_info.filename;
3315
3316   LoadLevelFromFileInfo(&level, &level.file_info);
3317
3318 #else
3319
3320 #if 1
3321   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
3322   char *filename = level_file_info->filename;
3323
3324   LoadLevelFromFileInfo(&level, level_file_info);
3325 #else
3326   char *filename = getLevelFilename(nr);
3327
3328   LoadLevelFromFilename_RND(&level, filename);
3329 #endif
3330 #endif
3331
3332   if (level.use_custom_template)
3333     LoadLevelTemplate(-1);
3334
3335 #if 1
3336   LoadLevel_InitVersion(&level, filename);
3337   LoadLevel_InitElements(&level, filename);
3338   LoadLevel_InitPlayfield(&level, filename);
3339 #else
3340   LoadLevel_InitLevel(&level, filename);
3341 #endif
3342 }
3343
3344 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
3345 {
3346   putFileVersion(file, level->file_version);
3347   putFileVersion(file, level->game_version);
3348 }
3349
3350 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
3351 {
3352   int i, x, y;
3353
3354   putFile8Bit(file, level->fieldx);
3355   putFile8Bit(file, level->fieldy);
3356
3357   putFile16BitBE(file, level->time);
3358   putFile16BitBE(file, level->gems_needed);
3359
3360   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3361     putFile8Bit(file, level->name[i]);
3362
3363   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3364     putFile8Bit(file, level->score[i]);
3365
3366   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3367     for (y = 0; y < 3; y++)
3368       for (x = 0; x < 3; x++)
3369         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3370                            level->yamyam_content[i][x][y]));
3371   putFile8Bit(file, level->amoeba_speed);
3372   putFile8Bit(file, level->time_magic_wall);
3373   putFile8Bit(file, level->time_wheel);
3374   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3375                      level->amoeba_content));
3376   putFile8Bit(file, (level->double_speed ? 1 : 0));
3377   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3378   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3379   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3380
3381   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3382
3383   putFile8Bit(file, (level->block_last_field ? 1 : 0));
3384   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3385   putFile32BitBE(file, level->can_move_into_acid_bits);
3386   putFile8Bit(file, level->dont_collide_with_bits);
3387
3388   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3389   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3390
3391   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3392   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3393   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3394
3395   putFile8Bit(file, level->game_engine_type);
3396
3397   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3398 }
3399
3400 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3401 {
3402   int i;
3403
3404   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3405     putFile8Bit(file, level->author[i]);
3406 }
3407
3408 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3409 {
3410   int x, y;
3411
3412   for (y = 0; y < level->fieldy; y++) 
3413     for (x = 0; x < level->fieldx; x++) 
3414       if (level->encoding_16bit_field)
3415         putFile16BitBE(file, level->field[x][y]);
3416       else
3417         putFile8Bit(file, level->field[x][y]);
3418 }
3419
3420 #if 0
3421 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3422 {
3423   int i, x, y;
3424
3425   putFile8Bit(file, EL_YAMYAM);
3426   putFile8Bit(file, level->num_yamyam_contents);
3427   putFile8Bit(file, 0);
3428   putFile8Bit(file, 0);
3429
3430   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3431     for (y = 0; y < 3; y++)
3432       for (x = 0; x < 3; x++)
3433         if (level->encoding_16bit_field)
3434           putFile16BitBE(file, level->yamyam_content[i][x][y]);
3435         else
3436           putFile8Bit(file, level->yamyam_content[i][x][y]);
3437 }
3438 #endif
3439
3440 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3441 {
3442   int i, x, y;
3443   int num_contents, content_xsize, content_ysize;
3444   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3445
3446   if (element == EL_YAMYAM)
3447   {
3448     num_contents = level->num_yamyam_contents;
3449     content_xsize = 3;
3450     content_ysize = 3;
3451
3452     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3453       for (y = 0; y < 3; y++)
3454         for (x = 0; x < 3; x++)
3455           content_array[i][x][y] = level->yamyam_content[i][x][y];
3456   }
3457   else if (element == EL_BD_AMOEBA)
3458   {
3459     num_contents = 1;
3460     content_xsize = 1;
3461     content_ysize = 1;
3462
3463     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3464       for (y = 0; y < 3; y++)
3465         for (x = 0; x < 3; x++)
3466           content_array[i][x][y] = EL_EMPTY;
3467     content_array[0][0][0] = level->amoeba_content;
3468   }
3469   else
3470   {
3471     /* chunk header already written -- write empty chunk data */
3472     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3473
3474     Error(ERR_WARN, "cannot save content for element '%d'", element);
3475     return;
3476   }
3477
3478   putFile16BitBE(file, element);
3479   putFile8Bit(file, num_contents);
3480   putFile8Bit(file, content_xsize);
3481   putFile8Bit(file, content_ysize);
3482
3483   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3484
3485   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3486     for (y = 0; y < 3; y++)
3487       for (x = 0; x < 3; x++)
3488         putFile16BitBE(file, content_array[i][x][y]);
3489 }
3490
3491 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3492 {
3493   int i;
3494   int envelope_nr = element - EL_ENVELOPE_1;
3495   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3496
3497   putFile16BitBE(file, element);
3498   putFile16BitBE(file, envelope_len);
3499   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3500   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3501
3502   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3503
3504   for (i = 0; i < envelope_len; i++)
3505     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3506 }
3507
3508 #if 0
3509 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3510                            int num_changed_custom_elements)
3511 {
3512   int i, check = 0;
3513
3514   putFile16BitBE(file, num_changed_custom_elements);
3515
3516   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3517   {
3518     int element = EL_CUSTOM_START + i;
3519
3520     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3521     {
3522       if (check < num_changed_custom_elements)
3523       {
3524         putFile16BitBE(file, element);
3525         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3526       }
3527
3528       check++;
3529     }
3530   }
3531
3532   if (check != num_changed_custom_elements)     /* should not happen */
3533     Error(ERR_WARN, "inconsistent number of custom element properties");
3534 }
3535 #endif
3536
3537 #if 0
3538 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3539                            int num_changed_custom_elements)
3540 {
3541   int i, check = 0;
3542
3543   putFile16BitBE(file, num_changed_custom_elements);
3544
3545   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3546   {
3547     int element = EL_CUSTOM_START + i;
3548
3549     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3550     {
3551       if (check < num_changed_custom_elements)
3552       {
3553         putFile16BitBE(file, element);
3554         putFile16BitBE(file, element_info[element].change->target_element);
3555       }
3556
3557       check++;
3558     }
3559   }
3560
3561   if (check != num_changed_custom_elements)     /* should not happen */
3562     Error(ERR_WARN, "inconsistent number of custom target elements");
3563 }
3564 #endif
3565
3566 #if 0
3567 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3568                            int num_changed_custom_elements)
3569 {
3570   int i, j, x, y, check = 0;
3571
3572   putFile16BitBE(file, num_changed_custom_elements);
3573
3574   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3575   {
3576     int element = EL_CUSTOM_START + i;
3577
3578     if (element_info[element].modified_settings)
3579     {
3580       if (check < num_changed_custom_elements)
3581       {
3582         putFile16BitBE(file, element);
3583
3584         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3585           putFile8Bit(file, element_info[element].description[j]);
3586
3587         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3588
3589         /* some free bytes for future properties and padding */
3590         WriteUnusedBytesToFile(file, 7);
3591
3592         putFile8Bit(file, element_info[element].use_gfx_element);
3593         putFile16BitBE(file, element_info[element].gfx_element);
3594
3595         putFile8Bit(file, element_info[element].collect_score);
3596         putFile8Bit(file, element_info[element].collect_count);
3597
3598         putFile16BitBE(file, element_info[element].push_delay_fixed);
3599         putFile16BitBE(file, element_info[element].push_delay_random);
3600         putFile16BitBE(file, element_info[element].move_delay_fixed);
3601         putFile16BitBE(file, element_info[element].move_delay_random);
3602
3603         putFile16BitBE(file, element_info[element].move_pattern);
3604         putFile8Bit(file, element_info[element].move_direction_initial);
3605         putFile8Bit(file, element_info[element].move_stepsize);
3606
3607         for (y = 0; y < 3; y++)
3608           for (x = 0; x < 3; x++)
3609             putFile16BitBE(file, element_info[element].content[x][y]);
3610
3611         putFile32BitBE(file, element_info[element].change->events);
3612
3613         putFile16BitBE(file, element_info[element].change->target_element);
3614
3615         putFile16BitBE(file, element_info[element].change->delay_fixed);
3616         putFile16BitBE(file, element_info[element].change->delay_random);
3617         putFile16BitBE(file, element_info[element].change->delay_frames);
3618
3619         putFile16BitBE(file, element_info[element].change->trigger_element);
3620
3621         putFile8Bit(file, element_info[element].change->explode);
3622         putFile8Bit(file, element_info[element].change->use_target_content);
3623         putFile8Bit(file, element_info[element].change->only_if_complete);
3624         putFile8Bit(file, element_info[element].change->use_random_replace);
3625
3626         putFile8Bit(file, element_info[element].change->random_percentage);
3627         putFile8Bit(file, element_info[element].change->replace_when);
3628
3629         for (y = 0; y < 3; y++)
3630           for (x = 0; x < 3; x++)
3631             putFile16BitBE(file, element_info[element].change->content[x][y]);
3632
3633         putFile8Bit(file, element_info[element].slippery_type);
3634
3635         /* some free bytes for future properties and padding */
3636         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3637       }
3638
3639       check++;
3640     }
3641   }
3642
3643   if (check != num_changed_custom_elements)     /* should not happen */
3644     Error(ERR_WARN, "inconsistent number of custom element properties");
3645 }
3646 #endif
3647
3648 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3649 {
3650   struct ElementInfo *ei = &element_info[element];
3651   int i, x, y;
3652
3653   putFile16BitBE(file, element);
3654
3655   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3656     putFile8Bit(file, ei->description[i]);
3657
3658   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3659   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
3660
3661   putFile8Bit(file, ei->num_change_pages);
3662
3663   /* some free bytes for future base property values and padding */
3664   WriteUnusedBytesToFile(file, 5);
3665
3666   /* write custom property values */
3667
3668   putFile8Bit(file, ei->use_gfx_element);
3669   putFile16BitBE(file, ei->gfx_element);
3670
3671   putFile8Bit(file, ei->collect_score);
3672   putFile8Bit(file, ei->collect_count);
3673
3674   putFile8Bit(file, ei->drop_delay_fixed);
3675   putFile8Bit(file, ei->push_delay_fixed);
3676   putFile8Bit(file, ei->drop_delay_random);
3677   putFile8Bit(file, ei->push_delay_random);
3678   putFile16BitBE(file, ei->move_delay_fixed);
3679   putFile16BitBE(file, ei->move_delay_random);
3680
3681   /* bits 0 - 15 of "move_pattern" ... */
3682   putFile16BitBE(file, ei->move_pattern & 0xffff);
3683   putFile8Bit(file, ei->move_direction_initial);
3684   putFile8Bit(file, ei->move_stepsize);
3685
3686   putFile8Bit(file, ei->slippery_type);
3687
3688   for (y = 0; y < 3; y++)
3689     for (x = 0; x < 3; x++)
3690       putFile16BitBE(file, ei->content[x][y]);
3691
3692   putFile16BitBE(file, ei->move_enter_element);
3693   putFile16BitBE(file, ei->move_leave_element);
3694   putFile8Bit(file, ei->move_leave_type);
3695
3696   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3697   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3698
3699   putFile8Bit(file, ei->access_direction);
3700
3701   putFile8Bit(file, ei->explosion_delay);
3702   putFile8Bit(file, ei->ignition_delay);
3703   putFile8Bit(file, ei->explosion_type);
3704
3705   /* some free bytes for future custom property values and padding */
3706   WriteUnusedBytesToFile(file, 1);
3707
3708   /* write change property values */
3709
3710   for (i = 0; i < ei->num_change_pages; i++)
3711   {
3712     struct ElementChangeInfo *change = &ei->change_page[i];
3713
3714     putFile32BitBE(file, change->events);
3715
3716     putFile16BitBE(file, change->target_element);
3717
3718     putFile16BitBE(file, change->delay_fixed);
3719     putFile16BitBE(file, change->delay_random);
3720     putFile16BitBE(file, change->delay_frames);
3721
3722     putFile16BitBE(file, change->trigger_element);
3723
3724     putFile8Bit(file, change->explode);
3725     putFile8Bit(file, change->use_target_content);
3726     putFile8Bit(file, change->only_if_complete);
3727     putFile8Bit(file, change->use_random_replace);
3728
3729     putFile8Bit(file, change->random_percentage);
3730     putFile8Bit(file, change->replace_when);
3731
3732     for (y = 0; y < 3; y++)
3733       for (x = 0; x < 3; x++)
3734         putFile16BitBE(file, change->target_content[x][y]);
3735
3736     putFile8Bit(file, change->can_change);
3737
3738     putFile8Bit(file, change->trigger_side);
3739
3740 #if 1
3741     putFile8Bit(file, change->trigger_player);
3742     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3743                        log_2(change->trigger_page)));
3744
3745     /* some free bytes for future change property values and padding */
3746     WriteUnusedBytesToFile(file, 6);
3747
3748 #else
3749
3750     /* some free bytes for future change property values and padding */
3751     WriteUnusedBytesToFile(file, 8);
3752 #endif
3753   }
3754 }
3755
3756 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3757 {
3758   struct ElementInfo *ei = &element_info[element];
3759   struct ElementGroupInfo *group = ei->group;
3760   int i;
3761
3762   putFile16BitBE(file, element);
3763
3764   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3765     putFile8Bit(file, ei->description[i]);
3766
3767   putFile8Bit(file, group->num_elements);
3768
3769   putFile8Bit(file, ei->use_gfx_element);
3770   putFile16BitBE(file, ei->gfx_element);
3771
3772   putFile8Bit(file, group->choice_mode);
3773
3774   /* some free bytes for future values and padding */
3775   WriteUnusedBytesToFile(file, 3);
3776
3777   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3778     putFile16BitBE(file, group->element[i]);
3779 }
3780
3781 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3782 {
3783   int body_chunk_size;
3784   int i, x, y;
3785   FILE *file;
3786
3787   if (!(file = fopen(filename, MODE_WRITE)))
3788   {
3789     Error(ERR_WARN, "cannot save level file '%s'", filename);
3790     return;
3791   }
3792
3793   level->file_version = FILE_VERSION_ACTUAL;
3794   level->game_version = GAME_VERSION_ACTUAL;
3795
3796   /* check level field for 16-bit elements */
3797   level->encoding_16bit_field = FALSE;
3798   for (y = 0; y < level->fieldy; y++) 
3799     for (x = 0; x < level->fieldx; x++) 
3800       if (level->field[x][y] > 255)
3801         level->encoding_16bit_field = TRUE;
3802
3803   /* check yamyam content for 16-bit elements */
3804   level->encoding_16bit_yamyam = FALSE;
3805   for (i = 0; i < level->num_yamyam_contents; i++)
3806     for (y = 0; y < 3; y++)
3807       for (x = 0; x < 3; x++)
3808         if (level->yamyam_content[i][x][y] > 255)
3809           level->encoding_16bit_yamyam = TRUE;
3810
3811   /* check amoeba content for 16-bit elements */
3812   level->encoding_16bit_amoeba = FALSE;
3813   if (level->amoeba_content > 255)
3814     level->encoding_16bit_amoeba = TRUE;
3815
3816   /* calculate size of "BODY" chunk */
3817   body_chunk_size =
3818     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3819
3820   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3821   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3822
3823   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3824   SaveLevel_VERS(file, level);
3825
3826   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3827   SaveLevel_HEAD(file, level);
3828
3829   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3830   SaveLevel_AUTH(file, level);
3831
3832   putFileChunkBE(file, "BODY", body_chunk_size);
3833   SaveLevel_BODY(file, level);
3834
3835   if (level->encoding_16bit_yamyam ||
3836       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3837   {
3838     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3839     SaveLevel_CNT2(file, level, EL_YAMYAM);
3840   }
3841
3842   if (level->encoding_16bit_amoeba)
3843   {
3844     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3845     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3846   }
3847
3848   /* check for envelope content */
3849   for (i = 0; i < 4; i++)
3850   {
3851     if (strlen(level->envelope_text[i]) > 0)
3852     {
3853       int envelope_len = strlen(level->envelope_text[i]) + 1;
3854
3855       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3856       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3857     }
3858   }
3859
3860   /* check for non-default custom elements (unless using template level) */
3861   if (!level->use_custom_template)
3862   {
3863     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3864     {
3865       int element = EL_CUSTOM_START + i;
3866
3867       if (element_info[element].modified_settings)
3868       {
3869         int num_change_pages = element_info[element].num_change_pages;
3870
3871         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3872         SaveLevel_CUS4(file, level, element);
3873       }
3874     }
3875   }
3876
3877   /* check for non-default group elements (unless using template level) */
3878   if (!level->use_custom_template)
3879   {
3880     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3881     {
3882       int element = EL_GROUP_START + i;
3883
3884       if (element_info[element].modified_settings)
3885       {
3886         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3887         SaveLevel_GRP1(file, level, element);
3888       }
3889     }
3890   }
3891
3892   fclose(file);
3893
3894   SetFilePermissions(filename, PERMS_PRIVATE);
3895 }
3896
3897 void SaveLevel(int nr)
3898 {
3899   char *filename = getDefaultLevelFilename(nr);
3900
3901   SaveLevelFromFilename(&level, filename);
3902 }
3903
3904 void SaveLevelTemplate()
3905 {
3906   char *filename = getDefaultLevelFilename(-1);
3907
3908   SaveLevelFromFilename(&level, filename);
3909 }
3910
3911 void DumpLevel(struct LevelInfo *level)
3912 {
3913   if (level->no_valid_file)
3914   {
3915     Error(ERR_WARN, "cannot dump -- no valid level file found");
3916
3917     return;
3918   }
3919
3920   printf_line("-", 79);
3921   printf("Level xxx (file version %08d, game version %08d)\n",
3922          level->file_version, level->game_version);
3923   printf_line("-", 79);
3924
3925   printf("Level author: '%s'\n", level->author);
3926   printf("Level title:  '%s'\n", level->name);
3927   printf("\n");
3928   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3929   printf("\n");
3930   printf("Level time:  %d seconds\n", level->time);
3931   printf("Gems needed: %d\n", level->gems_needed);
3932   printf("\n");
3933   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3934   printf("Time for wheel:      %d seconds\n", level->time_wheel);
3935   printf("Time for light:      %d seconds\n", level->time_light);
3936   printf("Time for timegate:   %d seconds\n", level->time_timegate);
3937   printf("\n");
3938   printf("Amoeba speed: %d\n", level->amoeba_speed);
3939   printf("\n");
3940   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
3941   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
3942   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
3943   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
3944   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3945   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3946   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3947
3948   printf_line("-", 79);
3949 }
3950
3951
3952 /* ========================================================================= */
3953 /* tape file functions                                                       */
3954 /* ========================================================================= */
3955
3956 static void setTapeInfoToDefaults()
3957 {
3958   int i;
3959
3960   /* always start with reliable default values (empty tape) */
3961   TapeErase();
3962
3963   /* default values (also for pre-1.2 tapes) with only the first player */
3964   tape.player_participates[0] = TRUE;
3965   for (i = 1; i < MAX_PLAYERS; i++)
3966     tape.player_participates[i] = FALSE;
3967
3968   /* at least one (default: the first) player participates in every tape */
3969   tape.num_participating_players = 1;
3970
3971   tape.level_nr = level_nr;
3972   tape.counter = 0;
3973   tape.changed = FALSE;
3974
3975   tape.recording = FALSE;
3976   tape.playing = FALSE;
3977   tape.pausing = FALSE;
3978
3979   tape.no_valid_file = FALSE;
3980 }
3981
3982 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3983 {
3984   tape->file_version = getFileVersion(file);
3985   tape->game_version = getFileVersion(file);
3986
3987   return chunk_size;
3988 }
3989
3990 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3991 {
3992   int i;
3993
3994   tape->random_seed = getFile32BitBE(file);
3995   tape->date        = getFile32BitBE(file);
3996   tape->length      = getFile32BitBE(file);
3997
3998   /* read header fields that are new since version 1.2 */
3999   if (tape->file_version >= FILE_VERSION_1_2)
4000   {
4001     byte store_participating_players = getFile8Bit(file);
4002     int engine_version;
4003
4004     /* since version 1.2, tapes store which players participate in the tape */
4005     tape->num_participating_players = 0;
4006     for (i = 0; i < MAX_PLAYERS; i++)
4007     {
4008       tape->player_participates[i] = FALSE;
4009
4010       if (store_participating_players & (1 << i))
4011       {
4012         tape->player_participates[i] = TRUE;
4013         tape->num_participating_players++;
4014       }
4015     }
4016
4017     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
4018
4019     engine_version = getFileVersion(file);
4020     if (engine_version > 0)
4021       tape->engine_version = engine_version;
4022     else
4023       tape->engine_version = tape->game_version;
4024   }
4025
4026   return chunk_size;
4027 }
4028
4029 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
4030 {
4031   int level_identifier_size;
4032   int i;
4033
4034   level_identifier_size = getFile16BitBE(file);
4035
4036   tape->level_identifier =
4037     checked_realloc(tape->level_identifier, level_identifier_size);
4038
4039   for (i = 0; i < level_identifier_size; i++)
4040     tape->level_identifier[i] = getFile8Bit(file);
4041
4042   tape->level_nr = getFile16BitBE(file);
4043
4044   chunk_size = 2 + level_identifier_size + 2;
4045
4046   return chunk_size;
4047 }
4048
4049 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
4050 {
4051   int i, j;
4052   int chunk_size_expected =
4053     (tape->num_participating_players + 1) * tape->length;
4054
4055   if (chunk_size_expected != chunk_size)
4056   {
4057     ReadUnusedBytesFromFile(file, chunk_size);
4058     return chunk_size_expected;
4059   }
4060
4061   for (i = 0; i < tape->length; i++)
4062   {
4063     if (i >= MAX_TAPELEN)
4064       break;
4065
4066     for (j = 0; j < MAX_PLAYERS; j++)
4067     {
4068       tape->pos[i].action[j] = MV_NO_MOVING;
4069
4070       if (tape->player_participates[j])
4071         tape->pos[i].action[j] = getFile8Bit(file);
4072     }
4073
4074     tape->pos[i].delay = getFile8Bit(file);
4075
4076     if (tape->file_version == FILE_VERSION_1_0)
4077     {
4078       /* eliminate possible diagonal moves in old tapes */
4079       /* this is only for backward compatibility */
4080
4081       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
4082       byte action = tape->pos[i].action[0];
4083       int k, num_moves = 0;
4084
4085       for (k = 0; k<4; k++)
4086       {
4087         if (action & joy_dir[k])
4088         {
4089           tape->pos[i + num_moves].action[0] = joy_dir[k];
4090           if (num_moves > 0)
4091             tape->pos[i + num_moves].delay = 0;
4092           num_moves++;
4093         }
4094       }
4095
4096       if (num_moves > 1)
4097       {
4098         num_moves--;
4099         i += num_moves;
4100         tape->length += num_moves;
4101       }
4102     }
4103     else if (tape->file_version < FILE_VERSION_2_0)
4104     {
4105       /* convert pre-2.0 tapes to new tape format */
4106
4107       if (tape->pos[i].delay > 1)
4108       {
4109         /* action part */
4110         tape->pos[i + 1] = tape->pos[i];
4111         tape->pos[i + 1].delay = 1;
4112
4113         /* delay part */
4114         for (j = 0; j < MAX_PLAYERS; j++)
4115           tape->pos[i].action[j] = MV_NO_MOVING;
4116         tape->pos[i].delay--;
4117
4118         i++;
4119         tape->length++;
4120       }
4121     }
4122
4123     if (feof(file))
4124       break;
4125   }
4126
4127   if (i != tape->length)
4128     chunk_size = (tape->num_participating_players + 1) * i;
4129
4130   return chunk_size;
4131 }
4132
4133 void LoadTapeFromFilename(char *filename)
4134 {
4135   char cookie[MAX_LINE_LEN];
4136   char chunk_name[CHUNK_ID_LEN + 1];
4137   FILE *file;
4138   int chunk_size;
4139
4140   /* always start with reliable default values */
4141   setTapeInfoToDefaults();
4142
4143   if (!(file = fopen(filename, MODE_READ)))
4144   {
4145     tape.no_valid_file = TRUE;
4146
4147 #if 0
4148     Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
4149 #endif
4150
4151     return;
4152   }
4153
4154   getFileChunkBE(file, chunk_name, NULL);
4155   if (strcmp(chunk_name, "RND1") == 0)
4156   {
4157     getFile32BitBE(file);               /* not used */
4158
4159     getFileChunkBE(file, chunk_name, NULL);
4160     if (strcmp(chunk_name, "TAPE") != 0)
4161     {
4162       tape.no_valid_file = TRUE;
4163
4164       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4165       fclose(file);
4166       return;
4167     }
4168   }
4169   else  /* check for pre-2.0 file format with cookie string */
4170   {
4171     strcpy(cookie, chunk_name);
4172     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
4173     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4174       cookie[strlen(cookie) - 1] = '\0';
4175
4176     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
4177     {
4178       tape.no_valid_file = TRUE;
4179
4180       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
4181       fclose(file);
4182       return;
4183     }
4184
4185     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
4186     {
4187       tape.no_valid_file = TRUE;
4188
4189       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
4190       fclose(file);
4191       return;
4192     }
4193
4194     /* pre-2.0 tape files have no game version, so use file version here */
4195     tape.game_version = tape.file_version;
4196   }
4197
4198   if (tape.file_version < FILE_VERSION_1_2)
4199   {
4200     /* tape files from versions before 1.2.0 without chunk structure */
4201     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
4202     LoadTape_BODY(file, 2 * tape.length,  &tape);
4203   }
4204   else
4205   {
4206     static struct
4207     {
4208       char *name;
4209       int size;
4210       int (*loader)(FILE *, int, struct TapeInfo *);
4211     }
4212     chunk_info[] =
4213     {
4214       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
4215       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
4216       { "INFO", -1,                     LoadTape_INFO },
4217       { "BODY", -1,                     LoadTape_BODY },
4218       {  NULL,  0,                      NULL }
4219     };
4220
4221     while (getFileChunkBE(file, chunk_name, &chunk_size))
4222     {
4223       int i = 0;
4224
4225       while (chunk_info[i].name != NULL &&
4226              strcmp(chunk_name, chunk_info[i].name) != 0)
4227         i++;
4228
4229       if (chunk_info[i].name == NULL)
4230       {
4231         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
4232               chunk_name, filename);
4233         ReadUnusedBytesFromFile(file, chunk_size);
4234       }
4235       else if (chunk_info[i].size != -1 &&
4236                chunk_info[i].size != chunk_size)
4237       {
4238         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4239               chunk_size, chunk_name, filename);
4240         ReadUnusedBytesFromFile(file, chunk_size);
4241       }
4242       else
4243       {
4244         /* call function to load this tape chunk */
4245         int chunk_size_expected =
4246           (chunk_info[i].loader)(file, chunk_size, &tape);
4247
4248         /* the size of some chunks cannot be checked before reading other
4249            chunks first (like "HEAD" and "BODY") that contain some header
4250            information, so check them here */
4251         if (chunk_size_expected != chunk_size)
4252         {
4253           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
4254                 chunk_size, chunk_name, filename);
4255         }
4256       }
4257     }
4258   }
4259
4260   fclose(file);
4261
4262   tape.length_seconds = GetTapeLength();
4263
4264 #if 0
4265   printf("::: tape game version: %d\n", tape.game_version);
4266   printf("::: tape engine version: %d\n", tape.engine_version);
4267 #endif
4268 }
4269
4270 void LoadTape(int nr)
4271 {
4272   char *filename = getTapeFilename(nr);
4273
4274   LoadTapeFromFilename(filename);
4275 }
4276
4277 void LoadSolutionTape(int nr)
4278 {
4279   char *filename = getSolutionTapeFilename(nr);
4280
4281   LoadTapeFromFilename(filename);
4282 }
4283
4284 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
4285 {
4286   putFileVersion(file, tape->file_version);
4287   putFileVersion(file, tape->game_version);
4288 }
4289
4290 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
4291 {
4292   int i;
4293   byte store_participating_players = 0;
4294
4295   /* set bits for participating players for compact storage */
4296   for (i = 0; i < MAX_PLAYERS; i++)
4297     if (tape->player_participates[i])
4298       store_participating_players |= (1 << i);
4299
4300   putFile32BitBE(file, tape->random_seed);
4301   putFile32BitBE(file, tape->date);
4302   putFile32BitBE(file, tape->length);
4303
4304   putFile8Bit(file, store_participating_players);
4305
4306   /* unused bytes not at the end here for 4-byte alignment of engine_version */
4307   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
4308
4309   putFileVersion(file, tape->engine_version);
4310 }
4311
4312 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
4313 {
4314   int level_identifier_size = strlen(tape->level_identifier) + 1;
4315   int i;
4316
4317   putFile16BitBE(file, level_identifier_size);
4318
4319   for (i = 0; i < level_identifier_size; i++)
4320     putFile8Bit(file, tape->level_identifier[i]);
4321
4322   putFile16BitBE(file, tape->level_nr);
4323 }
4324
4325 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
4326 {
4327   int i, j;
4328
4329   for (i = 0; i < tape->length; i++)
4330   {
4331     for (j = 0; j < MAX_PLAYERS; j++)
4332       if (tape->player_participates[j])
4333         putFile8Bit(file, tape->pos[i].action[j]);
4334
4335     putFile8Bit(file, tape->pos[i].delay);
4336   }
4337 }
4338
4339 void SaveTape(int nr)
4340 {
4341   char *filename = getTapeFilename(nr);
4342   FILE *file;
4343   boolean new_tape = TRUE;
4344   int num_participating_players = 0;
4345   int info_chunk_size;
4346   int body_chunk_size;
4347   int i;
4348
4349   InitTapeDirectory(leveldir_current->subdir);
4350
4351   /* if a tape still exists, ask to overwrite it */
4352   if (access(filename, F_OK) == 0)
4353   {
4354     new_tape = FALSE;
4355     if (!Request("Replace old tape ?", REQ_ASK))
4356       return;
4357   }
4358
4359   if (!(file = fopen(filename, MODE_WRITE)))
4360   {
4361     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4362     return;
4363   }
4364
4365   tape.file_version = FILE_VERSION_ACTUAL;
4366   tape.game_version = GAME_VERSION_ACTUAL;
4367
4368   /* count number of participating players  */
4369   for (i = 0; i < MAX_PLAYERS; i++)
4370     if (tape.player_participates[i])
4371       num_participating_players++;
4372
4373   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4374   body_chunk_size = (num_participating_players + 1) * tape.length;
4375
4376   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4377   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4378
4379   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4380   SaveTape_VERS(file, &tape);
4381
4382   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4383   SaveTape_HEAD(file, &tape);
4384
4385   putFileChunkBE(file, "INFO", info_chunk_size);
4386   SaveTape_INFO(file, &tape);
4387
4388   putFileChunkBE(file, "BODY", body_chunk_size);
4389   SaveTape_BODY(file, &tape);
4390
4391   fclose(file);
4392
4393   SetFilePermissions(filename, PERMS_PRIVATE);
4394
4395   tape.changed = FALSE;
4396
4397   if (new_tape)
4398     Request("tape saved !", REQ_CONFIRM);
4399 }
4400
4401 void DumpTape(struct TapeInfo *tape)
4402 {
4403   int i, j;
4404
4405 #if 1
4406   if (tape->no_valid_file)
4407   {
4408     Error(ERR_WARN, "cannot dump -- no valid tape file found");
4409
4410     return;
4411   }
4412 #else
4413   if (TAPE_IS_EMPTY(*tape))
4414   {
4415     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
4416
4417     return;
4418   }
4419 #endif
4420
4421   printf_line("-", 79);
4422   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4423          tape->level_nr, tape->file_version, tape->game_version);
4424   printf("                  (effective engine version %08d)\n",
4425          tape->engine_version);
4426   printf("Level series identifier: '%s'\n", tape->level_identifier);
4427   printf_line("-", 79);
4428
4429   for (i = 0; i < tape->length; i++)
4430   {
4431     if (i >= MAX_TAPELEN)
4432       break;
4433
4434     printf("%03d: ", i);
4435
4436     for (j = 0; j < MAX_PLAYERS; j++)
4437     {
4438       if (tape->player_participates[j])
4439       {
4440         int action = tape->pos[i].action[j];
4441
4442         printf("%d:%02x ", j, action);
4443         printf("[%c%c%c%c|%c%c] - ",
4444                (action & JOY_LEFT ? '<' : ' '),
4445                (action & JOY_RIGHT ? '>' : ' '),
4446                (action & JOY_UP ? '^' : ' '),
4447                (action & JOY_DOWN ? 'v' : ' '),
4448                (action & JOY_BUTTON_1 ? '1' : ' '),
4449                (action & JOY_BUTTON_2 ? '2' : ' '));
4450       }
4451     }
4452
4453     printf("(%03d)\n", tape->pos[i].delay);
4454   }
4455
4456   printf_line("-", 79);
4457 }
4458
4459
4460 /* ========================================================================= */
4461 /* score file functions                                                      */
4462 /* ========================================================================= */
4463
4464 void LoadScore(int nr)
4465 {
4466   int i;
4467   char *filename = getScoreFilename(nr);
4468   char cookie[MAX_LINE_LEN];
4469   char line[MAX_LINE_LEN];
4470   char *line_ptr;
4471   FILE *file;
4472
4473   /* always start with reliable default values */
4474   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4475   {
4476     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4477     highscore[i].Score = 0;
4478   }
4479
4480   if (!(file = fopen(filename, MODE_READ)))
4481     return;
4482
4483   /* check file identifier */
4484   fgets(cookie, MAX_LINE_LEN, file);
4485   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4486     cookie[strlen(cookie) - 1] = '\0';
4487
4488   if (!checkCookieString(cookie, SCORE_COOKIE))
4489   {
4490     Error(ERR_WARN, "unknown format of score file '%s'", filename);
4491     fclose(file);
4492     return;
4493   }
4494
4495   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4496   {
4497     fscanf(file, "%d", &highscore[i].Score);
4498     fgets(line, MAX_LINE_LEN, file);
4499
4500     if (line[strlen(line) - 1] == '\n')
4501       line[strlen(line) - 1] = '\0';
4502
4503     for (line_ptr = line; *line_ptr; line_ptr++)
4504     {
4505       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4506       {
4507         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4508         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4509         break;
4510       }
4511     }
4512   }
4513
4514   fclose(file);
4515 }
4516
4517 void SaveScore(int nr)
4518 {
4519   int i;
4520   char *filename = getScoreFilename(nr);
4521   FILE *file;
4522
4523   InitScoreDirectory(leveldir_current->subdir);
4524
4525   if (!(file = fopen(filename, MODE_WRITE)))
4526   {
4527     Error(ERR_WARN, "cannot save score for level %d", nr);
4528     return;
4529   }
4530
4531   fprintf(file, "%s\n\n", SCORE_COOKIE);
4532
4533   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4534     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4535
4536   fclose(file);
4537
4538   SetFilePermissions(filename, PERMS_PUBLIC);
4539 }
4540
4541
4542 /* ========================================================================= */
4543 /* setup file functions                                                      */
4544 /* ========================================================================= */
4545
4546 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
4547
4548 /* global setup */
4549 #define SETUP_TOKEN_PLAYER_NAME                 0
4550 #define SETUP_TOKEN_SOUND                       1
4551 #define SETUP_TOKEN_SOUND_LOOPS                 2
4552 #define SETUP_TOKEN_SOUND_MUSIC                 3
4553 #define SETUP_TOKEN_SOUND_SIMPLE                4
4554 #define SETUP_TOKEN_TOONS                       5
4555 #define SETUP_TOKEN_SCROLL_DELAY                6
4556 #define SETUP_TOKEN_SOFT_SCROLLING              7
4557 #define SETUP_TOKEN_FADING                      8
4558 #define SETUP_TOKEN_AUTORECORD                  9
4559 #define SETUP_TOKEN_QUICK_DOORS                 10
4560 #define SETUP_TOKEN_TEAM_MODE                   11
4561 #define SETUP_TOKEN_HANDICAP                    12
4562 #define SETUP_TOKEN_TIME_LIMIT                  13
4563 #define SETUP_TOKEN_FULLSCREEN                  14
4564 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
4565 #define SETUP_TOKEN_GRAPHICS_SET                16
4566 #define SETUP_TOKEN_SOUNDS_SET                  17
4567 #define SETUP_TOKEN_MUSIC_SET                   18
4568 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
4569 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
4570 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
4571
4572 #define NUM_GLOBAL_SETUP_TOKENS                 22
4573
4574 /* editor setup */
4575 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
4576 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
4577 #define SETUP_TOKEN_EDITOR_EL_MORE              2
4578 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
4579 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
4580 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
4581 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
4582 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
4583 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
4584 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
4585 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
4586 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
4587
4588 #define NUM_EDITOR_SETUP_TOKENS                 12
4589
4590 /* shortcut setup */
4591 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
4592 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
4593 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
4594
4595 #define NUM_SHORTCUT_SETUP_TOKENS               3
4596
4597 /* player setup */
4598 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
4599 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
4600 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
4601 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
4602 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
4603 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
4604 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
4605 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
4606 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
4607 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
4608 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
4609 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
4610 #define SETUP_TOKEN_PLAYER_KEY_UP               12
4611 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
4612 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
4613 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
4614
4615 #define NUM_PLAYER_SETUP_TOKENS                 16
4616
4617 /* system setup */
4618 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
4619 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
4620
4621 #define NUM_SYSTEM_SETUP_TOKENS                 2
4622
4623 /* options setup */
4624 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
4625
4626 #define NUM_OPTIONS_SETUP_TOKENS                1
4627
4628
4629 static struct SetupInfo si;
4630 static struct SetupEditorInfo sei;
4631 static struct SetupShortcutInfo ssi;
4632 static struct SetupInputInfo sii;
4633 static struct SetupSystemInfo syi;
4634 static struct OptionInfo soi;
4635
4636 static struct TokenInfo global_setup_tokens[] =
4637 {
4638   { TYPE_STRING, &si.player_name,       "player_name"                   },
4639   { TYPE_SWITCH, &si.sound,             "sound"                         },
4640   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
4641   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
4642   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
4643   { TYPE_SWITCH, &si.toons,             "toons"                         },
4644   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
4645   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
4646   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
4647   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
4648   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
4649   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
4650   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
4651   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
4652   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
4653   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
4654   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
4655   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
4656   { TYPE_STRING, &si.music_set,         "music_set"                     },
4657   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4658   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
4659   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
4660 };
4661
4662 static struct TokenInfo editor_setup_tokens[] =
4663 {
4664   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
4665   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
4666   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
4667   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
4668   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
4669   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
4670   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
4671   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
4672   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
4673   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
4674   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
4675   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
4676 };
4677
4678 static struct TokenInfo shortcut_setup_tokens[] =
4679 {
4680   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
4681   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
4682   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
4683 };
4684
4685 static struct TokenInfo player_setup_tokens[] =
4686 {
4687   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
4688   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
4689   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
4690   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
4691   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
4692   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
4693   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
4694   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
4695   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
4696   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
4697   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
4698   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
4699   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
4700   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
4701   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
4702   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
4703 };
4704
4705 static struct TokenInfo system_setup_tokens[] =
4706 {
4707   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
4708   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4709 };
4710
4711 static struct TokenInfo options_setup_tokens[] =
4712 {
4713   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
4714 };
4715
4716 static char *get_corrected_login_name(char *login_name)
4717 {
4718   /* needed because player name must be a fixed length string */
4719   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4720
4721   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4722   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4723
4724   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
4725     if (strchr(login_name_new, ' '))
4726       *strchr(login_name_new, ' ') = '\0';
4727
4728   return login_name_new;
4729 }
4730
4731 static void setSetupInfoToDefaults(struct SetupInfo *si)
4732 {
4733   int i;
4734
4735   si->player_name = get_corrected_login_name(getLoginName());
4736
4737   si->sound = TRUE;
4738   si->sound_loops = TRUE;
4739   si->sound_music = TRUE;
4740   si->sound_simple = TRUE;
4741   si->toons = TRUE;
4742   si->double_buffering = TRUE;
4743   si->direct_draw = !si->double_buffering;
4744   si->scroll_delay = TRUE;
4745   si->soft_scrolling = TRUE;
4746   si->fading = FALSE;
4747   si->autorecord = TRUE;
4748   si->quick_doors = FALSE;
4749   si->team_mode = FALSE;
4750   si->handicap = TRUE;
4751   si->time_limit = TRUE;
4752   si->fullscreen = FALSE;
4753   si->ask_on_escape = TRUE;
4754
4755   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4756   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4757   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4758   si->override_level_graphics = FALSE;
4759   si->override_level_sounds = FALSE;
4760   si->override_level_music = FALSE;
4761
4762   si->editor.el_boulderdash = TRUE;
4763   si->editor.el_emerald_mine = TRUE;
4764   si->editor.el_more = TRUE;
4765   si->editor.el_sokoban = TRUE;
4766   si->editor.el_supaplex = TRUE;
4767   si->editor.el_diamond_caves = TRUE;
4768   si->editor.el_dx_boulderdash = TRUE;
4769   si->editor.el_chars = TRUE;
4770   si->editor.el_custom = TRUE;
4771   si->editor.el_custom_more = FALSE;
4772
4773   si->editor.el_headlines = TRUE;
4774   si->editor.el_user_defined = FALSE;
4775
4776   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4777   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4778   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4779
4780   for (i = 0; i < MAX_PLAYERS; i++)
4781   {
4782     si->input[i].use_joystick = FALSE;
4783     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4784     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
4785     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4786     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
4787     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
4788     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4789     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
4790     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
4791     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
4792     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
4793     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4794     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
4795     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
4796     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
4797     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
4798   }
4799
4800   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4801   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4802
4803   si->options.verbose = FALSE;
4804 }
4805
4806 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4807 {
4808   int i, pnr;
4809
4810   if (!setup_file_hash)
4811     return;
4812
4813   /* global setup */
4814   si = setup;
4815   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4816     setSetupInfo(global_setup_tokens, i,
4817                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4818   setup = si;
4819
4820   /* editor setup */
4821   sei = setup.editor;
4822   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4823     setSetupInfo(editor_setup_tokens, i,
4824                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4825   setup.editor = sei;
4826
4827   /* shortcut setup */
4828   ssi = setup.shortcut;
4829   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4830     setSetupInfo(shortcut_setup_tokens, i,
4831                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4832   setup.shortcut = ssi;
4833
4834   /* player setup */
4835   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4836   {
4837     char prefix[30];
4838
4839     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4840
4841     sii = setup.input[pnr];
4842     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4843     {
4844       char full_token[100];
4845
4846       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4847       setSetupInfo(player_setup_tokens, i,
4848                    getHashEntry(setup_file_hash, full_token));
4849     }
4850     setup.input[pnr] = sii;
4851   }
4852
4853   /* system setup */
4854   syi = setup.system;
4855   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4856     setSetupInfo(system_setup_tokens, i,
4857                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4858   setup.system = syi;
4859
4860   /* options setup */
4861   soi = setup.options;
4862   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4863     setSetupInfo(options_setup_tokens, i,
4864                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4865   setup.options = soi;
4866 }
4867
4868 void LoadSetup()
4869 {
4870   char *filename = getSetupFilename();
4871   SetupFileHash *setup_file_hash = NULL;
4872
4873   /* always start with reliable default values */
4874   setSetupInfoToDefaults(&setup);
4875
4876   setup_file_hash = loadSetupFileHash(filename);
4877
4878   if (setup_file_hash)
4879   {
4880     char *player_name_new;
4881
4882     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4883     decodeSetupFileHash(setup_file_hash);
4884
4885     setup.direct_draw = !setup.double_buffering;
4886
4887     freeSetupFileHash(setup_file_hash);
4888
4889     /* needed to work around problems with fixed length strings */
4890     player_name_new = get_corrected_login_name(setup.player_name);
4891     free(setup.player_name);
4892     setup.player_name = player_name_new;
4893   }
4894   else
4895     Error(ERR_WARN, "using default setup values");
4896 }
4897
4898 void SaveSetup()
4899 {
4900   char *filename = getSetupFilename();
4901   FILE *file;
4902   int i, pnr;
4903
4904   InitUserDataDirectory();
4905
4906   if (!(file = fopen(filename, MODE_WRITE)))
4907   {
4908     Error(ERR_WARN, "cannot write setup file '%s'", filename);
4909     return;
4910   }
4911
4912   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4913                                                getCookie("SETUP")));
4914   fprintf(file, "\n");
4915
4916   /* global setup */
4917   si = setup;
4918   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4919   {
4920     /* just to make things nicer :) */
4921     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4922         i == SETUP_TOKEN_GRAPHICS_SET)
4923       fprintf(file, "\n");
4924
4925     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4926   }
4927
4928   /* editor setup */
4929   sei = setup.editor;
4930   fprintf(file, "\n");
4931   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4932     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4933
4934   /* shortcut setup */
4935   ssi = setup.shortcut;
4936   fprintf(file, "\n");
4937   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4938     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4939
4940   /* player setup */
4941   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4942   {
4943     char prefix[30];
4944
4945     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4946     fprintf(file, "\n");
4947
4948     sii = setup.input[pnr];
4949     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4950       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4951   }
4952
4953   /* system setup */
4954   syi = setup.system;
4955   fprintf(file, "\n");
4956   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4957     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4958
4959   /* options setup */
4960   soi = setup.options;
4961   fprintf(file, "\n");
4962   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4963     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4964
4965   fclose(file);
4966
4967   SetFilePermissions(filename, PERMS_PRIVATE);
4968 }
4969
4970 void LoadCustomElementDescriptions()
4971 {
4972   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4973   SetupFileHash *setup_file_hash;
4974   int i;
4975
4976   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4977   {
4978     if (element_info[i].custom_description != NULL)
4979     {
4980       free(element_info[i].custom_description);
4981       element_info[i].custom_description = NULL;
4982     }
4983   }
4984
4985   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4986     return;
4987
4988   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4989   {
4990     char *token = getStringCat2(element_info[i].token_name, ".name");
4991     char *value = getHashEntry(setup_file_hash, token);
4992
4993     if (value != NULL)
4994       element_info[i].custom_description = getStringCopy(value);
4995
4996     free(token);
4997   }
4998
4999   freeSetupFileHash(setup_file_hash);
5000 }
5001
5002 void LoadSpecialMenuDesignSettings()
5003 {
5004   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
5005   SetupFileHash *setup_file_hash;
5006   int i, j;
5007
5008   /* always start with reliable default values from default config */
5009   for (i = 0; image_config_vars[i].token != NULL; i++)
5010     for (j = 0; image_config[j].token != NULL; j++)
5011       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
5012         *image_config_vars[i].value =
5013           get_auto_parameter_value(image_config_vars[i].token,
5014                                    image_config[j].value);
5015
5016   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
5017     return;
5018
5019   /* special case: initialize with default values that may be overwritten */
5020   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5021   {
5022     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
5023     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
5024     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
5025
5026     if (value_x != NULL)
5027       menu.draw_xoffset[i] = get_integer_from_string(value_x);
5028     if (value_y != NULL)
5029       menu.draw_yoffset[i] = get_integer_from_string(value_y);
5030     if (list_size != NULL)
5031       menu.list_size[i] = get_integer_from_string(list_size);
5032   }
5033
5034   /* read (and overwrite with) values that may be specified in config file */
5035   for (i = 0; image_config_vars[i].token != NULL; i++)
5036   {
5037     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
5038
5039     if (value != NULL)
5040       *image_config_vars[i].value =
5041         get_auto_parameter_value(image_config_vars[i].token, value);
5042   }
5043
5044   freeSetupFileHash(setup_file_hash);
5045 }
5046
5047 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
5048 {
5049   char *filename = getEditorSetupFilename();
5050   SetupFileList *setup_file_list, *list;
5051   SetupFileHash *element_hash;
5052   int num_unknown_tokens = 0;
5053   int i;
5054
5055   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
5056     return;
5057
5058   element_hash = newSetupFileHash();
5059
5060   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5061     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5062
5063   /* determined size may be larger than needed (due to unknown elements) */
5064   *num_elements = 0;
5065   for (list = setup_file_list; list != NULL; list = list->next)
5066     (*num_elements)++;
5067
5068   /* add space for up to 3 more elements for padding that may be needed */
5069   *num_elements += 3;
5070
5071   *elements = checked_malloc(*num_elements * sizeof(int));
5072
5073   *num_elements = 0;
5074   for (list = setup_file_list; list != NULL; list = list->next)
5075   {
5076     char *value = getHashEntry(element_hash, list->token);
5077
5078     if (value)
5079     {
5080       (*elements)[(*num_elements)++] = atoi(value);
5081     }
5082     else
5083     {
5084       if (num_unknown_tokens == 0)
5085       {
5086         Error(ERR_RETURN_LINE, "-");
5087         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5088         Error(ERR_RETURN, "- config file: '%s'", filename);
5089
5090         num_unknown_tokens++;
5091       }
5092
5093       Error(ERR_RETURN, "- token: '%s'", list->token);
5094     }
5095   }
5096
5097   if (num_unknown_tokens > 0)
5098     Error(ERR_RETURN_LINE, "-");
5099
5100   while (*num_elements % 4)     /* pad with empty elements, if needed */
5101     (*elements)[(*num_elements)++] = EL_EMPTY;
5102
5103   freeSetupFileList(setup_file_list);
5104   freeSetupFileHash(element_hash);
5105
5106 #if 0
5107   /* TEST-ONLY */
5108   for (i = 0; i < *num_elements; i++)
5109     printf("editor: element '%s' [%d]\n",
5110            element_info[(*elements)[i]].token_name, (*elements)[i]);
5111 #endif
5112 }
5113
5114 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
5115                                                      boolean is_sound)
5116 {
5117   SetupFileHash *setup_file_hash = NULL;
5118   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
5119   char *filename_music, *filename_prefix, *filename_info;
5120   struct
5121   {
5122     char *token;
5123     char **value_ptr;
5124   }
5125   token_to_value_ptr[] =
5126   {
5127     { "title_header",   &tmp_music_file_info.title_header       },
5128     { "artist_header",  &tmp_music_file_info.artist_header      },
5129     { "album_header",   &tmp_music_file_info.album_header       },
5130     { "year_header",    &tmp_music_file_info.year_header        },
5131
5132     { "title",          &tmp_music_file_info.title              },
5133     { "artist",         &tmp_music_file_info.artist             },
5134     { "album",          &tmp_music_file_info.album              },
5135     { "year",           &tmp_music_file_info.year               },
5136
5137     { NULL,             NULL                                    },
5138   };
5139   int i;
5140
5141   filename_music = (is_sound ? getCustomSoundFilename(basename) :
5142                     getCustomMusicFilename(basename));
5143
5144   if (filename_music == NULL)
5145     return NULL;
5146
5147   /* ---------- try to replace file extension ---------- */
5148
5149   filename_prefix = getStringCopy(filename_music);
5150   if (strrchr(filename_prefix, '.') != NULL)
5151     *strrchr(filename_prefix, '.') = '\0';
5152   filename_info = getStringCat2(filename_prefix, ".txt");
5153
5154 #if 0
5155   printf("trying to load file '%s'...\n", filename_info);
5156 #endif
5157
5158   if (fileExists(filename_info))
5159     setup_file_hash = loadSetupFileHash(filename_info);
5160
5161   free(filename_prefix);
5162   free(filename_info);
5163
5164   if (setup_file_hash == NULL)
5165   {
5166     /* ---------- try to add file extension ---------- */
5167
5168     filename_prefix = getStringCopy(filename_music);
5169     filename_info = getStringCat2(filename_prefix, ".txt");
5170
5171 #if 0
5172     printf("trying to load file '%s'...\n", filename_info);
5173 #endif
5174
5175     if (fileExists(filename_info))
5176       setup_file_hash = loadSetupFileHash(filename_info);
5177
5178     free(filename_prefix);
5179     free(filename_info);
5180   }
5181
5182   if (setup_file_hash == NULL)
5183     return NULL;
5184
5185   /* ---------- music file info found ---------- */
5186
5187   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
5188
5189   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
5190   {
5191     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
5192
5193     *token_to_value_ptr[i].value_ptr =
5194       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
5195   }
5196
5197   tmp_music_file_info.basename = getStringCopy(basename);
5198   tmp_music_file_info.music = music;
5199   tmp_music_file_info.is_sound = is_sound;
5200
5201   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
5202   *new_music_file_info = tmp_music_file_info;
5203
5204   return new_music_file_info;
5205 }
5206
5207 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
5208 {
5209   return get_music_file_info_ext(basename, music, FALSE);
5210 }
5211
5212 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
5213 {
5214   return get_music_file_info_ext(basename, sound, TRUE);
5215 }
5216
5217 static boolean music_info_listed_ext(struct MusicFileInfo *list,
5218                                      char *basename, boolean is_sound)
5219 {
5220   for (; list != NULL; list = list->next)
5221     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
5222       return TRUE;
5223
5224   return FALSE;
5225 }
5226
5227 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
5228 {
5229   return music_info_listed_ext(list, basename, FALSE);
5230 }
5231
5232 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
5233 {
5234   return music_info_listed_ext(list, basename, TRUE);
5235 }
5236
5237 void LoadMusicInfo()
5238 {
5239   char *music_directory = getCustomMusicDirectory();
5240   int num_music = getMusicListSize();
5241   int num_music_noconf = 0;
5242   int num_sounds = getSoundListSize();
5243   DIR *dir;
5244   struct dirent *dir_entry;
5245   struct FileInfo *music, *sound;
5246   struct MusicFileInfo *next, **new;
5247   int i;
5248
5249   while (music_file_info != NULL)
5250   {
5251     next = music_file_info->next;
5252
5253     checked_free(music_file_info->basename);
5254
5255     checked_free(music_file_info->title_header);
5256     checked_free(music_file_info->artist_header);
5257     checked_free(music_file_info->album_header);
5258     checked_free(music_file_info->year_header);
5259
5260     checked_free(music_file_info->title);
5261     checked_free(music_file_info->artist);
5262     checked_free(music_file_info->album);
5263     checked_free(music_file_info->year);
5264
5265     free(music_file_info);
5266
5267     music_file_info = next;
5268   }
5269
5270   new = &music_file_info;
5271
5272 #if 0
5273   printf("::: num_music == %d\n", num_music);
5274 #endif
5275
5276   for (i = 0; i < num_music; i++)
5277   {
5278     music = getMusicListEntry(i);
5279
5280 #if 0
5281     printf("::: %d [%08x]\n", i, music->filename);
5282 #endif
5283
5284     if (music->filename == NULL)
5285       continue;
5286
5287     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
5288       continue;
5289
5290     /* a configured file may be not recognized as music */
5291     if (!FileIsMusic(music->filename))
5292       continue;
5293
5294 #if 0
5295     printf("::: -> '%s' (configured)\n", music->filename);
5296 #endif
5297
5298     if (!music_info_listed(music_file_info, music->filename))
5299     {
5300       *new = get_music_file_info(music->filename, i);
5301       if (*new != NULL)
5302         new = &(*new)->next;
5303     }
5304   }
5305
5306   if ((dir = opendir(music_directory)) == NULL)
5307   {
5308     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
5309     return;
5310   }
5311
5312   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
5313   {
5314     char *basename = dir_entry->d_name;
5315     boolean music_already_used = FALSE;
5316     int i;
5317
5318     /* skip all music files that are configured in music config file */
5319     for (i = 0; i < num_music; i++)
5320     {
5321       music = getMusicListEntry(i);
5322
5323       if (music->filename == NULL)
5324         continue;
5325
5326       if (strcmp(basename, music->filename) == 0)
5327       {
5328         music_already_used = TRUE;
5329         break;
5330       }
5331     }
5332
5333     if (music_already_used)
5334       continue;
5335
5336     if (!FileIsMusic(basename))
5337       continue;
5338
5339 #if 0
5340     printf("::: -> '%s' (found in directory)\n", basename);
5341 #endif
5342
5343     if (!music_info_listed(music_file_info, basename))
5344     {
5345       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5346       if (*new != NULL)
5347         new = &(*new)->next;
5348     }
5349
5350     num_music_noconf++;
5351   }
5352
5353   closedir(dir);
5354
5355   for (i = 0; i < num_sounds; i++)
5356   {
5357     sound = getSoundListEntry(i);
5358
5359     if (sound->filename == NULL)
5360       continue;
5361
5362     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5363       continue;
5364
5365     /* a configured file may be not recognized as sound */
5366     if (!FileIsSound(sound->filename))
5367       continue;
5368
5369 #if 0
5370     printf("::: -> '%s' (configured)\n", sound->filename);
5371 #endif
5372
5373     if (!sound_info_listed(music_file_info, sound->filename))
5374     {
5375       *new = get_sound_file_info(sound->filename, i);
5376       if (*new != NULL)
5377         new = &(*new)->next;
5378     }
5379   }
5380
5381 #if 0
5382   /* TEST-ONLY */
5383   for (next = music_file_info; next != NULL; next = next->next)
5384     printf("::: title == '%s'\n", next->title);
5385 #endif
5386 }
5387
5388 void add_helpanim_entry(int element, int action, int direction, int delay,
5389                         int *num_list_entries)
5390 {
5391   struct HelpAnimInfo *new_list_entry;
5392   (*num_list_entries)++;
5393
5394   helpanim_info =
5395     checked_realloc(helpanim_info,
5396                     *num_list_entries * sizeof(struct HelpAnimInfo));
5397   new_list_entry = &helpanim_info[*num_list_entries - 1];
5398
5399   new_list_entry->element = element;
5400   new_list_entry->action = action;
5401   new_list_entry->direction = direction;
5402   new_list_entry->delay = delay;
5403 }
5404
5405 void print_unknown_token(char *filename, char *token, int token_nr)
5406 {
5407   if (token_nr == 0)
5408   {
5409     Error(ERR_RETURN_LINE, "-");
5410     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5411     Error(ERR_RETURN, "- config file: '%s'", filename);
5412   }
5413
5414   Error(ERR_RETURN, "- token: '%s'", token);
5415 }
5416
5417 void print_unknown_token_end(int token_nr)
5418 {
5419   if (token_nr > 0)
5420     Error(ERR_RETURN_LINE, "-");
5421 }
5422
5423 void LoadHelpAnimInfo()
5424 {
5425   char *filename = getHelpAnimFilename();
5426   SetupFileList *setup_file_list = NULL, *list;
5427   SetupFileHash *element_hash, *action_hash, *direction_hash;
5428   int num_list_entries = 0;
5429   int num_unknown_tokens = 0;
5430   int i;
5431
5432   if (fileExists(filename))
5433     setup_file_list = loadSetupFileList(filename);
5434
5435   if (setup_file_list == NULL)
5436   {
5437     /* use reliable default values from static configuration */
5438     SetupFileList *insert_ptr;
5439
5440     insert_ptr = setup_file_list =
5441       newSetupFileList(helpanim_config[0].token,
5442                        helpanim_config[0].value);
5443
5444     for (i = 1; helpanim_config[i].token; i++)
5445       insert_ptr = addListEntry(insert_ptr,
5446                                 helpanim_config[i].token,
5447                                 helpanim_config[i].value);
5448   }
5449
5450   element_hash   = newSetupFileHash();
5451   action_hash    = newSetupFileHash();
5452   direction_hash = newSetupFileHash();
5453
5454   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5455     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5456
5457   for (i = 0; i < NUM_ACTIONS; i++)
5458     setHashEntry(action_hash, element_action_info[i].suffix,
5459                  i_to_a(element_action_info[i].value));
5460
5461   /* do not store direction index (bit) here, but direction value! */
5462   for (i = 0; i < NUM_DIRECTIONS; i++)
5463     setHashEntry(direction_hash, element_direction_info[i].suffix,
5464                  i_to_a(1 << element_direction_info[i].value));
5465
5466   for (list = setup_file_list; list != NULL; list = list->next)
5467   {
5468     char *element_token, *action_token, *direction_token;
5469     char *element_value, *action_value, *direction_value;
5470     int delay = atoi(list->value);
5471
5472     if (strcmp(list->token, "end") == 0)
5473     {
5474       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5475
5476       continue;
5477     }
5478
5479     /* first try to break element into element/action/direction parts;
5480        if this does not work, also accept combined "element[.act][.dir]"
5481        elements (like "dynamite.active"), which are unique elements */
5482
5483     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
5484     {
5485       element_value = getHashEntry(element_hash, list->token);
5486       if (element_value != NULL)        /* element found */
5487         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5488                            &num_list_entries);
5489       else
5490       {
5491         /* no further suffixes found -- this is not an element */
5492         print_unknown_token(filename, list->token, num_unknown_tokens++);
5493       }
5494
5495       continue;
5496     }
5497
5498     /* token has format "<prefix>.<something>" */
5499
5500     action_token = strchr(list->token, '.');    /* suffix may be action ... */
5501     direction_token = action_token;             /* ... or direction */
5502
5503     element_token = getStringCopy(list->token);
5504     *strchr(element_token, '.') = '\0';
5505
5506     element_value = getHashEntry(element_hash, element_token);
5507
5508     if (element_value == NULL)          /* this is no element */
5509     {
5510       element_value = getHashEntry(element_hash, list->token);
5511       if (element_value != NULL)        /* combined element found */
5512         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5513                            &num_list_entries);
5514       else
5515         print_unknown_token(filename, list->token, num_unknown_tokens++);
5516
5517       free(element_token);
5518
5519       continue;
5520     }
5521
5522     action_value = getHashEntry(action_hash, action_token);
5523
5524     if (action_value != NULL)           /* action found */
5525     {
5526       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5527                     &num_list_entries);
5528
5529       free(element_token);
5530
5531       continue;
5532     }
5533
5534     direction_value = getHashEntry(direction_hash, direction_token);
5535
5536     if (direction_value != NULL)        /* direction found */
5537     {
5538       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5539                          &num_list_entries);
5540
5541       free(element_token);
5542
5543       continue;
5544     }
5545
5546     if (strchr(action_token + 1, '.') == NULL)
5547     {
5548       /* no further suffixes found -- this is not an action nor direction */
5549
5550       element_value = getHashEntry(element_hash, list->token);
5551       if (element_value != NULL)        /* combined element found */
5552         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5553                            &num_list_entries);
5554       else
5555         print_unknown_token(filename, list->token, num_unknown_tokens++);
5556
5557       free(element_token);
5558
5559       continue;
5560     }
5561
5562     /* token has format "<prefix>.<suffix>.<something>" */
5563
5564     direction_token = strchr(action_token + 1, '.');
5565
5566     action_token = getStringCopy(action_token);
5567     *strchr(action_token + 1, '.') = '\0';
5568
5569     action_value = getHashEntry(action_hash, action_token);
5570
5571     if (action_value == NULL)           /* this is no action */
5572     {
5573       element_value = getHashEntry(element_hash, list->token);
5574       if (element_value != NULL)        /* combined element found */
5575         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5576                            &num_list_entries);
5577       else
5578         print_unknown_token(filename, list->token, num_unknown_tokens++);
5579
5580       free(element_token);
5581       free(action_token);
5582
5583       continue;
5584     }
5585
5586     direction_value = getHashEntry(direction_hash, direction_token);
5587
5588     if (direction_value != NULL)        /* direction found */
5589     {
5590       add_helpanim_entry(atoi(element_value), atoi(action_value),
5591                          atoi(direction_value), delay, &num_list_entries);
5592
5593       free(element_token);
5594       free(action_token);
5595
5596       continue;
5597     }
5598
5599     /* this is no direction */
5600
5601     element_value = getHashEntry(element_hash, list->token);
5602     if (element_value != NULL)          /* combined element found */
5603       add_helpanim_entry(atoi(element_value), -1, -1, delay,
5604                          &num_list_entries);
5605     else
5606       print_unknown_token(filename, list->token, num_unknown_tokens++);
5607
5608     free(element_token);
5609     free(action_token);
5610   }
5611
5612   print_unknown_token_end(num_unknown_tokens);
5613
5614   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5615   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
5616
5617   freeSetupFileList(setup_file_list);
5618   freeSetupFileHash(element_hash);
5619   freeSetupFileHash(action_hash);
5620   freeSetupFileHash(direction_hash);
5621
5622 #if 0
5623   /* TEST ONLY */
5624   for (i = 0; i < num_list_entries; i++)
5625     printf("::: %d, %d, %d => %d\n",
5626            helpanim_info[i].element,
5627            helpanim_info[i].action,
5628            helpanim_info[i].direction,
5629            helpanim_info[i].delay);
5630 #endif
5631 }
5632
5633 void LoadHelpTextInfo()
5634 {
5635   char *filename = getHelpTextFilename();
5636   int i;
5637
5638   if (helptext_info != NULL)
5639   {
5640     freeSetupFileHash(helptext_info);
5641     helptext_info = NULL;
5642   }
5643
5644   if (fileExists(filename))
5645     helptext_info = loadSetupFileHash(filename);
5646
5647   if (helptext_info == NULL)
5648   {
5649     /* use reliable default values from static configuration */
5650     helptext_info = newSetupFileHash();
5651
5652     for (i = 0; helptext_config[i].token; i++)
5653       setHashEntry(helptext_info,
5654                    helptext_config[i].token,
5655                    helptext_config[i].value);
5656   }
5657
5658 #if 0
5659   /* TEST ONLY */
5660   BEGIN_HASH_ITERATION(helptext_info, itr)
5661   {
5662     printf("::: '%s' => '%s'\n",
5663            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5664   }
5665   END_HASH_ITERATION(hash, itr)
5666 #endif
5667 }
5668
5669
5670 /* ------------------------------------------------------------------------- *
5671  * convert levels
5672  * ------------------------------------------------------------------------- */
5673
5674 #define MAX_NUM_CONVERT_LEVELS          1000
5675
5676 void ConvertLevels()
5677 {
5678   static LevelDirTree *convert_leveldir = NULL;
5679   static int convert_level_nr = -1;
5680   static int num_levels_handled = 0;
5681   static int num_levels_converted = 0;
5682   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5683   int i;
5684
5685   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5686                                                global.convert_leveldir);
5687
5688   if (convert_leveldir == NULL)
5689     Error(ERR_EXIT, "no such level identifier: '%s'",
5690           global.convert_leveldir);
5691
5692   leveldir_current = convert_leveldir;
5693
5694   if (global.convert_level_nr != -1)
5695   {
5696     convert_leveldir->first_level = global.convert_level_nr;
5697     convert_leveldir->last_level  = global.convert_level_nr;
5698   }
5699
5700   convert_level_nr = convert_leveldir->first_level;
5701
5702   printf_line("=", 79);
5703   printf("Converting levels\n");
5704   printf_line("-", 79);
5705   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5706   printf("Level series name:       '%s'\n", convert_leveldir->name);
5707   printf("Level series author:     '%s'\n", convert_leveldir->author);
5708   printf("Number of levels:        %d\n",   convert_leveldir->levels);
5709   printf_line("=", 79);
5710   printf("\n");
5711
5712   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5713     levels_failed[i] = FALSE;
5714
5715   while (convert_level_nr <= convert_leveldir->last_level)
5716   {
5717     char *level_filename;
5718     boolean new_level;
5719
5720     level_nr = convert_level_nr++;
5721
5722     printf("Level %03d: ", level_nr);
5723
5724     LoadLevel(level_nr);
5725     if (level.no_valid_file)
5726     {
5727       printf("(no level)\n");
5728       continue;
5729     }
5730
5731     printf("converting level ... ");
5732
5733     level_filename = getDefaultLevelFilename(level_nr);
5734     new_level = !fileExists(level_filename);
5735
5736     if (new_level)
5737     {
5738       SaveLevel(level_nr);
5739
5740       num_levels_converted++;
5741
5742       printf("converted.\n");
5743     }
5744     else
5745     {
5746       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5747         levels_failed[level_nr] = TRUE;
5748
5749       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5750     }
5751
5752     num_levels_handled++;
5753   }
5754
5755   printf("\n");
5756   printf_line("=", 79);
5757   printf("Number of levels handled: %d\n", num_levels_handled);
5758   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5759          (num_levels_handled ?
5760           num_levels_converted * 100 / num_levels_handled : 0));
5761   printf_line("-", 79);
5762   printf("Summary (for automatic parsing by scripts):\n");
5763   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5764          convert_leveldir->identifier, num_levels_converted,
5765          num_levels_handled,
5766          (num_levels_handled ?
5767           num_levels_converted * 100 / num_levels_handled : 0));
5768
5769   if (num_levels_handled != num_levels_converted)
5770   {
5771     printf(", FAILED:");
5772     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5773       if (levels_failed[i])
5774         printf(" %03d", i);
5775   }
5776
5777   printf("\n");
5778   printf_line("=", 79);
5779
5780   CloseAllAndExit(0);
5781 }