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