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