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