rnd-20040927-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->ameuba_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 at 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->ameuba_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   level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
1888   level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
1889 }
1890
1891 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1892                                      struct LevelFileInfo *level_file_info)
1893 {
1894   if (!LoadNativeLevel_EM(level_file_info->filename))
1895     level->no_valid_file = TRUE;
1896 }
1897
1898 #endif
1899
1900 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
1901 {
1902   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
1903     CopyNativeLevel_RND_to_EM(level);
1904 }
1905
1906 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
1907 {
1908   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
1909     CopyNativeLevel_EM_to_RND(level);
1910 }
1911
1912
1913 /* ------------------------------------------------------------------------- */
1914 /* functions for loading SP level                                            */
1915 /* ------------------------------------------------------------------------- */
1916
1917 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1918 #define SP_LEVEL_SIZE                   1536
1919 #define SP_LEVEL_XSIZE                  60
1920 #define SP_LEVEL_YSIZE                  24
1921 #define SP_LEVEL_NAME_LEN               23
1922
1923 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1924                                        int nr)
1925 {
1926   int num_special_ports;
1927   int i, x, y;
1928
1929   /* for details of the Supaplex level format, see Herman Perk's Supaplex
1930      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
1931
1932   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1933   for (y = 0; y < SP_LEVEL_YSIZE; y++)
1934   {
1935     for (x = 0; x < SP_LEVEL_XSIZE; x++)
1936     {
1937       int element_old = fgetc(file);
1938       int element_new;
1939
1940       if (element_old <= 0x27)
1941         element_new = getMappedElement(EL_SP_START + element_old);
1942       else if (element_old == 0x28)
1943         element_new = EL_INVISIBLE_WALL;
1944       else
1945       {
1946         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1947         Error(ERR_WARN, "invalid level element %d", element_old);
1948
1949         element_new = EL_UNKNOWN;
1950       }
1951
1952       level->field[x][y] = element_new;
1953     }
1954   }
1955
1956   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
1957
1958   /* initial gravity: 1 == "on", anything else (0) == "off" */
1959   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1960
1961   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
1962
1963   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1964   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1965     level->name[i] = fgetc(file);
1966   level->name[SP_LEVEL_NAME_LEN] = '\0';
1967
1968   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
1969   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
1970
1971   /* number of infotrons needed; 0 means that Supaplex will count the total
1972      amount of infotrons in the level and use the low byte of that number
1973      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1974   level->gems_needed = fgetc(file);
1975
1976   /* number of special ("gravity") port entries below (maximum 10 allowed) */
1977   num_special_ports = fgetc(file);
1978
1979   /* database of properties of up to 10 special ports (6 bytes per port) */
1980   for (i = 0; i < 10; i++)
1981   {
1982     int port_location, port_x, port_y, port_element;
1983     int gravity;
1984
1985     /* high and low byte of the location of a special port; if (x, y) are the
1986        coordinates of a port in the field and (0, 0) is the top-left corner,
1987        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
1988        of what may be expected: Supaplex works with a game field in memory
1989        which is 2 bytes per tile) */
1990     port_location = getFile16BitBE(file);
1991
1992     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
1993     gravity = fgetc(file);
1994
1995     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
1996     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
1997
1998     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
1999     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
2000
2001     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
2002
2003     if (i >= num_special_ports)
2004       continue;
2005
2006     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
2007     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
2008
2009     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
2010         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
2011     {
2012       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
2013             port_x, port_y);
2014
2015       continue;
2016     }
2017
2018     port_element = level->field[port_x][port_y];
2019
2020     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
2021         port_element > EL_SP_GRAVITY_PORT_UP)
2022     {
2023       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
2024
2025       continue;
2026     }
2027
2028     /* change previous (wrong) gravity inverting special port to either
2029        gravity enabling special port or gravity disabling special port */
2030     level->field[port_x][port_y] +=
2031       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
2032        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
2033   }
2034
2035   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
2036
2037   /* change special gravity ports without database entries to normal ports */
2038   for (y = 0; y < SP_LEVEL_YSIZE; y++)
2039     for (x = 0; x < SP_LEVEL_XSIZE; x++)
2040       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
2041           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
2042         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
2043
2044   /* auto-determine number of infotrons if it was stored as "0" -- see above */
2045   if (level->gems_needed == 0)
2046   {
2047     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2048       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2049         if (level->field[x][y] == EL_SP_INFOTRON)
2050           level->gems_needed++;
2051
2052     level->gems_needed &= 0xff;         /* only use low byte -- see above */
2053   }
2054
2055   level->fieldx = SP_LEVEL_XSIZE;
2056   level->fieldy = SP_LEVEL_YSIZE;
2057
2058   level->time = 0;                      /* no time limit */
2059   level->amoeba_speed = 0;
2060   level->time_magic_wall = 0;
2061   level->time_wheel = 0;
2062   level->amoeba_content = EL_EMPTY;
2063
2064   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2065     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
2066
2067   /* there are no yamyams in supaplex levels */
2068   for (i = 0; i < level->num_yamyam_contents; i++)
2069     for (y = 0; y < 3; y++)
2070       for (x = 0; x < 3; x++)
2071         level->yamyam_content[i][x][y] = EL_EMPTY;
2072 }
2073
2074 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
2075                                      struct LevelFileInfo *level_file_info)
2076 {
2077   char *filename = level_file_info->filename;
2078   FILE *file;
2079   int nr = level_file_info->nr - leveldir_current->first_level;
2080   int i, l, x, y;
2081   char name_first, name_last;
2082   struct LevelInfo multipart_level;
2083   int multipart_xpos, multipart_ypos;
2084   boolean is_multipart_level;
2085   boolean is_first_part;
2086   boolean reading_multipart_level = FALSE;
2087   boolean use_empty_level = FALSE;
2088
2089   if (!(file = fopen(filename, MODE_READ)))
2090   {
2091     level->no_valid_file = TRUE;
2092
2093     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2094
2095     return;
2096   }
2097
2098   /* position file stream to the requested level inside the level package */
2099   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
2100   {
2101     level->no_valid_file = TRUE;
2102
2103     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
2104
2105     return;
2106   }
2107
2108   /* there exist Supaplex level package files with multi-part levels which
2109      can be detected as follows: instead of leading and trailing dashes ('-')
2110      to pad the level name, they have leading and trailing numbers which are
2111      the x and y coordinations of the current part of the multi-part level;
2112      if there are '?' characters instead of numbers on the left or right side
2113      of the level name, the multi-part level consists of only horizontal or
2114      vertical parts */
2115
2116   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
2117   {
2118     LoadLevelFromFileStream_SP(file, level, l);
2119
2120     /* check if this level is a part of a bigger multi-part level */
2121
2122     name_first = level->name[0];
2123     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
2124
2125     is_multipart_level =
2126       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
2127        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
2128
2129     is_first_part =
2130       ((name_first == '?' || name_first == '1') &&
2131        (name_last  == '?' || name_last  == '1'));
2132
2133     /* correct leading multipart level meta information in level name */
2134     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
2135       level->name[i] = '-';
2136
2137     /* correct trailing multipart level meta information in level name */
2138     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
2139       level->name[i] = '-';
2140
2141     /* ---------- check for normal single level ---------- */
2142
2143     if (!reading_multipart_level && !is_multipart_level)
2144     {
2145       /* the current level is simply a normal single-part level, and we are
2146          not reading a multi-part level yet, so return the level as it is */
2147
2148       break;
2149     }
2150
2151     /* ---------- check for empty level (unused multi-part) ---------- */
2152
2153     if (!reading_multipart_level && is_multipart_level && !is_first_part)
2154     {
2155       /* this is a part of a multi-part level, but not the first part
2156          (and we are not already reading parts of a multi-part level);
2157          in this case, use an empty level instead of the single part */
2158
2159       use_empty_level = TRUE;
2160
2161       break;
2162     }
2163
2164     /* ---------- check for finished multi-part level ---------- */
2165
2166     if (reading_multipart_level &&
2167         (!is_multipart_level ||
2168          strcmp(level->name, multipart_level.name) != 0))
2169     {
2170       /* we are already reading parts of a multi-part level, but this level is
2171          either not a multi-part level, or a part of a different multi-part
2172          level; in both cases, the multi-part level seems to be complete */
2173
2174       break;
2175     }
2176
2177     /* ---------- here we have one part of a multi-part level ---------- */
2178
2179     reading_multipart_level = TRUE;
2180
2181     if (is_first_part)  /* start with first part of new multi-part level */
2182     {
2183       /* copy level info structure from first part */
2184       multipart_level = *level;
2185
2186       /* clear playfield of new multi-part level */
2187       for (y = 0; y < MAX_LEV_FIELDY; y++)
2188         for (x = 0; x < MAX_LEV_FIELDX; x++)
2189           multipart_level.field[x][y] = EL_EMPTY;
2190     }
2191
2192     if (name_first == '?')
2193       name_first = '1';
2194     if (name_last == '?')
2195       name_last = '1';
2196
2197     multipart_xpos = (int)(name_first - '0');
2198     multipart_ypos = (int)(name_last  - '0');
2199
2200 #if 0
2201     printf("----------> part (%d/%d) of multi-part level '%s'\n",
2202            multipart_xpos, multipart_ypos, multipart_level.name);
2203 #endif
2204
2205     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2206         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2207     {
2208       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2209
2210       break;
2211     }
2212
2213     multipart_level.fieldx = MAX(multipart_level.fieldx,
2214                                  multipart_xpos * SP_LEVEL_XSIZE);
2215     multipart_level.fieldy = MAX(multipart_level.fieldy,
2216                                  multipart_ypos * SP_LEVEL_YSIZE);
2217
2218     /* copy level part at the right position of multi-part level */
2219     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2220     {
2221       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2222       {
2223         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2224         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2225
2226         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2227       }
2228     }
2229   }
2230
2231   fclose(file);
2232
2233   if (use_empty_level)
2234   {
2235     setLevelInfoToDefaults(level);
2236
2237     level->fieldx = SP_LEVEL_XSIZE;
2238     level->fieldy = SP_LEVEL_YSIZE;
2239
2240     for (y = 0; y < SP_LEVEL_YSIZE; y++)
2241       for (x = 0; x < SP_LEVEL_XSIZE; x++)
2242         level->field[x][y] = EL_EMPTY;
2243
2244     strcpy(level->name, "-------- EMPTY --------");
2245
2246     Error(ERR_WARN, "single part of multi-part level -- using empty level");
2247   }
2248
2249   if (reading_multipart_level)
2250     *level = multipart_level;
2251 }
2252
2253 /* ------------------------------------------------------------------------- */
2254 /* functions for loading generic level                                       */
2255 /* ------------------------------------------------------------------------- */
2256
2257 void LoadLevelFromFileInfo(struct LevelInfo *level,
2258                            struct LevelFileInfo *level_file_info)
2259 {
2260   /* always start with reliable default values */
2261   setLevelInfoToDefaults(level);
2262
2263   switch (level_file_info->type)
2264   {
2265     case LEVEL_FILE_TYPE_RND:
2266       LoadLevelFromFileInfo_RND(level, level_file_info);
2267       break;
2268
2269     case LEVEL_FILE_TYPE_EM:
2270       LoadLevelFromFileInfo_EM(level, level_file_info);
2271       level->game_engine_type = GAME_ENGINE_TYPE_EM;
2272       break;
2273
2274     case LEVEL_FILE_TYPE_SP:
2275       LoadLevelFromFileInfo_SP(level, level_file_info);
2276       break;
2277
2278     default:
2279       LoadLevelFromFileInfo_RND(level, level_file_info);
2280       break;
2281   }
2282
2283   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
2284     level->game_engine_type = GAME_ENGINE_TYPE_RND;
2285
2286   CopyNativeLevel_Native_to_RND(level);
2287 }
2288
2289 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2290 {
2291   static struct LevelFileInfo level_file_info;
2292
2293   /* always start with reliable default values */
2294   setFileInfoToDefaults(&level_file_info);
2295
2296   level_file_info.nr = 0;                       /* unknown level number */
2297   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
2298   level_file_info.filename = filename;
2299
2300   LoadLevelFromFileInfo(level, &level_file_info);
2301 }
2302
2303 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2304 {
2305   if (leveldir_current == NULL)         /* only when dumping level */
2306     return;
2307
2308 #if 0
2309   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2310 #endif
2311
2312   /* determine correct game engine version of current level */
2313 #if 1
2314   if (!leveldir_current->latest_engine)
2315 #else
2316   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2317       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2318       IS_LEVELCLASS_UNDEFINED(leveldir_current))
2319 #endif
2320   {
2321 #if 0
2322     printf("\n::: This level is private or contributed: '%s'\n", filename);
2323 #endif
2324
2325 #if 0
2326     printf("\n::: Use the stored game engine version for this level\n");
2327 #endif
2328
2329     /* For all levels which are not forced to use the latest game engine
2330        version (normally user contributed, private and undefined levels),
2331        use the version of the game engine the levels were created for.
2332
2333        Since 2.0.1, the game engine version is now directly stored
2334        in the level file (chunk "VERS"), so there is no need anymore
2335        to set the game version from the file version (except for old,
2336        pre-2.0 levels, where the game version is still taken from the
2337        file format version used to store the level -- see above). */
2338
2339 #if 1
2340     /* player was faster than enemies in 1.0.0 and before */
2341     if (level->file_version == FILE_VERSION_1_0)
2342       level->double_speed = TRUE;
2343 #else
2344     /* do some special adjustments to support older level versions */
2345     if (level->file_version == FILE_VERSION_1_0)
2346     {
2347       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2348       Error(ERR_WARN, "using high speed movement for player");
2349
2350       /* player was faster than monsters in (pre-)1.0 levels */
2351       level->double_speed = TRUE;
2352     }
2353 #endif
2354
2355     /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2356     if (level->game_version == VERSION_IDENT(2,0,1,0))
2357       level->em_slippery_gems = TRUE;
2358
2359     /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2360     if (level->game_version < VERSION_IDENT(2,2,0,0))
2361       level->use_spring_bug = TRUE;
2362
2363     /* only few elements were able to actively move into acid before 3.1.0 */
2364     if (level->game_version < VERSION_IDENT(3,1,0,0))
2365     {
2366       int i, j;
2367
2368       level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2369       level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2370
2371       setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
2372       setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2373       setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
2374       setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
2375
2376       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2377         SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2378
2379       for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2380       {
2381         int element = EL_CUSTOM_START + i;
2382         struct ElementInfo *ei = &element_info[element];
2383
2384         for (j = 0; j < ei->num_change_pages; j++)
2385         {
2386           struct ElementChangeInfo *change = &ei->change_page[j];
2387
2388           change->trigger_player = CH_PLAYER_ANY;
2389           change->trigger_page = CH_PAGE_ANY;
2390         }
2391       }
2392     }
2393
2394 #if 1   /* USE_NEW_BLOCK_STYLE */
2395     /* blocking the last field when moving was corrected in version 3.1.1 */
2396     if (level->game_version < VERSION_IDENT(3,1,1,0))
2397     {
2398 #if 0
2399       printf("::: %d\n", level->block_last_field);
2400 #endif
2401
2402       /* even "not blocking" was blocking the last field for one frame */
2403       level->block_delay    = (level->block_last_field    ? 7 : 1);
2404       level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
2405
2406       level->block_last_field = TRUE;
2407       level->sp_block_last_field = TRUE;
2408     }
2409 #endif
2410   }
2411   else          /* always use the latest game engine version */
2412   {
2413 #if 0
2414     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2415            leveldir_current->sort_priority, filename);
2416 #endif
2417
2418 #if 0
2419     printf("\n::: Use latest game engine version for this level.\n");
2420 #endif
2421
2422     /* For all levels which are forced to use the latest game engine version
2423        (normally all but user contributed, private and undefined levels), set
2424        the game engine version to the actual version; this allows for actual
2425        corrections in the game engine to take effect for existing, converted
2426        levels (from "classic" or other existing games) to make the emulation
2427        of the corresponding game more accurate, while (hopefully) not breaking
2428        existing levels created from other players. */
2429
2430 #if 0
2431     printf("::: changing engine from %d to %d\n",
2432            level->game_version, GAME_VERSION_ACTUAL);
2433 #endif
2434
2435     level->game_version = GAME_VERSION_ACTUAL;
2436
2437     /* Set special EM style gems behaviour: EM style gems slip down from
2438        normal, steel and growing wall. As this is a more fundamental change,
2439        it seems better to set the default behaviour to "off" (as it is more
2440        natural) and make it configurable in the level editor (as a property
2441        of gem style elements). Already existing converted levels (neither
2442        private nor contributed levels) are changed to the new behaviour. */
2443
2444     if (level->file_version < FILE_VERSION_2_0)
2445       level->em_slippery_gems = TRUE;
2446   }
2447
2448 #if 0
2449   printf("::: => %d\n", level->game_version);
2450 #endif
2451 }
2452
2453 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2454 {
2455   int i, j, x, y;
2456
2457   /* map custom element change events that have changed in newer versions
2458      (these following values were accidentally changed in version 3.0.1) */
2459   if (level->game_version <= VERSION_IDENT(3,0,0,0))
2460   {
2461     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2462     {
2463       int element = EL_CUSTOM_START + i;
2464
2465       /* order of checking and copying events to be mapped is important */
2466       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2467       {
2468         if (HAS_CHANGE_EVENT(element, j - 2))
2469         {
2470           SET_CHANGE_EVENT(element, j - 2, FALSE);
2471           SET_CHANGE_EVENT(element, j, TRUE);
2472         }
2473       }
2474
2475       /* order of checking and copying events to be mapped is important */
2476       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2477       {
2478         if (HAS_CHANGE_EVENT(element, j - 1))
2479         {
2480           SET_CHANGE_EVENT(element, j - 1, FALSE);
2481           SET_CHANGE_EVENT(element, j, TRUE);
2482         }
2483       }
2484     }
2485   }
2486
2487   /* some custom element change events get mapped since version 3.0.3 */
2488   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2489   {
2490     int element = EL_CUSTOM_START + i;
2491
2492     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2493         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2494     {
2495       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2496       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2497
2498       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2499     }
2500   }
2501
2502   /* initialize "can_change" field for old levels with only one change page */
2503   if (level->game_version <= VERSION_IDENT(3,0,2,0))
2504   {
2505     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2506     {
2507       int element = EL_CUSTOM_START + i;
2508
2509       if (CAN_CHANGE(element))
2510         element_info[element].change->can_change = TRUE;
2511     }
2512   }
2513
2514   /* correct custom element values (for old levels without these options) */
2515   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2516   {
2517     int element = EL_CUSTOM_START + i;
2518     struct ElementInfo *ei = &element_info[element];
2519
2520     if (ei->access_direction == MV_NO_MOVING)
2521       ei->access_direction = MV_ALL_DIRECTIONS;
2522
2523     for (j = 0; j < ei->num_change_pages; j++)
2524     {
2525       struct ElementChangeInfo *change = &ei->change_page[j];
2526
2527       if (change->trigger_side == CH_SIDE_NONE)
2528         change->trigger_side = CH_SIDE_ANY;
2529     }
2530   }
2531
2532   /* initialize "can_explode" field for old levels which did not store this */
2533   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
2534   if (level->game_version <= VERSION_IDENT(3,1,0,0))
2535   {
2536     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2537     {
2538       int element = EL_CUSTOM_START + i;
2539
2540       if (EXPLODES_1X1_OLD(element))
2541         element_info[element].explosion_type = EXPLODES_1X1;
2542
2543       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
2544                                              EXPLODES_SMASHED(element) ||
2545                                              EXPLODES_IMPACT(element)));
2546     }
2547   }
2548
2549   /* correct previously hard-coded move delay values for maze runner style */
2550   if (level->game_version < VERSION_IDENT(3,1,1,0))
2551   {
2552     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2553     {
2554       int element = EL_CUSTOM_START + i;
2555
2556       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
2557       {
2558         /* previously hard-coded and therefore ignored */
2559         element_info[element].move_delay_fixed = 9;
2560         element_info[element].move_delay_random = 0;
2561       }
2562     }
2563   }
2564
2565 #if 0
2566   /* set default push delay values (corrected since version 3.0.7-1) */
2567   if (level->game_version < VERSION_IDENT(3,0,7,1))
2568   {
2569     game.default_push_delay_fixed = 2;
2570     game.default_push_delay_random = 8;
2571   }
2572   else
2573   {
2574     game.default_push_delay_fixed = 8;
2575     game.default_push_delay_random = 8;
2576   }
2577
2578   /* set uninitialized push delay values of custom elements in older levels */
2579   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2580   {
2581     int element = EL_CUSTOM_START + i;
2582
2583     if (element_info[element].push_delay_fixed == -1)
2584       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2585     if (element_info[element].push_delay_random == -1)
2586       element_info[element].push_delay_random = game.default_push_delay_random;
2587   }
2588 #endif
2589
2590   /* map elements that have changed in newer versions */
2591   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2592                                                     level->game_version);
2593   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2594     for (x = 0; x < 3; x++)
2595       for (y = 0; y < 3; y++)
2596         level->yamyam_content[i][x][y] =
2597           getMappedElementByVersion(level->yamyam_content[i][x][y],
2598                                     level->game_version);
2599
2600   /* initialize element properties for level editor etc. */
2601   InitElementPropertiesEngine(level->game_version);
2602 }
2603
2604 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2605 {
2606   int x, y;
2607
2608   /* map elements that have changed in newer versions */
2609   for (y = 0; y < level->fieldy; y++)
2610   {
2611     for (x = 0; x < level->fieldx; x++)
2612     {
2613       int element = level->field[x][y];
2614
2615 #if 1
2616       element = getMappedElementByVersion(element, level->game_version);
2617 #else
2618       if (level->game_version <= VERSION_IDENT(2,2,0,0))
2619       {
2620         /* map game font elements */
2621         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2622                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2623                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2624                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2625       }
2626
2627       if (level->game_version < VERSION_IDENT(3,0,0,0))
2628       {
2629         /* map Supaplex gravity tube elements */
2630         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2631                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2632                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2633                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2634                    element);
2635       }
2636 #endif
2637
2638       level->field[x][y] = element;
2639     }
2640   }
2641
2642   /* copy elements to runtime playfield array */
2643   for (x = 0; x < MAX_LEV_FIELDX; x++)
2644     for (y = 0; y < MAX_LEV_FIELDY; y++)
2645       Feld[x][y] = level->field[x][y];
2646
2647   /* initialize level size variables for faster access */
2648   lev_fieldx = level->fieldx;
2649   lev_fieldy = level->fieldy;
2650
2651   /* determine border element for this level */
2652   SetBorderElement();
2653 }
2654
2655 void LoadLevelTemplate(int nr)
2656 {
2657 #if 1
2658   char *filename;
2659
2660   setLevelFileInfo(&level_template.file_info, nr);
2661   filename = level_template.file_info.filename;
2662
2663   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
2664
2665 #else
2666
2667 #if 1
2668   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2669   char *filename = level_file_info->filename;
2670
2671   LoadLevelFromFileInfo(&level_template, level_file_info);
2672 #else
2673   char *filename = getDefaultLevelFilename(nr);
2674
2675   LoadLevelFromFilename_RND(&level_template, filename);
2676 #endif
2677 #endif
2678
2679 #if 1
2680   LoadLevel_InitVersion(&level_template, filename);
2681   LoadLevel_InitElements(&level_template, filename);
2682 #else
2683   LoadLevel_InitVersion(&level, filename);
2684   LoadLevel_InitElements(&level, filename);
2685 #endif
2686
2687   ActivateLevelTemplate();
2688 }
2689
2690 void LoadLevel(int nr)
2691 {
2692 #if 1
2693   char *filename;
2694
2695   setLevelFileInfo(&level.file_info, nr);
2696   filename = level.file_info.filename;
2697
2698   LoadLevelFromFileInfo(&level, &level.file_info);
2699
2700 #else
2701
2702 #if 1
2703   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2704   char *filename = level_file_info->filename;
2705
2706   LoadLevelFromFileInfo(&level, level_file_info);
2707 #else
2708   char *filename = getLevelFilename(nr);
2709
2710   LoadLevelFromFilename_RND(&level, filename);
2711 #endif
2712 #endif
2713
2714   if (level.use_custom_template)
2715     LoadLevelTemplate(-1);
2716
2717 #if 1
2718   LoadLevel_InitVersion(&level, filename);
2719   LoadLevel_InitElements(&level, filename);
2720   LoadLevel_InitPlayfield(&level, filename);
2721 #else
2722   LoadLevel_InitLevel(&level, filename);
2723 #endif
2724 }
2725
2726 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2727 {
2728   putFileVersion(file, level->file_version);
2729   putFileVersion(file, level->game_version);
2730 }
2731
2732 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2733 {
2734   int i, x, y;
2735
2736   putFile8Bit(file, level->fieldx);
2737   putFile8Bit(file, level->fieldy);
2738
2739   putFile16BitBE(file, level->time);
2740   putFile16BitBE(file, level->gems_needed);
2741
2742   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2743     putFile8Bit(file, level->name[i]);
2744
2745   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2746     putFile8Bit(file, level->score[i]);
2747
2748   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2749     for (y = 0; y < 3; y++)
2750       for (x = 0; x < 3; x++)
2751         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2752                            level->yamyam_content[i][x][y]));
2753   putFile8Bit(file, level->amoeba_speed);
2754   putFile8Bit(file, level->time_magic_wall);
2755   putFile8Bit(file, level->time_wheel);
2756   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2757                      level->amoeba_content));
2758   putFile8Bit(file, (level->double_speed ? 1 : 0));
2759   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2760   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2761   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2762
2763   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2764
2765   putFile8Bit(file, (level->block_last_field ? 1 : 0));
2766   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2767   putFile32BitBE(file, level->can_move_into_acid_bits);
2768   putFile8Bit(file, level->dont_collide_with_bits);
2769
2770   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2771   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2772
2773   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
2774   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
2775   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
2776
2777   putFile8Bit(file, level->game_engine_type);
2778
2779   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2780 }
2781
2782 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2783 {
2784   int i;
2785
2786   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2787     putFile8Bit(file, level->author[i]);
2788 }
2789
2790 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2791 {
2792   int x, y;
2793
2794   for (y = 0; y < level->fieldy; y++) 
2795     for (x = 0; x < level->fieldx; x++) 
2796       if (level->encoding_16bit_field)
2797         putFile16BitBE(file, level->field[x][y]);
2798       else
2799         putFile8Bit(file, level->field[x][y]);
2800 }
2801
2802 #if 0
2803 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2804 {
2805   int i, x, y;
2806
2807   putFile8Bit(file, EL_YAMYAM);
2808   putFile8Bit(file, level->num_yamyam_contents);
2809   putFile8Bit(file, 0);
2810   putFile8Bit(file, 0);
2811
2812   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2813     for (y = 0; y < 3; y++)
2814       for (x = 0; x < 3; x++)
2815         if (level->encoding_16bit_field)
2816           putFile16BitBE(file, level->yamyam_content[i][x][y]);
2817         else
2818           putFile8Bit(file, level->yamyam_content[i][x][y]);
2819 }
2820 #endif
2821
2822 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2823 {
2824   int i, x, y;
2825   int num_contents, content_xsize, content_ysize;
2826   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2827
2828   if (element == EL_YAMYAM)
2829   {
2830     num_contents = level->num_yamyam_contents;
2831     content_xsize = 3;
2832     content_ysize = 3;
2833
2834     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2835       for (y = 0; y < 3; y++)
2836         for (x = 0; x < 3; x++)
2837           content_array[i][x][y] = level->yamyam_content[i][x][y];
2838   }
2839   else if (element == EL_BD_AMOEBA)
2840   {
2841     num_contents = 1;
2842     content_xsize = 1;
2843     content_ysize = 1;
2844
2845     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2846       for (y = 0; y < 3; y++)
2847         for (x = 0; x < 3; x++)
2848           content_array[i][x][y] = EL_EMPTY;
2849     content_array[0][0][0] = level->amoeba_content;
2850   }
2851   else
2852   {
2853     /* chunk header already written -- write empty chunk data */
2854     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2855
2856     Error(ERR_WARN, "cannot save content for element '%d'", element);
2857     return;
2858   }
2859
2860   putFile16BitBE(file, element);
2861   putFile8Bit(file, num_contents);
2862   putFile8Bit(file, content_xsize);
2863   putFile8Bit(file, content_ysize);
2864
2865   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2866
2867   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2868     for (y = 0; y < 3; y++)
2869       for (x = 0; x < 3; x++)
2870         putFile16BitBE(file, content_array[i][x][y]);
2871 }
2872
2873 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2874 {
2875   int i;
2876   int envelope_nr = element - EL_ENVELOPE_1;
2877   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2878
2879   putFile16BitBE(file, element);
2880   putFile16BitBE(file, envelope_len);
2881   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2882   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2883
2884   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2885
2886   for (i = 0; i < envelope_len; i++)
2887     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2888 }
2889
2890 #if 0
2891 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2892                            int num_changed_custom_elements)
2893 {
2894   int i, check = 0;
2895
2896   putFile16BitBE(file, num_changed_custom_elements);
2897
2898   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2899   {
2900     int element = EL_CUSTOM_START + i;
2901
2902     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2903     {
2904       if (check < num_changed_custom_elements)
2905       {
2906         putFile16BitBE(file, element);
2907         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2908       }
2909
2910       check++;
2911     }
2912   }
2913
2914   if (check != num_changed_custom_elements)     /* should not happen */
2915     Error(ERR_WARN, "inconsistent number of custom element properties");
2916 }
2917 #endif
2918
2919 #if 0
2920 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2921                            int num_changed_custom_elements)
2922 {
2923   int i, check = 0;
2924
2925   putFile16BitBE(file, num_changed_custom_elements);
2926
2927   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2928   {
2929     int element = EL_CUSTOM_START + i;
2930
2931     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2932     {
2933       if (check < num_changed_custom_elements)
2934       {
2935         putFile16BitBE(file, element);
2936         putFile16BitBE(file, element_info[element].change->target_element);
2937       }
2938
2939       check++;
2940     }
2941   }
2942
2943   if (check != num_changed_custom_elements)     /* should not happen */
2944     Error(ERR_WARN, "inconsistent number of custom target elements");
2945 }
2946 #endif
2947
2948 #if 0
2949 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2950                            int num_changed_custom_elements)
2951 {
2952   int i, j, x, y, check = 0;
2953
2954   putFile16BitBE(file, num_changed_custom_elements);
2955
2956   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2957   {
2958     int element = EL_CUSTOM_START + i;
2959
2960     if (element_info[element].modified_settings)
2961     {
2962       if (check < num_changed_custom_elements)
2963       {
2964         putFile16BitBE(file, element);
2965
2966         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2967           putFile8Bit(file, element_info[element].description[j]);
2968
2969         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2970
2971         /* some free bytes for future properties and padding */
2972         WriteUnusedBytesToFile(file, 7);
2973
2974         putFile8Bit(file, element_info[element].use_gfx_element);
2975         putFile16BitBE(file, element_info[element].gfx_element);
2976
2977         putFile8Bit(file, element_info[element].collect_score);
2978         putFile8Bit(file, element_info[element].collect_count);
2979
2980         putFile16BitBE(file, element_info[element].push_delay_fixed);
2981         putFile16BitBE(file, element_info[element].push_delay_random);
2982         putFile16BitBE(file, element_info[element].move_delay_fixed);
2983         putFile16BitBE(file, element_info[element].move_delay_random);
2984
2985         putFile16BitBE(file, element_info[element].move_pattern);
2986         putFile8Bit(file, element_info[element].move_direction_initial);
2987         putFile8Bit(file, element_info[element].move_stepsize);
2988
2989         for (y = 0; y < 3; y++)
2990           for (x = 0; x < 3; x++)
2991             putFile16BitBE(file, element_info[element].content[x][y]);
2992
2993         putFile32BitBE(file, element_info[element].change->events);
2994
2995         putFile16BitBE(file, element_info[element].change->target_element);
2996
2997         putFile16BitBE(file, element_info[element].change->delay_fixed);
2998         putFile16BitBE(file, element_info[element].change->delay_random);
2999         putFile16BitBE(file, element_info[element].change->delay_frames);
3000
3001         putFile16BitBE(file, element_info[element].change->trigger_element);
3002
3003         putFile8Bit(file, element_info[element].change->explode);
3004         putFile8Bit(file, element_info[element].change->use_target_content);
3005         putFile8Bit(file, element_info[element].change->only_if_complete);
3006         putFile8Bit(file, element_info[element].change->use_random_replace);
3007
3008         putFile8Bit(file, element_info[element].change->random_percentage);
3009         putFile8Bit(file, element_info[element].change->replace_when);
3010
3011         for (y = 0; y < 3; y++)
3012           for (x = 0; x < 3; x++)
3013             putFile16BitBE(file, element_info[element].change->content[x][y]);
3014
3015         putFile8Bit(file, element_info[element].slippery_type);
3016
3017         /* some free bytes for future properties and padding */
3018         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3019       }
3020
3021       check++;
3022     }
3023   }
3024
3025   if (check != num_changed_custom_elements)     /* should not happen */
3026     Error(ERR_WARN, "inconsistent number of custom element properties");
3027 }
3028 #endif
3029
3030 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3031 {
3032   struct ElementInfo *ei = &element_info[element];
3033   int i, x, y;
3034
3035   putFile16BitBE(file, element);
3036
3037   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3038     putFile8Bit(file, ei->description[i]);
3039
3040   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3041   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
3042
3043   putFile8Bit(file, ei->num_change_pages);
3044
3045   /* some free bytes for future base property values and padding */
3046   WriteUnusedBytesToFile(file, 5);
3047
3048   /* write custom property values */
3049
3050   putFile8Bit(file, ei->use_gfx_element);
3051   putFile16BitBE(file, ei->gfx_element);
3052
3053   putFile8Bit(file, ei->collect_score);
3054   putFile8Bit(file, ei->collect_count);
3055
3056   putFile8Bit(file, ei->drop_delay_fixed);
3057   putFile8Bit(file, ei->push_delay_fixed);
3058   putFile8Bit(file, ei->drop_delay_random);
3059   putFile8Bit(file, ei->push_delay_random);
3060   putFile16BitBE(file, ei->move_delay_fixed);
3061   putFile16BitBE(file, ei->move_delay_random);
3062
3063   /* bits 0 - 15 of "move_pattern" ... */
3064   putFile16BitBE(file, ei->move_pattern & 0xffff);
3065   putFile8Bit(file, ei->move_direction_initial);
3066   putFile8Bit(file, ei->move_stepsize);
3067
3068   putFile8Bit(file, ei->slippery_type);
3069
3070   for (y = 0; y < 3; y++)
3071     for (x = 0; x < 3; x++)
3072       putFile16BitBE(file, ei->content[x][y]);
3073
3074   putFile16BitBE(file, ei->move_enter_element);
3075   putFile16BitBE(file, ei->move_leave_element);
3076   putFile8Bit(file, ei->move_leave_type);
3077
3078   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3079   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3080
3081   putFile8Bit(file, ei->access_direction);
3082
3083   putFile8Bit(file, ei->explosion_delay);
3084   putFile8Bit(file, ei->ignition_delay);
3085   putFile8Bit(file, ei->explosion_type);
3086
3087   /* some free bytes for future custom property values and padding */
3088   WriteUnusedBytesToFile(file, 1);
3089
3090   /* write change property values */
3091
3092   for (i = 0; i < ei->num_change_pages; i++)
3093   {
3094     struct ElementChangeInfo *change = &ei->change_page[i];
3095
3096     putFile32BitBE(file, change->events);
3097
3098     putFile16BitBE(file, change->target_element);
3099
3100     putFile16BitBE(file, change->delay_fixed);
3101     putFile16BitBE(file, change->delay_random);
3102     putFile16BitBE(file, change->delay_frames);
3103
3104     putFile16BitBE(file, change->trigger_element);
3105
3106     putFile8Bit(file, change->explode);
3107     putFile8Bit(file, change->use_target_content);
3108     putFile8Bit(file, change->only_if_complete);
3109     putFile8Bit(file, change->use_random_replace);
3110
3111     putFile8Bit(file, change->random_percentage);
3112     putFile8Bit(file, change->replace_when);
3113
3114     for (y = 0; y < 3; y++)
3115       for (x = 0; x < 3; x++)
3116         putFile16BitBE(file, change->target_content[x][y]);
3117
3118     putFile8Bit(file, change->can_change);
3119
3120     putFile8Bit(file, change->trigger_side);
3121
3122 #if 1
3123     putFile8Bit(file, change->trigger_player);
3124     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3125                        log_2(change->trigger_page)));
3126
3127     /* some free bytes for future change property values and padding */
3128     WriteUnusedBytesToFile(file, 6);
3129
3130 #else
3131
3132     /* some free bytes for future change property values and padding */
3133     WriteUnusedBytesToFile(file, 8);
3134 #endif
3135   }
3136 }
3137
3138 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3139 {
3140   struct ElementInfo *ei = &element_info[element];
3141   struct ElementGroupInfo *group = ei->group;
3142   int i;
3143
3144   putFile16BitBE(file, element);
3145
3146   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3147     putFile8Bit(file, ei->description[i]);
3148
3149   putFile8Bit(file, group->num_elements);
3150
3151   putFile8Bit(file, ei->use_gfx_element);
3152   putFile16BitBE(file, ei->gfx_element);
3153
3154   putFile8Bit(file, group->choice_mode);
3155
3156   /* some free bytes for future values and padding */
3157   WriteUnusedBytesToFile(file, 3);
3158
3159   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3160     putFile16BitBE(file, group->element[i]);
3161 }
3162
3163 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3164 {
3165   int body_chunk_size;
3166   int i, x, y;
3167   FILE *file;
3168
3169   if (!(file = fopen(filename, MODE_WRITE)))
3170   {
3171     Error(ERR_WARN, "cannot save level file '%s'", filename);
3172     return;
3173   }
3174
3175   level->file_version = FILE_VERSION_ACTUAL;
3176   level->game_version = GAME_VERSION_ACTUAL;
3177
3178   /* check level field for 16-bit elements */
3179   level->encoding_16bit_field = FALSE;
3180   for (y = 0; y < level->fieldy; y++) 
3181     for (x = 0; x < level->fieldx; x++) 
3182       if (level->field[x][y] > 255)
3183         level->encoding_16bit_field = TRUE;
3184
3185   /* check yamyam content for 16-bit elements */
3186   level->encoding_16bit_yamyam = FALSE;
3187   for (i = 0; i < level->num_yamyam_contents; i++)
3188     for (y = 0; y < 3; y++)
3189       for (x = 0; x < 3; x++)
3190         if (level->yamyam_content[i][x][y] > 255)
3191           level->encoding_16bit_yamyam = TRUE;
3192
3193   /* check amoeba content for 16-bit elements */
3194   level->encoding_16bit_amoeba = FALSE;
3195   if (level->amoeba_content > 255)
3196     level->encoding_16bit_amoeba = TRUE;
3197
3198   /* calculate size of "BODY" chunk */
3199   body_chunk_size =
3200     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3201
3202   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3203   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3204
3205   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3206   SaveLevel_VERS(file, level);
3207
3208   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3209   SaveLevel_HEAD(file, level);
3210
3211   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3212   SaveLevel_AUTH(file, level);
3213
3214   putFileChunkBE(file, "BODY", body_chunk_size);
3215   SaveLevel_BODY(file, level);
3216
3217   if (level->encoding_16bit_yamyam ||
3218       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3219   {
3220     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3221     SaveLevel_CNT2(file, level, EL_YAMYAM);
3222   }
3223
3224   if (level->encoding_16bit_amoeba)
3225   {
3226     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3227     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3228   }
3229
3230   /* check for envelope content */
3231   for (i = 0; i < 4; i++)
3232   {
3233     if (strlen(level->envelope_text[i]) > 0)
3234     {
3235       int envelope_len = strlen(level->envelope_text[i]) + 1;
3236
3237       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3238       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3239     }
3240   }
3241
3242   /* check for non-default custom elements (unless using template level) */
3243   if (!level->use_custom_template)
3244   {
3245     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246     {
3247       int element = EL_CUSTOM_START + i;
3248
3249       if (element_info[element].modified_settings)
3250       {
3251         int num_change_pages = element_info[element].num_change_pages;
3252
3253         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3254         SaveLevel_CUS4(file, level, element);
3255       }
3256     }
3257   }
3258
3259   /* check for non-default group elements (unless using template level) */
3260   if (!level->use_custom_template)
3261   {
3262     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3263     {
3264       int element = EL_GROUP_START + i;
3265
3266       if (element_info[element].modified_settings)
3267       {
3268         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3269         SaveLevel_GRP1(file, level, element);
3270       }
3271     }
3272   }
3273
3274   fclose(file);
3275
3276   SetFilePermissions(filename, PERMS_PRIVATE);
3277 }
3278
3279 void SaveLevel(int nr)
3280 {
3281   char *filename = getDefaultLevelFilename(nr);
3282
3283   SaveLevelFromFilename(&level, filename);
3284 }
3285
3286 void SaveLevelTemplate()
3287 {
3288   char *filename = getDefaultLevelFilename(-1);
3289
3290   SaveLevelFromFilename(&level, filename);
3291 }
3292
3293 void DumpLevel(struct LevelInfo *level)
3294 {
3295   if (level->no_valid_file)
3296   {
3297     Error(ERR_WARN, "cannot dump -- no valid level file found");
3298
3299     return;
3300   }
3301
3302   printf_line("-", 79);
3303   printf("Level xxx (file version %08d, game version %08d)\n",
3304          level->file_version, level->game_version);
3305   printf_line("-", 79);
3306
3307   printf("Level author: '%s'\n", level->author);
3308   printf("Level title:  '%s'\n", level->name);
3309   printf("\n");
3310   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3311   printf("\n");
3312   printf("Level time:  %d seconds\n", level->time);
3313   printf("Gems needed: %d\n", level->gems_needed);
3314   printf("\n");
3315   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3316   printf("Time for wheel:      %d seconds\n", level->time_wheel);
3317   printf("Time for light:      %d seconds\n", level->time_light);
3318   printf("Time for timegate:   %d seconds\n", level->time_timegate);
3319   printf("\n");
3320   printf("Amoeba speed: %d\n", level->amoeba_speed);
3321   printf("\n");
3322   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
3323   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
3324   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
3325   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
3326   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3327   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3328   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3329
3330   printf_line("-", 79);
3331 }
3332
3333
3334 /* ========================================================================= */
3335 /* tape file functions                                                       */
3336 /* ========================================================================= */
3337
3338 static void setTapeInfoToDefaults()
3339 {
3340   int i;
3341
3342   /* always start with reliable default values (empty tape) */
3343   TapeErase();
3344
3345   /* default values (also for pre-1.2 tapes) with only the first player */
3346   tape.player_participates[0] = TRUE;
3347   for (i = 1; i < MAX_PLAYERS; i++)
3348     tape.player_participates[i] = FALSE;
3349
3350   /* at least one (default: the first) player participates in every tape */
3351   tape.num_participating_players = 1;
3352
3353   tape.level_nr = level_nr;
3354   tape.counter = 0;
3355   tape.changed = FALSE;
3356
3357   tape.recording = FALSE;
3358   tape.playing = FALSE;
3359   tape.pausing = FALSE;
3360
3361   tape.no_valid_file = FALSE;
3362 }
3363
3364 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3365 {
3366   tape->file_version = getFileVersion(file);
3367   tape->game_version = getFileVersion(file);
3368
3369   return chunk_size;
3370 }
3371
3372 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3373 {
3374   int i;
3375
3376   tape->random_seed = getFile32BitBE(file);
3377   tape->date        = getFile32BitBE(file);
3378   tape->length      = getFile32BitBE(file);
3379
3380   /* read header fields that are new since version 1.2 */
3381   if (tape->file_version >= FILE_VERSION_1_2)
3382   {
3383     byte store_participating_players = getFile8Bit(file);
3384     int engine_version;
3385
3386     /* since version 1.2, tapes store which players participate in the tape */
3387     tape->num_participating_players = 0;
3388     for (i = 0; i < MAX_PLAYERS; i++)
3389     {
3390       tape->player_participates[i] = FALSE;
3391
3392       if (store_participating_players & (1 << i))
3393       {
3394         tape->player_participates[i] = TRUE;
3395         tape->num_participating_players++;
3396       }
3397     }
3398
3399     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3400
3401     engine_version = getFileVersion(file);
3402     if (engine_version > 0)
3403       tape->engine_version = engine_version;
3404     else
3405       tape->engine_version = tape->game_version;
3406   }
3407
3408   return chunk_size;
3409 }
3410
3411 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3412 {
3413   int level_identifier_size;
3414   int i;
3415
3416   level_identifier_size = getFile16BitBE(file);
3417
3418   tape->level_identifier =
3419     checked_realloc(tape->level_identifier, level_identifier_size);
3420
3421   for (i = 0; i < level_identifier_size; i++)
3422     tape->level_identifier[i] = getFile8Bit(file);
3423
3424   tape->level_nr = getFile16BitBE(file);
3425
3426   chunk_size = 2 + level_identifier_size + 2;
3427
3428   return chunk_size;
3429 }
3430
3431 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3432 {
3433   int i, j;
3434   int chunk_size_expected =
3435     (tape->num_participating_players + 1) * tape->length;
3436
3437   if (chunk_size_expected != chunk_size)
3438   {
3439     ReadUnusedBytesFromFile(file, chunk_size);
3440     return chunk_size_expected;
3441   }
3442
3443   for (i = 0; i < tape->length; i++)
3444   {
3445     if (i >= MAX_TAPELEN)
3446       break;
3447
3448     for (j = 0; j < MAX_PLAYERS; j++)
3449     {
3450       tape->pos[i].action[j] = MV_NO_MOVING;
3451
3452       if (tape->player_participates[j])
3453         tape->pos[i].action[j] = getFile8Bit(file);
3454     }
3455
3456     tape->pos[i].delay = getFile8Bit(file);
3457
3458     if (tape->file_version == FILE_VERSION_1_0)
3459     {
3460       /* eliminate possible diagonal moves in old tapes */
3461       /* this is only for backward compatibility */
3462
3463       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3464       byte action = tape->pos[i].action[0];
3465       int k, num_moves = 0;
3466
3467       for (k = 0; k<4; k++)
3468       {
3469         if (action & joy_dir[k])
3470         {
3471           tape->pos[i + num_moves].action[0] = joy_dir[k];
3472           if (num_moves > 0)
3473             tape->pos[i + num_moves].delay = 0;
3474           num_moves++;
3475         }
3476       }
3477
3478       if (num_moves > 1)
3479       {
3480         num_moves--;
3481         i += num_moves;
3482         tape->length += num_moves;
3483       }
3484     }
3485     else if (tape->file_version < FILE_VERSION_2_0)
3486     {
3487       /* convert pre-2.0 tapes to new tape format */
3488
3489       if (tape->pos[i].delay > 1)
3490       {
3491         /* action part */
3492         tape->pos[i + 1] = tape->pos[i];
3493         tape->pos[i + 1].delay = 1;
3494
3495         /* delay part */
3496         for (j = 0; j < MAX_PLAYERS; j++)
3497           tape->pos[i].action[j] = MV_NO_MOVING;
3498         tape->pos[i].delay--;
3499
3500         i++;
3501         tape->length++;
3502       }
3503     }
3504
3505     if (feof(file))
3506       break;
3507   }
3508
3509   if (i != tape->length)
3510     chunk_size = (tape->num_participating_players + 1) * i;
3511
3512   return chunk_size;
3513 }
3514
3515 void LoadTapeFromFilename(char *filename)
3516 {
3517   char cookie[MAX_LINE_LEN];
3518   char chunk_name[CHUNK_ID_LEN + 1];
3519   FILE *file;
3520   int chunk_size;
3521
3522   /* always start with reliable default values */
3523   setTapeInfoToDefaults();
3524
3525   if (!(file = fopen(filename, MODE_READ)))
3526   {
3527     tape.no_valid_file = TRUE;
3528
3529 #if 0
3530     Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
3531 #endif
3532
3533     return;
3534   }
3535
3536   getFileChunkBE(file, chunk_name, NULL);
3537   if (strcmp(chunk_name, "RND1") == 0)
3538   {
3539     getFile32BitBE(file);               /* not used */
3540
3541     getFileChunkBE(file, chunk_name, NULL);
3542     if (strcmp(chunk_name, "TAPE") != 0)
3543     {
3544       tape.no_valid_file = TRUE;
3545
3546       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3547       fclose(file);
3548       return;
3549     }
3550   }
3551   else  /* check for pre-2.0 file format with cookie string */
3552   {
3553     strcpy(cookie, chunk_name);
3554     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3555     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3556       cookie[strlen(cookie) - 1] = '\0';
3557
3558     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3559     {
3560       tape.no_valid_file = TRUE;
3561
3562       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3563       fclose(file);
3564       return;
3565     }
3566
3567     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3568     {
3569       tape.no_valid_file = TRUE;
3570
3571       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3572       fclose(file);
3573       return;
3574     }
3575
3576     /* pre-2.0 tape files have no game version, so use file version here */
3577     tape.game_version = tape.file_version;
3578   }
3579
3580   if (tape.file_version < FILE_VERSION_1_2)
3581   {
3582     /* tape files from versions before 1.2.0 without chunk structure */
3583     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3584     LoadTape_BODY(file, 2 * tape.length,  &tape);
3585   }
3586   else
3587   {
3588     static struct
3589     {
3590       char *name;
3591       int size;
3592       int (*loader)(FILE *, int, struct TapeInfo *);
3593     }
3594     chunk_info[] =
3595     {
3596       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
3597       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
3598       { "INFO", -1,                     LoadTape_INFO },
3599       { "BODY", -1,                     LoadTape_BODY },
3600       {  NULL,  0,                      NULL }
3601     };
3602
3603     while (getFileChunkBE(file, chunk_name, &chunk_size))
3604     {
3605       int i = 0;
3606
3607       while (chunk_info[i].name != NULL &&
3608              strcmp(chunk_name, chunk_info[i].name) != 0)
3609         i++;
3610
3611       if (chunk_info[i].name == NULL)
3612       {
3613         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3614               chunk_name, filename);
3615         ReadUnusedBytesFromFile(file, chunk_size);
3616       }
3617       else if (chunk_info[i].size != -1 &&
3618                chunk_info[i].size != chunk_size)
3619       {
3620         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3621               chunk_size, chunk_name, filename);
3622         ReadUnusedBytesFromFile(file, chunk_size);
3623       }
3624       else
3625       {
3626         /* call function to load this tape chunk */
3627         int chunk_size_expected =
3628           (chunk_info[i].loader)(file, chunk_size, &tape);
3629
3630         /* the size of some chunks cannot be checked before reading other
3631            chunks first (like "HEAD" and "BODY") that contain some header
3632            information, so check them here */
3633         if (chunk_size_expected != chunk_size)
3634         {
3635           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3636                 chunk_size, chunk_name, filename);
3637         }
3638       }
3639     }
3640   }
3641
3642   fclose(file);
3643
3644   tape.length_seconds = GetTapeLength();
3645
3646 #if 0
3647   printf("::: tape game version: %d\n", tape.game_version);
3648   printf("::: tape engine version: %d\n", tape.engine_version);
3649 #endif
3650 }
3651
3652 void LoadTape(int nr)
3653 {
3654   char *filename = getTapeFilename(nr);
3655
3656   LoadTapeFromFilename(filename);
3657 }
3658
3659 void LoadSolutionTape(int nr)
3660 {
3661   char *filename = getSolutionTapeFilename(nr);
3662
3663   LoadTapeFromFilename(filename);
3664 }
3665
3666 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3667 {
3668   putFileVersion(file, tape->file_version);
3669   putFileVersion(file, tape->game_version);
3670 }
3671
3672 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3673 {
3674   int i;
3675   byte store_participating_players = 0;
3676
3677   /* set bits for participating players for compact storage */
3678   for (i = 0; i < MAX_PLAYERS; i++)
3679     if (tape->player_participates[i])
3680       store_participating_players |= (1 << i);
3681
3682   putFile32BitBE(file, tape->random_seed);
3683   putFile32BitBE(file, tape->date);
3684   putFile32BitBE(file, tape->length);
3685
3686   putFile8Bit(file, store_participating_players);
3687
3688   /* unused bytes not at the end here for 4-byte alignment of engine_version */
3689   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3690
3691   putFileVersion(file, tape->engine_version);
3692 }
3693
3694 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3695 {
3696   int level_identifier_size = strlen(tape->level_identifier) + 1;
3697   int i;
3698
3699   putFile16BitBE(file, level_identifier_size);
3700
3701   for (i = 0; i < level_identifier_size; i++)
3702     putFile8Bit(file, tape->level_identifier[i]);
3703
3704   putFile16BitBE(file, tape->level_nr);
3705 }
3706
3707 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3708 {
3709   int i, j;
3710
3711   for (i = 0; i < tape->length; i++)
3712   {
3713     for (j = 0; j < MAX_PLAYERS; j++)
3714       if (tape->player_participates[j])
3715         putFile8Bit(file, tape->pos[i].action[j]);
3716
3717     putFile8Bit(file, tape->pos[i].delay);
3718   }
3719 }
3720
3721 void SaveTape(int nr)
3722 {
3723   char *filename = getTapeFilename(nr);
3724   FILE *file;
3725   boolean new_tape = TRUE;
3726   int num_participating_players = 0;
3727   int info_chunk_size;
3728   int body_chunk_size;
3729   int i;
3730
3731   InitTapeDirectory(leveldir_current->subdir);
3732
3733   /* if a tape still exists, ask to overwrite it */
3734   if (access(filename, F_OK) == 0)
3735   {
3736     new_tape = FALSE;
3737     if (!Request("Replace old tape ?", REQ_ASK))
3738       return;
3739   }
3740
3741   if (!(file = fopen(filename, MODE_WRITE)))
3742   {
3743     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3744     return;
3745   }
3746
3747   tape.file_version = FILE_VERSION_ACTUAL;
3748   tape.game_version = GAME_VERSION_ACTUAL;
3749
3750   /* count number of participating players  */
3751   for (i = 0; i < MAX_PLAYERS; i++)
3752     if (tape.player_participates[i])
3753       num_participating_players++;
3754
3755   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3756   body_chunk_size = (num_participating_players + 1) * tape.length;
3757
3758   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3759   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3760
3761   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3762   SaveTape_VERS(file, &tape);
3763
3764   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3765   SaveTape_HEAD(file, &tape);
3766
3767   putFileChunkBE(file, "INFO", info_chunk_size);
3768   SaveTape_INFO(file, &tape);
3769
3770   putFileChunkBE(file, "BODY", body_chunk_size);
3771   SaveTape_BODY(file, &tape);
3772
3773   fclose(file);
3774
3775   SetFilePermissions(filename, PERMS_PRIVATE);
3776
3777   tape.changed = FALSE;
3778
3779   if (new_tape)
3780     Request("tape saved !", REQ_CONFIRM);
3781 }
3782
3783 void DumpTape(struct TapeInfo *tape)
3784 {
3785   int i, j;
3786
3787 #if 1
3788   if (tape->no_valid_file)
3789   {
3790     Error(ERR_WARN, "cannot dump -- no valid tape file found");
3791
3792     return;
3793   }
3794 #else
3795   if (TAPE_IS_EMPTY(*tape))
3796   {
3797     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3798
3799     return;
3800   }
3801 #endif
3802
3803   printf_line("-", 79);
3804   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3805          tape->level_nr, tape->file_version, tape->game_version);
3806   printf("                  (effective engine version %08d)\n",
3807          tape->engine_version);
3808   printf("Level series identifier: '%s'\n", tape->level_identifier);
3809   printf_line("-", 79);
3810
3811   for (i = 0; i < tape->length; i++)
3812   {
3813     if (i >= MAX_TAPELEN)
3814       break;
3815
3816     printf("%03d: ", i);
3817
3818     for (j = 0; j < MAX_PLAYERS; j++)
3819     {
3820       if (tape->player_participates[j])
3821       {
3822         int action = tape->pos[i].action[j];
3823
3824         printf("%d:%02x ", j, action);
3825         printf("[%c%c%c%c|%c%c] - ",
3826                (action & JOY_LEFT ? '<' : ' '),
3827                (action & JOY_RIGHT ? '>' : ' '),
3828                (action & JOY_UP ? '^' : ' '),
3829                (action & JOY_DOWN ? 'v' : ' '),
3830                (action & JOY_BUTTON_1 ? '1' : ' '),
3831                (action & JOY_BUTTON_2 ? '2' : ' '));
3832       }
3833     }
3834
3835     printf("(%03d)\n", tape->pos[i].delay);
3836   }
3837
3838   printf_line("-", 79);
3839 }
3840
3841
3842 /* ========================================================================= */
3843 /* score file functions                                                      */
3844 /* ========================================================================= */
3845
3846 void LoadScore(int nr)
3847 {
3848   int i;
3849   char *filename = getScoreFilename(nr);
3850   char cookie[MAX_LINE_LEN];
3851   char line[MAX_LINE_LEN];
3852   char *line_ptr;
3853   FILE *file;
3854
3855   /* always start with reliable default values */
3856   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3857   {
3858     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3859     highscore[i].Score = 0;
3860   }
3861
3862   if (!(file = fopen(filename, MODE_READ)))
3863     return;
3864
3865   /* check file identifier */
3866   fgets(cookie, MAX_LINE_LEN, file);
3867   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3868     cookie[strlen(cookie) - 1] = '\0';
3869
3870   if (!checkCookieString(cookie, SCORE_COOKIE))
3871   {
3872     Error(ERR_WARN, "unknown format of score file '%s'", filename);
3873     fclose(file);
3874     return;
3875   }
3876
3877   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3878   {
3879     fscanf(file, "%d", &highscore[i].Score);
3880     fgets(line, MAX_LINE_LEN, file);
3881
3882     if (line[strlen(line) - 1] == '\n')
3883       line[strlen(line) - 1] = '\0';
3884
3885     for (line_ptr = line; *line_ptr; line_ptr++)
3886     {
3887       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3888       {
3889         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3890         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3891         break;
3892       }
3893     }
3894   }
3895
3896   fclose(file);
3897 }
3898
3899 void SaveScore(int nr)
3900 {
3901   int i;
3902   char *filename = getScoreFilename(nr);
3903   FILE *file;
3904
3905   InitScoreDirectory(leveldir_current->subdir);
3906
3907   if (!(file = fopen(filename, MODE_WRITE)))
3908   {
3909     Error(ERR_WARN, "cannot save score for level %d", nr);
3910     return;
3911   }
3912
3913   fprintf(file, "%s\n\n", SCORE_COOKIE);
3914
3915   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3916     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3917
3918   fclose(file);
3919
3920   SetFilePermissions(filename, PERMS_PUBLIC);
3921 }
3922
3923
3924 /* ========================================================================= */
3925 /* setup file functions                                                      */
3926 /* ========================================================================= */
3927
3928 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
3929
3930 /* global setup */
3931 #define SETUP_TOKEN_PLAYER_NAME                 0
3932 #define SETUP_TOKEN_SOUND                       1
3933 #define SETUP_TOKEN_SOUND_LOOPS                 2
3934 #define SETUP_TOKEN_SOUND_MUSIC                 3
3935 #define SETUP_TOKEN_SOUND_SIMPLE                4
3936 #define SETUP_TOKEN_TOONS                       5
3937 #define SETUP_TOKEN_SCROLL_DELAY                6
3938 #define SETUP_TOKEN_SOFT_SCROLLING              7
3939 #define SETUP_TOKEN_FADING                      8
3940 #define SETUP_TOKEN_AUTORECORD                  9
3941 #define SETUP_TOKEN_QUICK_DOORS                 10
3942 #define SETUP_TOKEN_TEAM_MODE                   11
3943 #define SETUP_TOKEN_HANDICAP                    12
3944 #define SETUP_TOKEN_TIME_LIMIT                  13
3945 #define SETUP_TOKEN_FULLSCREEN                  14
3946 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
3947 #define SETUP_TOKEN_GRAPHICS_SET                16
3948 #define SETUP_TOKEN_SOUNDS_SET                  17
3949 #define SETUP_TOKEN_MUSIC_SET                   18
3950 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
3951 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
3952 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
3953
3954 #define NUM_GLOBAL_SETUP_TOKENS                 22
3955
3956 /* editor setup */
3957 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
3958 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
3959 #define SETUP_TOKEN_EDITOR_EL_MORE              2
3960 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
3961 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
3962 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
3963 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
3964 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
3965 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
3966 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
3967 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
3968 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
3969
3970 #define NUM_EDITOR_SETUP_TOKENS                 12
3971
3972 /* shortcut setup */
3973 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
3974 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
3975 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
3976
3977 #define NUM_SHORTCUT_SETUP_TOKENS               3
3978
3979 /* player setup */
3980 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
3981 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
3982 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
3983 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
3984 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
3985 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
3986 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
3987 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
3988 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
3989 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
3990 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
3991 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
3992 #define SETUP_TOKEN_PLAYER_KEY_UP               12
3993 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
3994 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
3995 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
3996
3997 #define NUM_PLAYER_SETUP_TOKENS                 16
3998
3999 /* system setup */
4000 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
4001 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
4002
4003 #define NUM_SYSTEM_SETUP_TOKENS                 2
4004
4005 /* options setup */
4006 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
4007
4008 #define NUM_OPTIONS_SETUP_TOKENS                1
4009
4010
4011 static struct SetupInfo si;
4012 static struct SetupEditorInfo sei;
4013 static struct SetupShortcutInfo ssi;
4014 static struct SetupInputInfo sii;
4015 static struct SetupSystemInfo syi;
4016 static struct OptionInfo soi;
4017
4018 static struct TokenInfo global_setup_tokens[] =
4019 {
4020   { TYPE_STRING, &si.player_name,       "player_name"                   },
4021   { TYPE_SWITCH, &si.sound,             "sound"                         },
4022   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
4023   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
4024   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
4025   { TYPE_SWITCH, &si.toons,             "toons"                         },
4026   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
4027   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
4028   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
4029   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
4030   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
4031   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
4032   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
4033   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
4034   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
4035   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
4036   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
4037   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
4038   { TYPE_STRING, &si.music_set,         "music_set"                     },
4039   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4040   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
4041   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
4042 };
4043
4044 static struct TokenInfo editor_setup_tokens[] =
4045 {
4046   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
4047   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
4048   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
4049   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
4050   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
4051   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
4052   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
4053   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
4054   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
4055   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
4056   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
4057   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
4058 };
4059
4060 static struct TokenInfo shortcut_setup_tokens[] =
4061 {
4062   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
4063   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
4064   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
4065 };
4066
4067 static struct TokenInfo player_setup_tokens[] =
4068 {
4069   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
4070   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
4071   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
4072   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
4073   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
4074   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
4075   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
4076   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
4077   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
4078   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
4079   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
4080   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
4081   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
4082   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
4083   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
4084   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
4085 };
4086
4087 static struct TokenInfo system_setup_tokens[] =
4088 {
4089   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
4090   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4091 };
4092
4093 static struct TokenInfo options_setup_tokens[] =
4094 {
4095   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
4096 };
4097
4098 static char *get_corrected_login_name(char *login_name)
4099 {
4100   /* needed because player name must be a fixed length string */
4101   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4102
4103   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4104   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4105
4106   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
4107     if (strchr(login_name_new, ' '))
4108       *strchr(login_name_new, ' ') = '\0';
4109
4110   return login_name_new;
4111 }
4112
4113 static void setSetupInfoToDefaults(struct SetupInfo *si)
4114 {
4115   int i;
4116
4117   si->player_name = get_corrected_login_name(getLoginName());
4118
4119   si->sound = TRUE;
4120   si->sound_loops = TRUE;
4121   si->sound_music = TRUE;
4122   si->sound_simple = TRUE;
4123   si->toons = TRUE;
4124   si->double_buffering = TRUE;
4125   si->direct_draw = !si->double_buffering;
4126   si->scroll_delay = TRUE;
4127   si->soft_scrolling = TRUE;
4128   si->fading = FALSE;
4129   si->autorecord = TRUE;
4130   si->quick_doors = FALSE;
4131   si->team_mode = FALSE;
4132   si->handicap = TRUE;
4133   si->time_limit = TRUE;
4134   si->fullscreen = FALSE;
4135   si->ask_on_escape = TRUE;
4136
4137   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4138   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4139   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4140   si->override_level_graphics = FALSE;
4141   si->override_level_sounds = FALSE;
4142   si->override_level_music = FALSE;
4143
4144   si->editor.el_boulderdash = TRUE;
4145   si->editor.el_emerald_mine = TRUE;
4146   si->editor.el_more = TRUE;
4147   si->editor.el_sokoban = TRUE;
4148   si->editor.el_supaplex = TRUE;
4149   si->editor.el_diamond_caves = TRUE;
4150   si->editor.el_dx_boulderdash = TRUE;
4151   si->editor.el_chars = TRUE;
4152   si->editor.el_custom = TRUE;
4153   si->editor.el_custom_more = FALSE;
4154
4155   si->editor.el_headlines = TRUE;
4156   si->editor.el_user_defined = FALSE;
4157
4158   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4159   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4160   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4161
4162   for (i = 0; i < MAX_PLAYERS; i++)
4163   {
4164     si->input[i].use_joystick = FALSE;
4165     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4166     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
4167     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4168     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
4169     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
4170     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4171     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
4172     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
4173     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
4174     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
4175     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4176     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
4177     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
4178     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
4179     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
4180   }
4181
4182   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4183   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4184
4185   si->options.verbose = FALSE;
4186 }
4187
4188 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4189 {
4190   int i, pnr;
4191
4192   if (!setup_file_hash)
4193     return;
4194
4195   /* global setup */
4196   si = setup;
4197   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4198     setSetupInfo(global_setup_tokens, i,
4199                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4200   setup = si;
4201
4202   /* editor setup */
4203   sei = setup.editor;
4204   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4205     setSetupInfo(editor_setup_tokens, i,
4206                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4207   setup.editor = sei;
4208
4209   /* shortcut setup */
4210   ssi = setup.shortcut;
4211   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4212     setSetupInfo(shortcut_setup_tokens, i,
4213                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4214   setup.shortcut = ssi;
4215
4216   /* player setup */
4217   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4218   {
4219     char prefix[30];
4220
4221     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4222
4223     sii = setup.input[pnr];
4224     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4225     {
4226       char full_token[100];
4227
4228       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4229       setSetupInfo(player_setup_tokens, i,
4230                    getHashEntry(setup_file_hash, full_token));
4231     }
4232     setup.input[pnr] = sii;
4233   }
4234
4235   /* system setup */
4236   syi = setup.system;
4237   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4238     setSetupInfo(system_setup_tokens, i,
4239                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4240   setup.system = syi;
4241
4242   /* options setup */
4243   soi = setup.options;
4244   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4245     setSetupInfo(options_setup_tokens, i,
4246                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4247   setup.options = soi;
4248 }
4249
4250 void LoadSetup()
4251 {
4252   char *filename = getSetupFilename();
4253   SetupFileHash *setup_file_hash = NULL;
4254
4255   /* always start with reliable default values */
4256   setSetupInfoToDefaults(&setup);
4257
4258   setup_file_hash = loadSetupFileHash(filename);
4259
4260   if (setup_file_hash)
4261   {
4262     char *player_name_new;
4263
4264     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4265     decodeSetupFileHash(setup_file_hash);
4266
4267     setup.direct_draw = !setup.double_buffering;
4268
4269     freeSetupFileHash(setup_file_hash);
4270
4271     /* needed to work around problems with fixed length strings */
4272     player_name_new = get_corrected_login_name(setup.player_name);
4273     free(setup.player_name);
4274     setup.player_name = player_name_new;
4275   }
4276   else
4277     Error(ERR_WARN, "using default setup values");
4278 }
4279
4280 void SaveSetup()
4281 {
4282   char *filename = getSetupFilename();
4283   FILE *file;
4284   int i, pnr;
4285
4286   InitUserDataDirectory();
4287
4288   if (!(file = fopen(filename, MODE_WRITE)))
4289   {
4290     Error(ERR_WARN, "cannot write setup file '%s'", filename);
4291     return;
4292   }
4293
4294   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4295                                                getCookie("SETUP")));
4296   fprintf(file, "\n");
4297
4298   /* global setup */
4299   si = setup;
4300   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4301   {
4302     /* just to make things nicer :) */
4303     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4304         i == SETUP_TOKEN_GRAPHICS_SET)
4305       fprintf(file, "\n");
4306
4307     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4308   }
4309
4310   /* editor setup */
4311   sei = setup.editor;
4312   fprintf(file, "\n");
4313   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4314     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4315
4316   /* shortcut setup */
4317   ssi = setup.shortcut;
4318   fprintf(file, "\n");
4319   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4320     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4321
4322   /* player setup */
4323   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4324   {
4325     char prefix[30];
4326
4327     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4328     fprintf(file, "\n");
4329
4330     sii = setup.input[pnr];
4331     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4332       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4333   }
4334
4335   /* system setup */
4336   syi = setup.system;
4337   fprintf(file, "\n");
4338   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4339     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4340
4341   /* options setup */
4342   soi = setup.options;
4343   fprintf(file, "\n");
4344   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4345     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4346
4347   fclose(file);
4348
4349   SetFilePermissions(filename, PERMS_PRIVATE);
4350 }
4351
4352 void LoadCustomElementDescriptions()
4353 {
4354   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4355   SetupFileHash *setup_file_hash;
4356   int i;
4357
4358   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4359   {
4360     if (element_info[i].custom_description != NULL)
4361     {
4362       free(element_info[i].custom_description);
4363       element_info[i].custom_description = NULL;
4364     }
4365   }
4366
4367   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4368     return;
4369
4370   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4371   {
4372     char *token = getStringCat2(element_info[i].token_name, ".name");
4373     char *value = getHashEntry(setup_file_hash, token);
4374
4375     if (value != NULL)
4376       element_info[i].custom_description = getStringCopy(value);
4377
4378     free(token);
4379   }
4380
4381   freeSetupFileHash(setup_file_hash);
4382 }
4383
4384 void LoadSpecialMenuDesignSettings()
4385 {
4386   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4387   SetupFileHash *setup_file_hash;
4388   int i, j;
4389
4390   /* always start with reliable default values from default config */
4391   for (i = 0; image_config_vars[i].token != NULL; i++)
4392     for (j = 0; image_config[j].token != NULL; j++)
4393       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4394         *image_config_vars[i].value =
4395           get_auto_parameter_value(image_config_vars[i].token,
4396                                    image_config[j].value);
4397
4398   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4399     return;
4400
4401   /* special case: initialize with default values that may be overwritten */
4402   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4403   {
4404     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4405     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4406     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4407
4408     if (value_x != NULL)
4409       menu.draw_xoffset[i] = get_integer_from_string(value_x);
4410     if (value_y != NULL)
4411       menu.draw_yoffset[i] = get_integer_from_string(value_y);
4412     if (list_size != NULL)
4413       menu.list_size[i] = get_integer_from_string(list_size);
4414   }
4415
4416   /* read (and overwrite with) values that may be specified in config file */
4417   for (i = 0; image_config_vars[i].token != NULL; i++)
4418   {
4419     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4420
4421     if (value != NULL)
4422       *image_config_vars[i].value =
4423         get_auto_parameter_value(image_config_vars[i].token, value);
4424   }
4425
4426   freeSetupFileHash(setup_file_hash);
4427 }
4428
4429 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4430 {
4431   char *filename = getEditorSetupFilename();
4432   SetupFileList *setup_file_list, *list;
4433   SetupFileHash *element_hash;
4434   int num_unknown_tokens = 0;
4435   int i;
4436
4437   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4438     return;
4439
4440   element_hash = newSetupFileHash();
4441
4442   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4443     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4444
4445   /* determined size may be larger than needed (due to unknown elements) */
4446   *num_elements = 0;
4447   for (list = setup_file_list; list != NULL; list = list->next)
4448     (*num_elements)++;
4449
4450   /* add space for up to 3 more elements for padding that may be needed */
4451   *num_elements += 3;
4452
4453   *elements = checked_malloc(*num_elements * sizeof(int));
4454
4455   *num_elements = 0;
4456   for (list = setup_file_list; list != NULL; list = list->next)
4457   {
4458     char *value = getHashEntry(element_hash, list->token);
4459
4460     if (value)
4461     {
4462       (*elements)[(*num_elements)++] = atoi(value);
4463     }
4464     else
4465     {
4466       if (num_unknown_tokens == 0)
4467       {
4468         Error(ERR_RETURN_LINE, "-");
4469         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4470         Error(ERR_RETURN, "- config file: '%s'", filename);
4471
4472         num_unknown_tokens++;
4473       }
4474
4475       Error(ERR_RETURN, "- token: '%s'", list->token);
4476     }
4477   }
4478
4479   if (num_unknown_tokens > 0)
4480     Error(ERR_RETURN_LINE, "-");
4481
4482   while (*num_elements % 4)     /* pad with empty elements, if needed */
4483     (*elements)[(*num_elements)++] = EL_EMPTY;
4484
4485   freeSetupFileList(setup_file_list);
4486   freeSetupFileHash(element_hash);
4487
4488 #if 0
4489   /* TEST-ONLY */
4490   for (i = 0; i < *num_elements; i++)
4491     printf("editor: element '%s' [%d]\n",
4492            element_info[(*elements)[i]].token_name, (*elements)[i]);
4493 #endif
4494 }
4495
4496 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4497                                                      boolean is_sound)
4498 {
4499   SetupFileHash *setup_file_hash = NULL;
4500   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4501   char *filename_music, *filename_prefix, *filename_info;
4502   struct
4503   {
4504     char *token;
4505     char **value_ptr;
4506   }
4507   token_to_value_ptr[] =
4508   {
4509     { "title_header",   &tmp_music_file_info.title_header       },
4510     { "artist_header",  &tmp_music_file_info.artist_header      },
4511     { "album_header",   &tmp_music_file_info.album_header       },
4512     { "year_header",    &tmp_music_file_info.year_header        },
4513
4514     { "title",          &tmp_music_file_info.title              },
4515     { "artist",         &tmp_music_file_info.artist             },
4516     { "album",          &tmp_music_file_info.album              },
4517     { "year",           &tmp_music_file_info.year               },
4518
4519     { NULL,             NULL                                    },
4520   };
4521   int i;
4522
4523   filename_music = (is_sound ? getCustomSoundFilename(basename) :
4524                     getCustomMusicFilename(basename));
4525
4526   if (filename_music == NULL)
4527     return NULL;
4528
4529   /* ---------- try to replace file extension ---------- */
4530
4531   filename_prefix = getStringCopy(filename_music);
4532   if (strrchr(filename_prefix, '.') != NULL)
4533     *strrchr(filename_prefix, '.') = '\0';
4534   filename_info = getStringCat2(filename_prefix, ".txt");
4535
4536 #if 0
4537   printf("trying to load file '%s'...\n", filename_info);
4538 #endif
4539
4540   if (fileExists(filename_info))
4541     setup_file_hash = loadSetupFileHash(filename_info);
4542
4543   free(filename_prefix);
4544   free(filename_info);
4545
4546   if (setup_file_hash == NULL)
4547   {
4548     /* ---------- try to add file extension ---------- */
4549
4550     filename_prefix = getStringCopy(filename_music);
4551     filename_info = getStringCat2(filename_prefix, ".txt");
4552
4553 #if 0
4554     printf("trying to load file '%s'...\n", filename_info);
4555 #endif
4556
4557     if (fileExists(filename_info))
4558       setup_file_hash = loadSetupFileHash(filename_info);
4559
4560     free(filename_prefix);
4561     free(filename_info);
4562   }
4563
4564   if (setup_file_hash == NULL)
4565     return NULL;
4566
4567   /* ---------- music file info found ---------- */
4568
4569   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4570
4571   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4572   {
4573     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4574
4575     *token_to_value_ptr[i].value_ptr =
4576       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4577   }
4578
4579   tmp_music_file_info.basename = getStringCopy(basename);
4580   tmp_music_file_info.music = music;
4581   tmp_music_file_info.is_sound = is_sound;
4582
4583   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4584   *new_music_file_info = tmp_music_file_info;
4585
4586   return new_music_file_info;
4587 }
4588
4589 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4590 {
4591   return get_music_file_info_ext(basename, music, FALSE);
4592 }
4593
4594 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4595 {
4596   return get_music_file_info_ext(basename, sound, TRUE);
4597 }
4598
4599 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4600                                      char *basename, boolean is_sound)
4601 {
4602   for (; list != NULL; list = list->next)
4603     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4604       return TRUE;
4605
4606   return FALSE;
4607 }
4608
4609 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4610 {
4611   return music_info_listed_ext(list, basename, FALSE);
4612 }
4613
4614 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4615 {
4616   return music_info_listed_ext(list, basename, TRUE);
4617 }
4618
4619 void LoadMusicInfo()
4620 {
4621   char *music_directory = getCustomMusicDirectory();
4622   int num_music = getMusicListSize();
4623   int num_music_noconf = 0;
4624   int num_sounds = getSoundListSize();
4625   DIR *dir;
4626   struct dirent *dir_entry;
4627   struct FileInfo *music, *sound;
4628   struct MusicFileInfo *next, **new;
4629   int i;
4630
4631   while (music_file_info != NULL)
4632   {
4633     next = music_file_info->next;
4634
4635     checked_free(music_file_info->basename);
4636
4637     checked_free(music_file_info->title_header);
4638     checked_free(music_file_info->artist_header);
4639     checked_free(music_file_info->album_header);
4640     checked_free(music_file_info->year_header);
4641
4642     checked_free(music_file_info->title);
4643     checked_free(music_file_info->artist);
4644     checked_free(music_file_info->album);
4645     checked_free(music_file_info->year);
4646
4647     free(music_file_info);
4648
4649     music_file_info = next;
4650   }
4651
4652   new = &music_file_info;
4653
4654 #if 0
4655   printf("::: num_music == %d\n", num_music);
4656 #endif
4657
4658   for (i = 0; i < num_music; i++)
4659   {
4660     music = getMusicListEntry(i);
4661
4662 #if 0
4663     printf("::: %d [%08x]\n", i, music->filename);
4664 #endif
4665
4666     if (music->filename == NULL)
4667       continue;
4668
4669     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4670       continue;
4671
4672     /* a configured file may be not recognized as music */
4673     if (!FileIsMusic(music->filename))
4674       continue;
4675
4676 #if 0
4677     printf("::: -> '%s' (configured)\n", music->filename);
4678 #endif
4679
4680     if (!music_info_listed(music_file_info, music->filename))
4681     {
4682       *new = get_music_file_info(music->filename, i);
4683       if (*new != NULL)
4684         new = &(*new)->next;
4685     }
4686   }
4687
4688   if ((dir = opendir(music_directory)) == NULL)
4689   {
4690     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4691     return;
4692   }
4693
4694   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
4695   {
4696     char *basename = dir_entry->d_name;
4697     boolean music_already_used = FALSE;
4698     int i;
4699
4700     /* skip all music files that are configured in music config file */
4701     for (i = 0; i < num_music; i++)
4702     {
4703       music = getMusicListEntry(i);
4704
4705       if (music->filename == NULL)
4706         continue;
4707
4708       if (strcmp(basename, music->filename) == 0)
4709       {
4710         music_already_used = TRUE;
4711         break;
4712       }
4713     }
4714
4715     if (music_already_used)
4716       continue;
4717
4718     if (!FileIsMusic(basename))
4719       continue;
4720
4721 #if 0
4722     printf("::: -> '%s' (found in directory)\n", basename);
4723 #endif
4724
4725     if (!music_info_listed(music_file_info, basename))
4726     {
4727       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4728       if (*new != NULL)
4729         new = &(*new)->next;
4730     }
4731
4732     num_music_noconf++;
4733   }
4734
4735   closedir(dir);
4736
4737   for (i = 0; i < num_sounds; i++)
4738   {
4739     sound = getSoundListEntry(i);
4740
4741     if (sound->filename == NULL)
4742       continue;
4743
4744     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4745       continue;
4746
4747     /* a configured file may be not recognized as sound */
4748     if (!FileIsSound(sound->filename))
4749       continue;
4750
4751 #if 0
4752     printf("::: -> '%s' (configured)\n", sound->filename);
4753 #endif
4754
4755     if (!sound_info_listed(music_file_info, sound->filename))
4756     {
4757       *new = get_sound_file_info(sound->filename, i);
4758       if (*new != NULL)
4759         new = &(*new)->next;
4760     }
4761   }
4762
4763 #if 0
4764   /* TEST-ONLY */
4765   for (next = music_file_info; next != NULL; next = next->next)
4766     printf("::: title == '%s'\n", next->title);
4767 #endif
4768 }
4769
4770 void add_helpanim_entry(int element, int action, int direction, int delay,
4771                         int *num_list_entries)
4772 {
4773   struct HelpAnimInfo *new_list_entry;
4774   (*num_list_entries)++;
4775
4776   helpanim_info =
4777     checked_realloc(helpanim_info,
4778                     *num_list_entries * sizeof(struct HelpAnimInfo));
4779   new_list_entry = &helpanim_info[*num_list_entries - 1];
4780
4781   new_list_entry->element = element;
4782   new_list_entry->action = action;
4783   new_list_entry->direction = direction;
4784   new_list_entry->delay = delay;
4785 }
4786
4787 void print_unknown_token(char *filename, char *token, int token_nr)
4788 {
4789   if (token_nr == 0)
4790   {
4791     Error(ERR_RETURN_LINE, "-");
4792     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4793     Error(ERR_RETURN, "- config file: '%s'", filename);
4794   }
4795
4796   Error(ERR_RETURN, "- token: '%s'", token);
4797 }
4798
4799 void print_unknown_token_end(int token_nr)
4800 {
4801   if (token_nr > 0)
4802     Error(ERR_RETURN_LINE, "-");
4803 }
4804
4805 void LoadHelpAnimInfo()
4806 {
4807   char *filename = getHelpAnimFilename();
4808   SetupFileList *setup_file_list = NULL, *list;
4809   SetupFileHash *element_hash, *action_hash, *direction_hash;
4810   int num_list_entries = 0;
4811   int num_unknown_tokens = 0;
4812   int i;
4813
4814   if (fileExists(filename))
4815     setup_file_list = loadSetupFileList(filename);
4816
4817   if (setup_file_list == NULL)
4818   {
4819     /* use reliable default values from static configuration */
4820     SetupFileList *insert_ptr;
4821
4822     insert_ptr = setup_file_list =
4823       newSetupFileList(helpanim_config[0].token,
4824                        helpanim_config[0].value);
4825
4826     for (i = 1; helpanim_config[i].token; i++)
4827       insert_ptr = addListEntry(insert_ptr,
4828                                 helpanim_config[i].token,
4829                                 helpanim_config[i].value);
4830   }
4831
4832   element_hash   = newSetupFileHash();
4833   action_hash    = newSetupFileHash();
4834   direction_hash = newSetupFileHash();
4835
4836   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4837     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4838
4839   for (i = 0; i < NUM_ACTIONS; i++)
4840     setHashEntry(action_hash, element_action_info[i].suffix,
4841                  i_to_a(element_action_info[i].value));
4842
4843   /* do not store direction index (bit) here, but direction value! */
4844   for (i = 0; i < NUM_DIRECTIONS; i++)
4845     setHashEntry(direction_hash, element_direction_info[i].suffix,
4846                  i_to_a(1 << element_direction_info[i].value));
4847
4848   for (list = setup_file_list; list != NULL; list = list->next)
4849   {
4850     char *element_token, *action_token, *direction_token;
4851     char *element_value, *action_value, *direction_value;
4852     int delay = atoi(list->value);
4853
4854     if (strcmp(list->token, "end") == 0)
4855     {
4856       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4857
4858       continue;
4859     }
4860
4861     /* first try to break element into element/action/direction parts;
4862        if this does not work, also accept combined "element[.act][.dir]"
4863        elements (like "dynamite.active"), which are unique elements */
4864
4865     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
4866     {
4867       element_value = getHashEntry(element_hash, list->token);
4868       if (element_value != NULL)        /* element found */
4869         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4870                            &num_list_entries);
4871       else
4872       {
4873         /* no further suffixes found -- this is not an element */
4874         print_unknown_token(filename, list->token, num_unknown_tokens++);
4875       }
4876
4877       continue;
4878     }
4879
4880     /* token has format "<prefix>.<something>" */
4881
4882     action_token = strchr(list->token, '.');    /* suffix may be action ... */
4883     direction_token = action_token;             /* ... or direction */
4884
4885     element_token = getStringCopy(list->token);
4886     *strchr(element_token, '.') = '\0';
4887
4888     element_value = getHashEntry(element_hash, element_token);
4889
4890     if (element_value == NULL)          /* this is no element */
4891     {
4892       element_value = getHashEntry(element_hash, list->token);
4893       if (element_value != NULL)        /* combined element found */
4894         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4895                            &num_list_entries);
4896       else
4897         print_unknown_token(filename, list->token, num_unknown_tokens++);
4898
4899       free(element_token);
4900
4901       continue;
4902     }
4903
4904     action_value = getHashEntry(action_hash, action_token);
4905
4906     if (action_value != NULL)           /* action found */
4907     {
4908       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4909                     &num_list_entries);
4910
4911       free(element_token);
4912
4913       continue;
4914     }
4915
4916     direction_value = getHashEntry(direction_hash, direction_token);
4917
4918     if (direction_value != NULL)        /* direction found */
4919     {
4920       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4921                          &num_list_entries);
4922
4923       free(element_token);
4924
4925       continue;
4926     }
4927
4928     if (strchr(action_token + 1, '.') == NULL)
4929     {
4930       /* no further suffixes found -- this is not an action nor direction */
4931
4932       element_value = getHashEntry(element_hash, list->token);
4933       if (element_value != NULL)        /* combined element found */
4934         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4935                            &num_list_entries);
4936       else
4937         print_unknown_token(filename, list->token, num_unknown_tokens++);
4938
4939       free(element_token);
4940
4941       continue;
4942     }
4943
4944     /* token has format "<prefix>.<suffix>.<something>" */
4945
4946     direction_token = strchr(action_token + 1, '.');
4947
4948     action_token = getStringCopy(action_token);
4949     *strchr(action_token + 1, '.') = '\0';
4950
4951     action_value = getHashEntry(action_hash, action_token);
4952
4953     if (action_value == NULL)           /* this is no action */
4954     {
4955       element_value = getHashEntry(element_hash, list->token);
4956       if (element_value != NULL)        /* combined element found */
4957         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4958                            &num_list_entries);
4959       else
4960         print_unknown_token(filename, list->token, num_unknown_tokens++);
4961
4962       free(element_token);
4963       free(action_token);
4964
4965       continue;
4966     }
4967
4968     direction_value = getHashEntry(direction_hash, direction_token);
4969
4970     if (direction_value != NULL)        /* direction found */
4971     {
4972       add_helpanim_entry(atoi(element_value), atoi(action_value),
4973                          atoi(direction_value), delay, &num_list_entries);
4974
4975       free(element_token);
4976       free(action_token);
4977
4978       continue;
4979     }
4980
4981     /* this is no direction */
4982
4983     element_value = getHashEntry(element_hash, list->token);
4984     if (element_value != NULL)          /* combined element found */
4985       add_helpanim_entry(atoi(element_value), -1, -1, delay,
4986                          &num_list_entries);
4987     else
4988       print_unknown_token(filename, list->token, num_unknown_tokens++);
4989
4990     free(element_token);
4991     free(action_token);
4992   }
4993
4994   print_unknown_token_end(num_unknown_tokens);
4995
4996   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4997   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
4998
4999   freeSetupFileList(setup_file_list);
5000   freeSetupFileHash(element_hash);
5001   freeSetupFileHash(action_hash);
5002   freeSetupFileHash(direction_hash);
5003
5004 #if 0
5005   /* TEST ONLY */
5006   for (i = 0; i < num_list_entries; i++)
5007     printf("::: %d, %d, %d => %d\n",
5008            helpanim_info[i].element,
5009            helpanim_info[i].action,
5010            helpanim_info[i].direction,
5011            helpanim_info[i].delay);
5012 #endif
5013 }
5014
5015 void LoadHelpTextInfo()
5016 {
5017   char *filename = getHelpTextFilename();
5018   int i;
5019
5020   if (helptext_info != NULL)
5021   {
5022     freeSetupFileHash(helptext_info);
5023     helptext_info = NULL;
5024   }
5025
5026   if (fileExists(filename))
5027     helptext_info = loadSetupFileHash(filename);
5028
5029   if (helptext_info == NULL)
5030   {
5031     /* use reliable default values from static configuration */
5032     helptext_info = newSetupFileHash();
5033
5034     for (i = 0; helptext_config[i].token; i++)
5035       setHashEntry(helptext_info,
5036                    helptext_config[i].token,
5037                    helptext_config[i].value);
5038   }
5039
5040 #if 0
5041   /* TEST ONLY */
5042   BEGIN_HASH_ITERATION(helptext_info, itr)
5043   {
5044     printf("::: '%s' => '%s'\n",
5045            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5046   }
5047   END_HASH_ITERATION(hash, itr)
5048 #endif
5049 }
5050
5051
5052 /* ------------------------------------------------------------------------- *
5053  * convert levels
5054  * ------------------------------------------------------------------------- */
5055
5056 #define MAX_NUM_CONVERT_LEVELS          1000
5057
5058 void ConvertLevels()
5059 {
5060   static LevelDirTree *convert_leveldir = NULL;
5061   static int convert_level_nr = -1;
5062   static int num_levels_handled = 0;
5063   static int num_levels_converted = 0;
5064   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5065   int i;
5066
5067   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5068                                                global.convert_leveldir);
5069
5070   if (convert_leveldir == NULL)
5071     Error(ERR_EXIT, "no such level identifier: '%s'",
5072           global.convert_leveldir);
5073
5074   leveldir_current = convert_leveldir;
5075
5076   if (global.convert_level_nr != -1)
5077   {
5078     convert_leveldir->first_level = global.convert_level_nr;
5079     convert_leveldir->last_level  = global.convert_level_nr;
5080   }
5081
5082   convert_level_nr = convert_leveldir->first_level;
5083
5084   printf_line("=", 79);
5085   printf("Converting levels\n");
5086   printf_line("-", 79);
5087   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5088   printf("Level series name:       '%s'\n", convert_leveldir->name);
5089   printf("Level series author:     '%s'\n", convert_leveldir->author);
5090   printf("Number of levels:        %d\n",   convert_leveldir->levels);
5091   printf_line("=", 79);
5092   printf("\n");
5093
5094   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5095     levels_failed[i] = FALSE;
5096
5097   while (convert_level_nr <= convert_leveldir->last_level)
5098   {
5099     char *level_filename;
5100     boolean new_level;
5101
5102     level_nr = convert_level_nr++;
5103
5104     printf("Level %03d: ", level_nr);
5105
5106     LoadLevel(level_nr);
5107     if (level.no_valid_file)
5108     {
5109       printf("(no level)\n");
5110       continue;
5111     }
5112
5113     printf("converting level ... ");
5114
5115     level_filename = getDefaultLevelFilename(level_nr);
5116     new_level = !fileExists(level_filename);
5117
5118     if (new_level)
5119     {
5120       SaveLevel(level_nr);
5121
5122       num_levels_converted++;
5123
5124       printf("converted.\n");
5125     }
5126     else
5127     {
5128       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5129         levels_failed[level_nr] = TRUE;
5130
5131       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5132     }
5133
5134     num_levels_handled++;
5135   }
5136
5137   printf("\n");
5138   printf_line("=", 79);
5139   printf("Number of levels handled: %d\n", num_levels_handled);
5140   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5141          (num_levels_handled ?
5142           num_levels_converted * 100 / num_levels_handled : 0));
5143   printf_line("-", 79);
5144   printf("Summary (for automatic parsing by scripts):\n");
5145   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5146          convert_leveldir->identifier, num_levels_converted,
5147          num_levels_handled,
5148          (num_levels_handled ?
5149           num_levels_converted * 100 / num_levels_handled : 0));
5150
5151   if (num_levels_handled != num_levels_converted)
5152   {
5153     printf(", FAILED:");
5154     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5155       if (levels_failed[i])
5156         printf(" %03d", i);
5157   }
5158
5159   printf("\n");
5160   printf_line("=", 79);
5161
5162   CloseAllAndExit(0);
5163 }