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