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