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