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