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