34e3d3acebb9f2f3af0d013718574c7eae867e39
[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 #if 0
2162   /* set default push delay values (corrected since version 3.0.7-1) */
2163   if (level->game_version < VERSION_IDENT(3,0,7,1))
2164   {
2165     game.default_push_delay_fixed = 2;
2166     game.default_push_delay_random = 8;
2167   }
2168   else
2169   {
2170     game.default_push_delay_fixed = 8;
2171     game.default_push_delay_random = 8;
2172   }
2173
2174   /* set uninitialized push delay values of custom elements in older levels */
2175   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2176   {
2177     int element = EL_CUSTOM_START + i;
2178
2179     if (element_info[element].push_delay_fixed == -1)
2180       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2181     if (element_info[element].push_delay_random == -1)
2182       element_info[element].push_delay_random = game.default_push_delay_random;
2183   }
2184 #endif
2185
2186   /* initialize element properties for level editor etc. */
2187   InitElementPropertiesEngine(level->game_version);
2188 }
2189
2190 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2191 {
2192   int x, y;
2193
2194   /* map elements that have changed in newer versions */
2195   for (y = 0; y < level->fieldy; y++)
2196   {
2197     for (x = 0; x < level->fieldx; x++)
2198     {
2199       int element = level->field[x][y];
2200
2201       if (level->game_version <= VERSION_IDENT(2,2,0,0))
2202       {
2203         /* map game font elements */
2204         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2205                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2206                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2207                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2208       }
2209
2210       if (level->game_version < VERSION_IDENT(3,0,0,0))
2211       {
2212         /* map Supaplex gravity tube elements */
2213         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2214                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2215                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2216                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2217                    element);
2218       }
2219
2220       level->field[x][y] = element;
2221     }
2222   }
2223
2224   /* copy elements to runtime playfield array */
2225   for (x = 0; x < MAX_LEV_FIELDX; x++)
2226     for (y = 0; y < MAX_LEV_FIELDY; y++)
2227       Feld[x][y] = level->field[x][y];
2228
2229   /* initialize level size variables for faster access */
2230   lev_fieldx = level->fieldx;
2231   lev_fieldy = level->fieldy;
2232
2233   /* determine border element for this level */
2234   SetBorderElement();
2235 }
2236
2237 void LoadLevelTemplate(int nr)
2238 {
2239 #if 1
2240   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2241   char *filename = level_file_info->filename;
2242
2243   LoadLevelFromFileInfo(&level_template, level_file_info);
2244 #else
2245   char *filename = getDefaultLevelFilename(nr);
2246
2247   LoadLevelFromFilename_RND(&level_template, filename);
2248 #endif
2249
2250   LoadLevel_InitVersion(&level, filename);
2251   LoadLevel_InitElements(&level, filename);
2252
2253   ActivateLevelTemplate();
2254 }
2255
2256 void LoadLevel(int nr)
2257 {
2258 #if 1
2259   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2260   char *filename = level_file_info->filename;
2261
2262   LoadLevelFromFileInfo(&level, level_file_info);
2263 #else
2264   char *filename = getLevelFilename(nr);
2265
2266   LoadLevelFromFilename_RND(&level, filename);
2267 #endif
2268
2269   if (level.use_custom_template)
2270     LoadLevelTemplate(-1);
2271
2272 #if 1
2273   LoadLevel_InitVersion(&level, filename);
2274   LoadLevel_InitElements(&level, filename);
2275   LoadLevel_InitPlayfield(&level, filename);
2276 #else
2277   LoadLevel_InitLevel(&level, filename);
2278 #endif
2279 }
2280
2281 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2282 {
2283   putFileVersion(file, level->file_version);
2284   putFileVersion(file, level->game_version);
2285 }
2286
2287 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2288 {
2289   int i, x, y;
2290
2291   putFile8Bit(file, level->fieldx);
2292   putFile8Bit(file, level->fieldy);
2293
2294   putFile16BitBE(file, level->time);
2295   putFile16BitBE(file, level->gems_needed);
2296
2297   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2298     putFile8Bit(file, level->name[i]);
2299
2300   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2301     putFile8Bit(file, level->score[i]);
2302
2303   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2304     for (y = 0; y < 3; y++)
2305       for (x = 0; x < 3; x++)
2306         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2307                            level->yamyam_content[i][x][y]));
2308   putFile8Bit(file, level->amoeba_speed);
2309   putFile8Bit(file, level->time_magic_wall);
2310   putFile8Bit(file, level->time_wheel);
2311   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2312                      level->amoeba_content));
2313   putFile8Bit(file, (level->double_speed ? 1 : 0));
2314   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2315   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2316   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2317
2318   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2319
2320   putFile8Bit(file, (level->block_last_field ? 1 : 0));
2321   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2322
2323   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2324
2325   putFile16BitBE(file, level->can_move_into_acid);
2326
2327   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2328 }
2329
2330 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2331 {
2332   int i;
2333
2334   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2335     putFile8Bit(file, level->author[i]);
2336 }
2337
2338 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2339 {
2340   int x, y;
2341
2342   for (y = 0; y < level->fieldy; y++) 
2343     for (x = 0; x < level->fieldx; x++) 
2344       if (level->encoding_16bit_field)
2345         putFile16BitBE(file, level->field[x][y]);
2346       else
2347         putFile8Bit(file, level->field[x][y]);
2348 }
2349
2350 #if 0
2351 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2352 {
2353   int i, x, y;
2354
2355   putFile8Bit(file, EL_YAMYAM);
2356   putFile8Bit(file, level->num_yamyam_contents);
2357   putFile8Bit(file, 0);
2358   putFile8Bit(file, 0);
2359
2360   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2361     for (y = 0; y < 3; y++)
2362       for (x = 0; x < 3; x++)
2363         if (level->encoding_16bit_field)
2364           putFile16BitBE(file, level->yamyam_content[i][x][y]);
2365         else
2366           putFile8Bit(file, level->yamyam_content[i][x][y]);
2367 }
2368 #endif
2369
2370 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2371 {
2372   int i, x, y;
2373   int num_contents, content_xsize, content_ysize;
2374   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2375
2376   if (element == EL_YAMYAM)
2377   {
2378     num_contents = level->num_yamyam_contents;
2379     content_xsize = 3;
2380     content_ysize = 3;
2381
2382     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2383       for (y = 0; y < 3; y++)
2384         for (x = 0; x < 3; x++)
2385           content_array[i][x][y] = level->yamyam_content[i][x][y];
2386   }
2387   else if (element == EL_BD_AMOEBA)
2388   {
2389     num_contents = 1;
2390     content_xsize = 1;
2391     content_ysize = 1;
2392
2393     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2394       for (y = 0; y < 3; y++)
2395         for (x = 0; x < 3; x++)
2396           content_array[i][x][y] = EL_EMPTY;
2397     content_array[0][0][0] = level->amoeba_content;
2398   }
2399   else
2400   {
2401     /* chunk header already written -- write empty chunk data */
2402     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2403
2404     Error(ERR_WARN, "cannot save content for element '%d'", element);
2405     return;
2406   }
2407
2408   putFile16BitBE(file, element);
2409   putFile8Bit(file, num_contents);
2410   putFile8Bit(file, content_xsize);
2411   putFile8Bit(file, content_ysize);
2412
2413   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2414
2415   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2416     for (y = 0; y < 3; y++)
2417       for (x = 0; x < 3; x++)
2418         putFile16BitBE(file, content_array[i][x][y]);
2419 }
2420
2421 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2422 {
2423   int i;
2424   int envelope_nr = element - EL_ENVELOPE_1;
2425   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2426
2427   putFile16BitBE(file, element);
2428   putFile16BitBE(file, envelope_len);
2429   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2430   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2431
2432   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2433
2434   for (i = 0; i < envelope_len; i++)
2435     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2436 }
2437
2438 #if 0
2439 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2440                            int num_changed_custom_elements)
2441 {
2442   int i, check = 0;
2443
2444   putFile16BitBE(file, num_changed_custom_elements);
2445
2446   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2447   {
2448     int element = EL_CUSTOM_START + i;
2449
2450     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2451     {
2452       if (check < num_changed_custom_elements)
2453       {
2454         putFile16BitBE(file, element);
2455         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2456       }
2457
2458       check++;
2459     }
2460   }
2461
2462   if (check != num_changed_custom_elements)     /* should not happen */
2463     Error(ERR_WARN, "inconsistent number of custom element properties");
2464 }
2465 #endif
2466
2467 #if 0
2468 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2469                            int num_changed_custom_elements)
2470 {
2471   int i, check = 0;
2472
2473   putFile16BitBE(file, num_changed_custom_elements);
2474
2475   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2476   {
2477     int element = EL_CUSTOM_START + i;
2478
2479     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2480     {
2481       if (check < num_changed_custom_elements)
2482       {
2483         putFile16BitBE(file, element);
2484         putFile16BitBE(file, element_info[element].change->target_element);
2485       }
2486
2487       check++;
2488     }
2489   }
2490
2491   if (check != num_changed_custom_elements)     /* should not happen */
2492     Error(ERR_WARN, "inconsistent number of custom target elements");
2493 }
2494 #endif
2495
2496 #if 0
2497 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2498                            int num_changed_custom_elements)
2499 {
2500   int i, j, x, y, check = 0;
2501
2502   putFile16BitBE(file, num_changed_custom_elements);
2503
2504   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2505   {
2506     int element = EL_CUSTOM_START + i;
2507
2508     if (element_info[element].modified_settings)
2509     {
2510       if (check < num_changed_custom_elements)
2511       {
2512         putFile16BitBE(file, element);
2513
2514         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2515           putFile8Bit(file, element_info[element].description[j]);
2516
2517         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2518
2519         /* some free bytes for future properties and padding */
2520         WriteUnusedBytesToFile(file, 7);
2521
2522         putFile8Bit(file, element_info[element].use_gfx_element);
2523         putFile16BitBE(file, element_info[element].gfx_element);
2524
2525         putFile8Bit(file, element_info[element].collect_score);
2526         putFile8Bit(file, element_info[element].collect_count);
2527
2528         putFile16BitBE(file, element_info[element].push_delay_fixed);
2529         putFile16BitBE(file, element_info[element].push_delay_random);
2530         putFile16BitBE(file, element_info[element].move_delay_fixed);
2531         putFile16BitBE(file, element_info[element].move_delay_random);
2532
2533         putFile16BitBE(file, element_info[element].move_pattern);
2534         putFile8Bit(file, element_info[element].move_direction_initial);
2535         putFile8Bit(file, element_info[element].move_stepsize);
2536
2537         for (y = 0; y < 3; y++)
2538           for (x = 0; x < 3; x++)
2539             putFile16BitBE(file, element_info[element].content[x][y]);
2540
2541         putFile32BitBE(file, element_info[element].change->events);
2542
2543         putFile16BitBE(file, element_info[element].change->target_element);
2544
2545         putFile16BitBE(file, element_info[element].change->delay_fixed);
2546         putFile16BitBE(file, element_info[element].change->delay_random);
2547         putFile16BitBE(file, element_info[element].change->delay_frames);
2548
2549         putFile16BitBE(file, element_info[element].change->trigger_element);
2550
2551         putFile8Bit(file, element_info[element].change->explode);
2552         putFile8Bit(file, element_info[element].change->use_content);
2553         putFile8Bit(file, element_info[element].change->only_complete);
2554         putFile8Bit(file, element_info[element].change->use_random_change);
2555
2556         putFile8Bit(file, element_info[element].change->random);
2557         putFile8Bit(file, element_info[element].change->power);
2558
2559         for (y = 0; y < 3; y++)
2560           for (x = 0; x < 3; x++)
2561             putFile16BitBE(file, element_info[element].change->content[x][y]);
2562
2563         putFile8Bit(file, element_info[element].slippery_type);
2564
2565         /* some free bytes for future properties and padding */
2566         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2567       }
2568
2569       check++;
2570     }
2571   }
2572
2573   if (check != num_changed_custom_elements)     /* should not happen */
2574     Error(ERR_WARN, "inconsistent number of custom element properties");
2575 }
2576 #endif
2577
2578 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2579 {
2580   struct ElementInfo *ei = &element_info[element];
2581   int i, x, y;
2582
2583   putFile16BitBE(file, element);
2584
2585   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2586     putFile8Bit(file, ei->description[i]);
2587
2588   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2589   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
2590
2591   putFile8Bit(file, ei->num_change_pages);
2592
2593   /* some free bytes for future base property values and padding */
2594   WriteUnusedBytesToFile(file, 5);
2595
2596   /* write custom property values */
2597
2598   putFile8Bit(file, ei->use_gfx_element);
2599   putFile16BitBE(file, ei->gfx_element);
2600
2601   putFile8Bit(file, ei->collect_score);
2602   putFile8Bit(file, ei->collect_count);
2603
2604   putFile16BitBE(file, ei->push_delay_fixed);
2605   putFile16BitBE(file, ei->push_delay_random);
2606   putFile16BitBE(file, ei->move_delay_fixed);
2607   putFile16BitBE(file, ei->move_delay_random);
2608
2609   /* bits 0 - 15 of "move_pattern" ... */
2610   putFile16BitBE(file, ei->move_pattern & 0xffff);
2611   putFile8Bit(file, ei->move_direction_initial);
2612   putFile8Bit(file, ei->move_stepsize);
2613
2614   putFile8Bit(file, ei->slippery_type);
2615
2616   for (y = 0; y < 3; y++)
2617     for (x = 0; x < 3; x++)
2618       putFile16BitBE(file, ei->content[x][y]);
2619
2620   putFile16BitBE(file, ei->move_enter_element);
2621   putFile16BitBE(file, ei->move_leave_element);
2622   putFile8Bit(file, ei->move_leave_type);
2623
2624   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2625   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2626
2627   putFile8Bit(file, ei->access_direction);
2628
2629   putFile8Bit(file, ei->explosion_delay);
2630   putFile8Bit(file, ei->ignition_delay);
2631
2632   /* some free bytes for future custom property values and padding */
2633   WriteUnusedBytesToFile(file, 2);
2634
2635   /* write change property values */
2636
2637   for (i = 0; i < ei->num_change_pages; i++)
2638   {
2639     struct ElementChangeInfo *change = &ei->change_page[i];
2640
2641     putFile32BitBE(file, change->events);
2642
2643     putFile16BitBE(file, change->target_element);
2644
2645     putFile16BitBE(file, change->delay_fixed);
2646     putFile16BitBE(file, change->delay_random);
2647     putFile16BitBE(file, change->delay_frames);
2648
2649     putFile16BitBE(file, change->trigger_element);
2650
2651     putFile8Bit(file, change->explode);
2652     putFile8Bit(file, change->use_content);
2653     putFile8Bit(file, change->only_complete);
2654     putFile8Bit(file, change->use_random_change);
2655
2656     putFile8Bit(file, change->random);
2657     putFile8Bit(file, change->power);
2658
2659     for (y = 0; y < 3; y++)
2660       for (x = 0; x < 3; x++)
2661         putFile16BitBE(file, change->content[x][y]);
2662
2663     putFile8Bit(file, change->can_change);
2664
2665     putFile8Bit(file, change->sides);
2666
2667     /* some free bytes for future change property values and padding */
2668     WriteUnusedBytesToFile(file, 8);
2669   }
2670 }
2671
2672 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2673 {
2674   struct ElementInfo *ei = &element_info[element];
2675   struct ElementGroupInfo *group = ei->group;
2676   int i;
2677
2678   putFile16BitBE(file, element);
2679
2680   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2681     putFile8Bit(file, ei->description[i]);
2682
2683   putFile8Bit(file, group->num_elements);
2684
2685   putFile8Bit(file, ei->use_gfx_element);
2686   putFile16BitBE(file, ei->gfx_element);
2687
2688   putFile8Bit(file, group->choice_mode);
2689
2690   /* some free bytes for future values and padding */
2691   WriteUnusedBytesToFile(file, 3);
2692
2693   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2694     putFile16BitBE(file, group->element[i]);
2695 }
2696
2697 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2698 {
2699   int body_chunk_size;
2700   int i, x, y;
2701   FILE *file;
2702
2703   if (!(file = fopen(filename, MODE_WRITE)))
2704   {
2705     Error(ERR_WARN, "cannot save level file '%s'", filename);
2706     return;
2707   }
2708
2709   level->file_version = FILE_VERSION_ACTUAL;
2710   level->game_version = GAME_VERSION_ACTUAL;
2711
2712   /* check level field for 16-bit elements */
2713   level->encoding_16bit_field = FALSE;
2714   for (y = 0; y < level->fieldy; y++) 
2715     for (x = 0; x < level->fieldx; x++) 
2716       if (level->field[x][y] > 255)
2717         level->encoding_16bit_field = TRUE;
2718
2719   /* check yamyam content for 16-bit elements */
2720   level->encoding_16bit_yamyam = FALSE;
2721   for (i = 0; i < level->num_yamyam_contents; i++)
2722     for (y = 0; y < 3; y++)
2723       for (x = 0; x < 3; x++)
2724         if (level->yamyam_content[i][x][y] > 255)
2725           level->encoding_16bit_yamyam = TRUE;
2726
2727   /* check amoeba content for 16-bit elements */
2728   level->encoding_16bit_amoeba = FALSE;
2729   if (level->amoeba_content > 255)
2730     level->encoding_16bit_amoeba = TRUE;
2731
2732   /* calculate size of "BODY" chunk */
2733   body_chunk_size =
2734     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2735
2736   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2737   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2738
2739   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2740   SaveLevel_VERS(file, level);
2741
2742   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2743   SaveLevel_HEAD(file, level);
2744
2745   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2746   SaveLevel_AUTH(file, level);
2747
2748   putFileChunkBE(file, "BODY", body_chunk_size);
2749   SaveLevel_BODY(file, level);
2750
2751   if (level->encoding_16bit_yamyam ||
2752       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2753   {
2754     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2755     SaveLevel_CNT2(file, level, EL_YAMYAM);
2756   }
2757
2758   if (level->encoding_16bit_amoeba)
2759   {
2760     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2761     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2762   }
2763
2764   /* check for envelope content */
2765   for (i = 0; i < 4; i++)
2766   {
2767     if (strlen(level->envelope_text[i]) > 0)
2768     {
2769       int envelope_len = strlen(level->envelope_text[i]) + 1;
2770
2771       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2772       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2773     }
2774   }
2775
2776   /* check for non-default custom elements (unless using template level) */
2777   if (!level->use_custom_template)
2778   {
2779     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2780     {
2781       int element = EL_CUSTOM_START + i;
2782
2783       if (element_info[element].modified_settings)
2784       {
2785         int num_change_pages = element_info[element].num_change_pages;
2786
2787         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2788         SaveLevel_CUS4(file, level, element);
2789       }
2790     }
2791   }
2792
2793   /* check for non-default group elements (unless using template level) */
2794   if (!level->use_custom_template)
2795   {
2796     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2797     {
2798       int element = EL_GROUP_START + i;
2799
2800       if (element_info[element].modified_settings)
2801       {
2802         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2803         SaveLevel_GRP1(file, level, element);
2804       }
2805     }
2806   }
2807
2808   fclose(file);
2809
2810   SetFilePermissions(filename, PERMS_PRIVATE);
2811 }
2812
2813 void SaveLevel(int nr)
2814 {
2815   char *filename = getDefaultLevelFilename(nr);
2816
2817   SaveLevelFromFilename(&level, filename);
2818 }
2819
2820 void SaveLevelTemplate()
2821 {
2822   char *filename = getDefaultLevelFilename(-1);
2823
2824   SaveLevelFromFilename(&level, filename);
2825 }
2826
2827 void DumpLevel(struct LevelInfo *level)
2828 {
2829   printf_line("-", 79);
2830   printf("Level xxx (file version %08d, game version %08d)\n",
2831          level->file_version, level->game_version);
2832   printf_line("-", 79);
2833
2834   printf("Level author: '%s'\n", level->author);
2835   printf("Level title:  '%s'\n", level->name);
2836   printf("\n");
2837   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2838   printf("\n");
2839   printf("Level time:  %d seconds\n", level->time);
2840   printf("Gems needed: %d\n", level->gems_needed);
2841   printf("\n");
2842   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2843   printf("Time for wheel:      %d seconds\n", level->time_wheel);
2844   printf("Time for light:      %d seconds\n", level->time_light);
2845   printf("Time for timegate:   %d seconds\n", level->time_timegate);
2846   printf("\n");
2847   printf("Amoeba speed: %d\n", level->amoeba_speed);
2848   printf("\n");
2849   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
2850   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
2851   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
2852   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
2853   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2854   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
2855
2856   printf_line("-", 79);
2857 }
2858
2859
2860 /* ========================================================================= */
2861 /* tape file functions                                                       */
2862 /* ========================================================================= */
2863
2864 static void setTapeInfoToDefaults()
2865 {
2866   int i;
2867
2868   /* always start with reliable default values (empty tape) */
2869   TapeErase();
2870
2871   /* default values (also for pre-1.2 tapes) with only the first player */
2872   tape.player_participates[0] = TRUE;
2873   for (i = 1; i < MAX_PLAYERS; i++)
2874     tape.player_participates[i] = FALSE;
2875
2876   /* at least one (default: the first) player participates in every tape */
2877   tape.num_participating_players = 1;
2878
2879   tape.level_nr = level_nr;
2880   tape.counter = 0;
2881   tape.changed = FALSE;
2882
2883   tape.recording = FALSE;
2884   tape.playing = FALSE;
2885   tape.pausing = FALSE;
2886 }
2887
2888 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2889 {
2890   tape->file_version = getFileVersion(file);
2891   tape->game_version = getFileVersion(file);
2892
2893   return chunk_size;
2894 }
2895
2896 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2897 {
2898   int i;
2899
2900   tape->random_seed = getFile32BitBE(file);
2901   tape->date        = getFile32BitBE(file);
2902   tape->length      = getFile32BitBE(file);
2903
2904   /* read header fields that are new since version 1.2 */
2905   if (tape->file_version >= FILE_VERSION_1_2)
2906   {
2907     byte store_participating_players = getFile8Bit(file);
2908     int engine_version;
2909
2910     /* since version 1.2, tapes store which players participate in the tape */
2911     tape->num_participating_players = 0;
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913     {
2914       tape->player_participates[i] = FALSE;
2915
2916       if (store_participating_players & (1 << i))
2917       {
2918         tape->player_participates[i] = TRUE;
2919         tape->num_participating_players++;
2920       }
2921     }
2922
2923     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2924
2925     engine_version = getFileVersion(file);
2926     if (engine_version > 0)
2927       tape->engine_version = engine_version;
2928     else
2929       tape->engine_version = tape->game_version;
2930   }
2931
2932   return chunk_size;
2933 }
2934
2935 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2936 {
2937   int level_identifier_size;
2938   int i;
2939
2940   level_identifier_size = getFile16BitBE(file);
2941
2942   tape->level_identifier =
2943     checked_realloc(tape->level_identifier, level_identifier_size);
2944
2945   for (i = 0; i < level_identifier_size; i++)
2946     tape->level_identifier[i] = getFile8Bit(file);
2947
2948   tape->level_nr = getFile16BitBE(file);
2949
2950   chunk_size = 2 + level_identifier_size + 2;
2951
2952   return chunk_size;
2953 }
2954
2955 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2956 {
2957   int i, j;
2958   int chunk_size_expected =
2959     (tape->num_participating_players + 1) * tape->length;
2960
2961   if (chunk_size_expected != chunk_size)
2962   {
2963     ReadUnusedBytesFromFile(file, chunk_size);
2964     return chunk_size_expected;
2965   }
2966
2967   for (i = 0; i < tape->length; i++)
2968   {
2969     if (i >= MAX_TAPELEN)
2970       break;
2971
2972     for (j = 0; j < MAX_PLAYERS; j++)
2973     {
2974       tape->pos[i].action[j] = MV_NO_MOVING;
2975
2976       if (tape->player_participates[j])
2977         tape->pos[i].action[j] = getFile8Bit(file);
2978     }
2979
2980     tape->pos[i].delay = getFile8Bit(file);
2981
2982     if (tape->file_version == FILE_VERSION_1_0)
2983     {
2984       /* eliminate possible diagonal moves in old tapes */
2985       /* this is only for backward compatibility */
2986
2987       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2988       byte action = tape->pos[i].action[0];
2989       int k, num_moves = 0;
2990
2991       for (k = 0; k<4; k++)
2992       {
2993         if (action & joy_dir[k])
2994         {
2995           tape->pos[i + num_moves].action[0] = joy_dir[k];
2996           if (num_moves > 0)
2997             tape->pos[i + num_moves].delay = 0;
2998           num_moves++;
2999         }
3000       }
3001
3002       if (num_moves > 1)
3003       {
3004         num_moves--;
3005         i += num_moves;
3006         tape->length += num_moves;
3007       }
3008     }
3009     else if (tape->file_version < FILE_VERSION_2_0)
3010     {
3011       /* convert pre-2.0 tapes to new tape format */
3012
3013       if (tape->pos[i].delay > 1)
3014       {
3015         /* action part */
3016         tape->pos[i + 1] = tape->pos[i];
3017         tape->pos[i + 1].delay = 1;
3018
3019         /* delay part */
3020         for (j = 0; j < MAX_PLAYERS; j++)
3021           tape->pos[i].action[j] = MV_NO_MOVING;
3022         tape->pos[i].delay--;
3023
3024         i++;
3025         tape->length++;
3026       }
3027     }
3028
3029     if (feof(file))
3030       break;
3031   }
3032
3033   if (i != tape->length)
3034     chunk_size = (tape->num_participating_players + 1) * i;
3035
3036   return chunk_size;
3037 }
3038
3039 void LoadTapeFromFilename(char *filename)
3040 {
3041   char cookie[MAX_LINE_LEN];
3042   char chunk_name[CHUNK_ID_LEN + 1];
3043   FILE *file;
3044   int chunk_size;
3045
3046   /* always start with reliable default values */
3047   setTapeInfoToDefaults();
3048
3049   if (!(file = fopen(filename, MODE_READ)))
3050     return;
3051
3052   getFileChunkBE(file, chunk_name, NULL);
3053   if (strcmp(chunk_name, "RND1") == 0)
3054   {
3055     getFile32BitBE(file);               /* not used */
3056
3057     getFileChunkBE(file, chunk_name, NULL);
3058     if (strcmp(chunk_name, "TAPE") != 0)
3059     {
3060       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3061       fclose(file);
3062       return;
3063     }
3064   }
3065   else  /* check for pre-2.0 file format with cookie string */
3066   {
3067     strcpy(cookie, chunk_name);
3068     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3069     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3070       cookie[strlen(cookie) - 1] = '\0';
3071
3072     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3073     {
3074       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3075       fclose(file);
3076       return;
3077     }
3078
3079     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3080     {
3081       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3082       fclose(file);
3083       return;
3084     }
3085
3086     /* pre-2.0 tape files have no game version, so use file version here */
3087     tape.game_version = tape.file_version;
3088   }
3089
3090   if (tape.file_version < FILE_VERSION_1_2)
3091   {
3092     /* tape files from versions before 1.2.0 without chunk structure */
3093     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3094     LoadTape_BODY(file, 2 * tape.length,  &tape);
3095   }
3096   else
3097   {
3098     static struct
3099     {
3100       char *name;
3101       int size;
3102       int (*loader)(FILE *, int, struct TapeInfo *);
3103     }
3104     chunk_info[] =
3105     {
3106       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
3107       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
3108       { "INFO", -1,                     LoadTape_INFO },
3109       { "BODY", -1,                     LoadTape_BODY },
3110       {  NULL,  0,                      NULL }
3111     };
3112
3113     while (getFileChunkBE(file, chunk_name, &chunk_size))
3114     {
3115       int i = 0;
3116
3117       while (chunk_info[i].name != NULL &&
3118              strcmp(chunk_name, chunk_info[i].name) != 0)
3119         i++;
3120
3121       if (chunk_info[i].name == NULL)
3122       {
3123         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3124               chunk_name, filename);
3125         ReadUnusedBytesFromFile(file, chunk_size);
3126       }
3127       else if (chunk_info[i].size != -1 &&
3128                chunk_info[i].size != chunk_size)
3129       {
3130         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3131               chunk_size, chunk_name, filename);
3132         ReadUnusedBytesFromFile(file, chunk_size);
3133       }
3134       else
3135       {
3136         /* call function to load this tape chunk */
3137         int chunk_size_expected =
3138           (chunk_info[i].loader)(file, chunk_size, &tape);
3139
3140         /* the size of some chunks cannot be checked before reading other
3141            chunks first (like "HEAD" and "BODY") that contain some header
3142            information, so check them here */
3143         if (chunk_size_expected != chunk_size)
3144         {
3145           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3146                 chunk_size, chunk_name, filename);
3147         }
3148       }
3149     }
3150   }
3151
3152   fclose(file);
3153
3154   tape.length_seconds = GetTapeLength();
3155
3156 #if 0
3157   printf("::: tape game version: %d\n", tape.game_version);
3158   printf("::: tape engine version: %d\n", tape.engine_version);
3159 #endif
3160 }
3161
3162 void LoadTape(int nr)
3163 {
3164   char *filename = getTapeFilename(nr);
3165
3166   LoadTapeFromFilename(filename);
3167 }
3168
3169 void LoadSolutionTape(int nr)
3170 {
3171   char *filename = getSolutionTapeFilename(nr);
3172
3173   LoadTapeFromFilename(filename);
3174 }
3175
3176 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3177 {
3178   putFileVersion(file, tape->file_version);
3179   putFileVersion(file, tape->game_version);
3180 }
3181
3182 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3183 {
3184   int i;
3185   byte store_participating_players = 0;
3186
3187   /* set bits for participating players for compact storage */
3188   for (i = 0; i < MAX_PLAYERS; i++)
3189     if (tape->player_participates[i])
3190       store_participating_players |= (1 << i);
3191
3192   putFile32BitBE(file, tape->random_seed);
3193   putFile32BitBE(file, tape->date);
3194   putFile32BitBE(file, tape->length);
3195
3196   putFile8Bit(file, store_participating_players);
3197
3198   /* unused bytes not at the end here for 4-byte alignment of engine_version */
3199   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3200
3201   putFileVersion(file, tape->engine_version);
3202 }
3203
3204 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3205 {
3206   int level_identifier_size = strlen(tape->level_identifier) + 1;
3207   int i;
3208
3209   putFile16BitBE(file, level_identifier_size);
3210
3211   for (i = 0; i < level_identifier_size; i++)
3212     putFile8Bit(file, tape->level_identifier[i]);
3213
3214   putFile16BitBE(file, tape->level_nr);
3215 }
3216
3217 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3218 {
3219   int i, j;
3220
3221   for (i = 0; i < tape->length; i++)
3222   {
3223     for (j = 0; j < MAX_PLAYERS; j++)
3224       if (tape->player_participates[j])
3225         putFile8Bit(file, tape->pos[i].action[j]);
3226
3227     putFile8Bit(file, tape->pos[i].delay);
3228   }
3229 }
3230
3231 void SaveTape(int nr)
3232 {
3233   char *filename = getTapeFilename(nr);
3234   FILE *file;
3235   boolean new_tape = TRUE;
3236   int num_participating_players = 0;
3237   int info_chunk_size;
3238   int body_chunk_size;
3239   int i;
3240
3241   InitTapeDirectory(leveldir_current->subdir);
3242
3243   /* if a tape still exists, ask to overwrite it */
3244   if (access(filename, F_OK) == 0)
3245   {
3246     new_tape = FALSE;
3247     if (!Request("Replace old tape ?", REQ_ASK))
3248       return;
3249   }
3250
3251   if (!(file = fopen(filename, MODE_WRITE)))
3252   {
3253     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3254     return;
3255   }
3256
3257   tape.file_version = FILE_VERSION_ACTUAL;
3258   tape.game_version = GAME_VERSION_ACTUAL;
3259
3260   /* count number of participating players  */
3261   for (i = 0; i < MAX_PLAYERS; i++)
3262     if (tape.player_participates[i])
3263       num_participating_players++;
3264
3265   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3266   body_chunk_size = (num_participating_players + 1) * tape.length;
3267
3268   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3269   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3270
3271   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3272   SaveTape_VERS(file, &tape);
3273
3274   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3275   SaveTape_HEAD(file, &tape);
3276
3277   putFileChunkBE(file, "INFO", info_chunk_size);
3278   SaveTape_INFO(file, &tape);
3279
3280   putFileChunkBE(file, "BODY", body_chunk_size);
3281   SaveTape_BODY(file, &tape);
3282
3283   fclose(file);
3284
3285   SetFilePermissions(filename, PERMS_PRIVATE);
3286
3287   tape.changed = FALSE;
3288
3289   if (new_tape)
3290     Request("tape saved !", REQ_CONFIRM);
3291 }
3292
3293 void DumpTape(struct TapeInfo *tape)
3294 {
3295   int i, j;
3296
3297   if (TAPE_IS_EMPTY(*tape))
3298   {
3299     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3300     return;
3301   }
3302
3303   printf_line("-", 79);
3304   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3305          tape->level_nr, tape->file_version, tape->game_version);
3306   printf("Level series identifier: '%s'\n", tape->level_identifier);
3307   printf_line("-", 79);
3308
3309   for (i = 0; i < tape->length; i++)
3310   {
3311     if (i >= MAX_TAPELEN)
3312       break;
3313
3314     printf("%03d: ", i);
3315
3316     for (j = 0; j < MAX_PLAYERS; j++)
3317     {
3318       if (tape->player_participates[j])
3319       {
3320         int action = tape->pos[i].action[j];
3321
3322         printf("%d:%02x ", j, action);
3323         printf("[%c%c%c%c|%c%c] - ",
3324                (action & JOY_LEFT ? '<' : ' '),
3325                (action & JOY_RIGHT ? '>' : ' '),
3326                (action & JOY_UP ? '^' : ' '),
3327                (action & JOY_DOWN ? 'v' : ' '),
3328                (action & JOY_BUTTON_1 ? '1' : ' '),
3329                (action & JOY_BUTTON_2 ? '2' : ' '));
3330       }
3331     }
3332
3333     printf("(%03d)\n", tape->pos[i].delay);
3334   }
3335
3336   printf_line("-", 79);
3337 }
3338
3339
3340 /* ========================================================================= */
3341 /* score file functions                                                      */
3342 /* ========================================================================= */
3343
3344 void LoadScore(int nr)
3345 {
3346   int i;
3347   char *filename = getScoreFilename(nr);
3348   char cookie[MAX_LINE_LEN];
3349   char line[MAX_LINE_LEN];
3350   char *line_ptr;
3351   FILE *file;
3352
3353   /* always start with reliable default values */
3354   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3355   {
3356     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3357     highscore[i].Score = 0;
3358   }
3359
3360   if (!(file = fopen(filename, MODE_READ)))
3361     return;
3362
3363   /* check file identifier */
3364   fgets(cookie, MAX_LINE_LEN, file);
3365   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3366     cookie[strlen(cookie) - 1] = '\0';
3367
3368   if (!checkCookieString(cookie, SCORE_COOKIE))
3369   {
3370     Error(ERR_WARN, "unknown format of score file '%s'", filename);
3371     fclose(file);
3372     return;
3373   }
3374
3375   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3376   {
3377     fscanf(file, "%d", &highscore[i].Score);
3378     fgets(line, MAX_LINE_LEN, file);
3379
3380     if (line[strlen(line) - 1] == '\n')
3381       line[strlen(line) - 1] = '\0';
3382
3383     for (line_ptr = line; *line_ptr; line_ptr++)
3384     {
3385       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3386       {
3387         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3388         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3389         break;
3390       }
3391     }
3392   }
3393
3394   fclose(file);
3395 }
3396
3397 void SaveScore(int nr)
3398 {
3399   int i;
3400   char *filename = getScoreFilename(nr);
3401   FILE *file;
3402
3403   InitScoreDirectory(leveldir_current->subdir);
3404
3405   if (!(file = fopen(filename, MODE_WRITE)))
3406   {
3407     Error(ERR_WARN, "cannot save score for level %d", nr);
3408     return;
3409   }
3410
3411   fprintf(file, "%s\n\n", SCORE_COOKIE);
3412
3413   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3414     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3415
3416   fclose(file);
3417
3418   SetFilePermissions(filename, PERMS_PUBLIC);
3419 }
3420
3421
3422 /* ========================================================================= */
3423 /* setup file functions                                                      */
3424 /* ========================================================================= */
3425
3426 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
3427
3428 /* global setup */
3429 #define SETUP_TOKEN_PLAYER_NAME                 0
3430 #define SETUP_TOKEN_SOUND                       1
3431 #define SETUP_TOKEN_SOUND_LOOPS                 2
3432 #define SETUP_TOKEN_SOUND_MUSIC                 3
3433 #define SETUP_TOKEN_SOUND_SIMPLE                4
3434 #define SETUP_TOKEN_TOONS                       5
3435 #define SETUP_TOKEN_SCROLL_DELAY                6
3436 #define SETUP_TOKEN_SOFT_SCROLLING              7
3437 #define SETUP_TOKEN_FADING                      8
3438 #define SETUP_TOKEN_AUTORECORD                  9
3439 #define SETUP_TOKEN_QUICK_DOORS                 10
3440 #define SETUP_TOKEN_TEAM_MODE                   11
3441 #define SETUP_TOKEN_HANDICAP                    12
3442 #define SETUP_TOKEN_TIME_LIMIT                  13
3443 #define SETUP_TOKEN_FULLSCREEN                  14
3444 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
3445 #define SETUP_TOKEN_GRAPHICS_SET                16
3446 #define SETUP_TOKEN_SOUNDS_SET                  17
3447 #define SETUP_TOKEN_MUSIC_SET                   18
3448 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
3449 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
3450 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
3451
3452 #define NUM_GLOBAL_SETUP_TOKENS                 22
3453
3454 /* editor setup */
3455 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
3456 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
3457 #define SETUP_TOKEN_EDITOR_EL_MORE              2
3458 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
3459 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
3460 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
3461 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
3462 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
3463 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
3464 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
3465 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
3466 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
3467
3468 #define NUM_EDITOR_SETUP_TOKENS                 12
3469
3470 /* shortcut setup */
3471 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
3472 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
3473 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
3474
3475 #define NUM_SHORTCUT_SETUP_TOKENS               3
3476
3477 /* player setup */
3478 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
3479 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
3480 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
3481 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
3482 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
3483 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
3484 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
3485 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
3486 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
3487 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
3488 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
3489 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
3490 #define SETUP_TOKEN_PLAYER_KEY_UP               12
3491 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
3492 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
3493 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
3494
3495 #define NUM_PLAYER_SETUP_TOKENS                 16
3496
3497 /* system setup */
3498 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
3499 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
3500
3501 #define NUM_SYSTEM_SETUP_TOKENS                 2
3502
3503 /* options setup */
3504 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
3505
3506 #define NUM_OPTIONS_SETUP_TOKENS                1
3507
3508
3509 static struct SetupInfo si;
3510 static struct SetupEditorInfo sei;
3511 static struct SetupShortcutInfo ssi;
3512 static struct SetupInputInfo sii;
3513 static struct SetupSystemInfo syi;
3514 static struct OptionInfo soi;
3515
3516 static struct TokenInfo global_setup_tokens[] =
3517 {
3518   { TYPE_STRING, &si.player_name,       "player_name"                   },
3519   { TYPE_SWITCH, &si.sound,             "sound"                         },
3520   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
3521   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
3522   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
3523   { TYPE_SWITCH, &si.toons,             "toons"                         },
3524   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
3525   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
3526   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
3527   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
3528   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
3529   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
3530   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
3531   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
3532   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
3533   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
3534   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
3535   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
3536   { TYPE_STRING, &si.music_set,         "music_set"                     },
3537   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3538   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
3539   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
3540 };
3541
3542 static struct TokenInfo editor_setup_tokens[] =
3543 {
3544   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
3545   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
3546   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
3547   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
3548   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
3549   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
3550   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
3551   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
3552   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
3553   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
3554   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
3555   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
3556 };
3557
3558 static struct TokenInfo shortcut_setup_tokens[] =
3559 {
3560   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
3561   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
3562   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
3563 };
3564
3565 static struct TokenInfo player_setup_tokens[] =
3566 {
3567   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
3568   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
3569   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
3570   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
3571   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
3572   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
3573   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
3574   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
3575   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
3576   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
3577   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
3578   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
3579   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
3580   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
3581   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
3582   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
3583 };
3584
3585 static struct TokenInfo system_setup_tokens[] =
3586 {
3587   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
3588   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3589 };
3590
3591 static struct TokenInfo options_setup_tokens[] =
3592 {
3593   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
3594 };
3595
3596 static char *get_corrected_login_name(char *login_name)
3597 {
3598   /* needed because player name must be a fixed length string */
3599   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3600
3601   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3602   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3603
3604   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
3605     if (strchr(login_name_new, ' '))
3606       *strchr(login_name_new, ' ') = '\0';
3607
3608   return login_name_new;
3609 }
3610
3611 static void setSetupInfoToDefaults(struct SetupInfo *si)
3612 {
3613   int i;
3614
3615   si->player_name = get_corrected_login_name(getLoginName());
3616
3617   si->sound = TRUE;
3618   si->sound_loops = TRUE;
3619   si->sound_music = TRUE;
3620   si->sound_simple = TRUE;
3621   si->toons = TRUE;
3622   si->double_buffering = TRUE;
3623   si->direct_draw = !si->double_buffering;
3624   si->scroll_delay = TRUE;
3625   si->soft_scrolling = TRUE;
3626   si->fading = FALSE;
3627   si->autorecord = TRUE;
3628   si->quick_doors = FALSE;
3629   si->team_mode = FALSE;
3630   si->handicap = TRUE;
3631   si->time_limit = TRUE;
3632   si->fullscreen = FALSE;
3633   si->ask_on_escape = TRUE;
3634
3635   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3636   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3637   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3638   si->override_level_graphics = FALSE;
3639   si->override_level_sounds = FALSE;
3640   si->override_level_music = FALSE;
3641
3642   si->editor.el_boulderdash = TRUE;
3643   si->editor.el_emerald_mine = TRUE;
3644   si->editor.el_more = TRUE;
3645   si->editor.el_sokoban = TRUE;
3646   si->editor.el_supaplex = TRUE;
3647   si->editor.el_diamond_caves = TRUE;
3648   si->editor.el_dx_boulderdash = TRUE;
3649   si->editor.el_chars = TRUE;
3650   si->editor.el_custom = TRUE;
3651   si->editor.el_custom_more = FALSE;
3652
3653   si->editor.el_headlines = TRUE;
3654   si->editor.el_user_defined = FALSE;
3655
3656   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3657   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3658   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3659
3660   for (i = 0; i < MAX_PLAYERS; i++)
3661   {
3662     si->input[i].use_joystick = FALSE;
3663     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3664     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
3665     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3666     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
3667     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
3668     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3669     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
3670     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
3671     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
3672     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
3673     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3674     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
3675     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
3676     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
3677     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
3678   }
3679
3680   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3681   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3682
3683   si->options.verbose = FALSE;
3684 }
3685
3686 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3687 {
3688   int i, pnr;
3689
3690   if (!setup_file_hash)
3691     return;
3692
3693   /* global setup */
3694   si = setup;
3695   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3696     setSetupInfo(global_setup_tokens, i,
3697                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3698   setup = si;
3699
3700   /* editor setup */
3701   sei = setup.editor;
3702   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3703     setSetupInfo(editor_setup_tokens, i,
3704                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3705   setup.editor = sei;
3706
3707   /* shortcut setup */
3708   ssi = setup.shortcut;
3709   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3710     setSetupInfo(shortcut_setup_tokens, i,
3711                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3712   setup.shortcut = ssi;
3713
3714   /* player setup */
3715   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3716   {
3717     char prefix[30];
3718
3719     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3720
3721     sii = setup.input[pnr];
3722     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3723     {
3724       char full_token[100];
3725
3726       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3727       setSetupInfo(player_setup_tokens, i,
3728                    getHashEntry(setup_file_hash, full_token));
3729     }
3730     setup.input[pnr] = sii;
3731   }
3732
3733   /* system setup */
3734   syi = setup.system;
3735   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3736     setSetupInfo(system_setup_tokens, i,
3737                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3738   setup.system = syi;
3739
3740   /* options setup */
3741   soi = setup.options;
3742   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3743     setSetupInfo(options_setup_tokens, i,
3744                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3745   setup.options = soi;
3746 }
3747
3748 void LoadSetup()
3749 {
3750   char *filename = getSetupFilename();
3751   SetupFileHash *setup_file_hash = NULL;
3752
3753   /* always start with reliable default values */
3754   setSetupInfoToDefaults(&setup);
3755
3756   setup_file_hash = loadSetupFileHash(filename);
3757
3758   if (setup_file_hash)
3759   {
3760     char *player_name_new;
3761
3762     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3763     decodeSetupFileHash(setup_file_hash);
3764
3765     setup.direct_draw = !setup.double_buffering;
3766
3767     freeSetupFileHash(setup_file_hash);
3768
3769     /* needed to work around problems with fixed length strings */
3770     player_name_new = get_corrected_login_name(setup.player_name);
3771     free(setup.player_name);
3772     setup.player_name = player_name_new;
3773   }
3774   else
3775     Error(ERR_WARN, "using default setup values");
3776 }
3777
3778 void SaveSetup()
3779 {
3780   char *filename = getSetupFilename();
3781   FILE *file;
3782   int i, pnr;
3783
3784   InitUserDataDirectory();
3785
3786   if (!(file = fopen(filename, MODE_WRITE)))
3787   {
3788     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3789     return;
3790   }
3791
3792   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3793                                                getCookie("SETUP")));
3794   fprintf(file, "\n");
3795
3796   /* global setup */
3797   si = setup;
3798   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3799   {
3800     /* just to make things nicer :) */
3801     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3802         i == SETUP_TOKEN_GRAPHICS_SET)
3803       fprintf(file, "\n");
3804
3805     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3806   }
3807
3808   /* editor setup */
3809   sei = setup.editor;
3810   fprintf(file, "\n");
3811   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3812     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3813
3814   /* shortcut setup */
3815   ssi = setup.shortcut;
3816   fprintf(file, "\n");
3817   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3818     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3819
3820   /* player setup */
3821   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3822   {
3823     char prefix[30];
3824
3825     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3826     fprintf(file, "\n");
3827
3828     sii = setup.input[pnr];
3829     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3830       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3831   }
3832
3833   /* system setup */
3834   syi = setup.system;
3835   fprintf(file, "\n");
3836   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3837     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3838
3839   /* options setup */
3840   soi = setup.options;
3841   fprintf(file, "\n");
3842   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3843     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3844
3845   fclose(file);
3846
3847   SetFilePermissions(filename, PERMS_PRIVATE);
3848 }
3849
3850 void LoadCustomElementDescriptions()
3851 {
3852   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3853   SetupFileHash *setup_file_hash;
3854   int i;
3855
3856   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3857   {
3858     if (element_info[i].custom_description != NULL)
3859     {
3860       free(element_info[i].custom_description);
3861       element_info[i].custom_description = NULL;
3862     }
3863   }
3864
3865   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3866     return;
3867
3868   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3869   {
3870     char *token = getStringCat2(element_info[i].token_name, ".name");
3871     char *value = getHashEntry(setup_file_hash, token);
3872
3873     if (value != NULL)
3874       element_info[i].custom_description = getStringCopy(value);
3875
3876     free(token);
3877   }
3878
3879   freeSetupFileHash(setup_file_hash);
3880 }
3881
3882 void LoadSpecialMenuDesignSettings()
3883 {
3884   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3885   SetupFileHash *setup_file_hash;
3886   int i, j;
3887
3888   /* always start with reliable default values from default config */
3889   for (i = 0; image_config_vars[i].token != NULL; i++)
3890     for (j = 0; image_config[j].token != NULL; j++)
3891       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3892         *image_config_vars[i].value =
3893           get_auto_parameter_value(image_config_vars[i].token,
3894                                    image_config[j].value);
3895
3896   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3897     return;
3898
3899   /* special case: initialize with default values that may be overwritten */
3900   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3901   {
3902     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3903     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3904     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3905
3906     if (value_x != NULL)
3907       menu.draw_xoffset[i] = get_integer_from_string(value_x);
3908     if (value_y != NULL)
3909       menu.draw_yoffset[i] = get_integer_from_string(value_y);
3910     if (list_size != NULL)
3911       menu.list_size[i] = get_integer_from_string(list_size);
3912   }
3913
3914   /* read (and overwrite with) values that may be specified in config file */
3915   for (i = 0; image_config_vars[i].token != NULL; i++)
3916   {
3917     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3918
3919     if (value != NULL)
3920       *image_config_vars[i].value =
3921         get_auto_parameter_value(image_config_vars[i].token, value);
3922   }
3923
3924   freeSetupFileHash(setup_file_hash);
3925 }
3926
3927 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3928 {
3929   char *filename = getEditorSetupFilename();
3930   SetupFileList *setup_file_list, *list;
3931   SetupFileHash *element_hash;
3932   int num_unknown_tokens = 0;
3933   int i;
3934
3935   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3936     return;
3937
3938   element_hash = newSetupFileHash();
3939
3940   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3941     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3942
3943   /* determined size may be larger than needed (due to unknown elements) */
3944   *num_elements = 0;
3945   for (list = setup_file_list; list != NULL; list = list->next)
3946     (*num_elements)++;
3947
3948   /* add space for up to 3 more elements for padding that may be needed */
3949   *num_elements += 3;
3950
3951   *elements = checked_malloc(*num_elements * sizeof(int));
3952
3953   *num_elements = 0;
3954   for (list = setup_file_list; list != NULL; list = list->next)
3955   {
3956     char *value = getHashEntry(element_hash, list->token);
3957
3958     if (value)
3959     {
3960       (*elements)[(*num_elements)++] = atoi(value);
3961     }
3962     else
3963     {
3964       if (num_unknown_tokens == 0)
3965       {
3966         Error(ERR_RETURN_LINE, "-");
3967         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3968         Error(ERR_RETURN, "- config file: '%s'", filename);
3969
3970         num_unknown_tokens++;
3971       }
3972
3973       Error(ERR_RETURN, "- token: '%s'", list->token);
3974     }
3975   }
3976
3977   if (num_unknown_tokens > 0)
3978     Error(ERR_RETURN_LINE, "-");
3979
3980   while (*num_elements % 4)     /* pad with empty elements, if needed */
3981     (*elements)[(*num_elements)++] = EL_EMPTY;
3982
3983   freeSetupFileList(setup_file_list);
3984   freeSetupFileHash(element_hash);
3985
3986 #if 0
3987   /* TEST-ONLY */
3988   for (i = 0; i < *num_elements; i++)
3989     printf("editor: element '%s' [%d]\n",
3990            element_info[(*elements)[i]].token_name, (*elements)[i]);
3991 #endif
3992 }
3993
3994 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3995                                                      boolean is_sound)
3996 {
3997   SetupFileHash *setup_file_hash = NULL;
3998   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3999   char *filename_music, *filename_prefix, *filename_info;
4000   struct
4001   {
4002     char *token;
4003     char **value_ptr;
4004   }
4005   token_to_value_ptr[] =
4006   {
4007     { "title_header",   &tmp_music_file_info.title_header       },
4008     { "artist_header",  &tmp_music_file_info.artist_header      },
4009     { "album_header",   &tmp_music_file_info.album_header       },
4010     { "year_header",    &tmp_music_file_info.year_header        },
4011
4012     { "title",          &tmp_music_file_info.title              },
4013     { "artist",         &tmp_music_file_info.artist             },
4014     { "album",          &tmp_music_file_info.album              },
4015     { "year",           &tmp_music_file_info.year               },
4016
4017     { NULL,             NULL                                    },
4018   };
4019   int i;
4020
4021   filename_music = (is_sound ? getCustomSoundFilename(basename) :
4022                     getCustomMusicFilename(basename));
4023
4024   if (filename_music == NULL)
4025     return NULL;
4026
4027   /* ---------- try to replace file extension ---------- */
4028
4029   filename_prefix = getStringCopy(filename_music);
4030   if (strrchr(filename_prefix, '.') != NULL)
4031     *strrchr(filename_prefix, '.') = '\0';
4032   filename_info = getStringCat2(filename_prefix, ".txt");
4033
4034 #if 0
4035   printf("trying to load file '%s'...\n", filename_info);
4036 #endif
4037
4038   if (fileExists(filename_info))
4039     setup_file_hash = loadSetupFileHash(filename_info);
4040
4041   free(filename_prefix);
4042   free(filename_info);
4043
4044   if (setup_file_hash == NULL)
4045   {
4046     /* ---------- try to add file extension ---------- */
4047
4048     filename_prefix = getStringCopy(filename_music);
4049     filename_info = getStringCat2(filename_prefix, ".txt");
4050
4051 #if 0
4052     printf("trying to load file '%s'...\n", filename_info);
4053 #endif
4054
4055     if (fileExists(filename_info))
4056       setup_file_hash = loadSetupFileHash(filename_info);
4057
4058     free(filename_prefix);
4059     free(filename_info);
4060   }
4061
4062   if (setup_file_hash == NULL)
4063     return NULL;
4064
4065   /* ---------- music file info found ---------- */
4066
4067   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4068
4069   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4070   {
4071     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4072
4073     *token_to_value_ptr[i].value_ptr =
4074       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4075   }
4076
4077   tmp_music_file_info.basename = getStringCopy(basename);
4078   tmp_music_file_info.music = music;
4079   tmp_music_file_info.is_sound = is_sound;
4080
4081   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4082   *new_music_file_info = tmp_music_file_info;
4083
4084   return new_music_file_info;
4085 }
4086
4087 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4088 {
4089   return get_music_file_info_ext(basename, music, FALSE);
4090 }
4091
4092 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4093 {
4094   return get_music_file_info_ext(basename, sound, TRUE);
4095 }
4096
4097 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4098                                      char *basename, boolean is_sound)
4099 {
4100   for (; list != NULL; list = list->next)
4101     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4102       return TRUE;
4103
4104   return FALSE;
4105 }
4106
4107 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4108 {
4109   return music_info_listed_ext(list, basename, FALSE);
4110 }
4111
4112 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4113 {
4114   return music_info_listed_ext(list, basename, TRUE);
4115 }
4116
4117 void LoadMusicInfo()
4118 {
4119   char *music_directory = getCustomMusicDirectory();
4120   int num_music = getMusicListSize();
4121   int num_music_noconf = 0;
4122   int num_sounds = getSoundListSize();
4123   DIR *dir;
4124   struct dirent *dir_entry;
4125   struct FileInfo *music, *sound;
4126   struct MusicFileInfo *next, **new;
4127   int i;
4128
4129   while (music_file_info != NULL)
4130   {
4131     next = music_file_info->next;
4132
4133     checked_free(music_file_info->basename);
4134
4135     checked_free(music_file_info->title_header);
4136     checked_free(music_file_info->artist_header);
4137     checked_free(music_file_info->album_header);
4138     checked_free(music_file_info->year_header);
4139
4140     checked_free(music_file_info->title);
4141     checked_free(music_file_info->artist);
4142     checked_free(music_file_info->album);
4143     checked_free(music_file_info->year);
4144
4145     free(music_file_info);
4146
4147     music_file_info = next;
4148   }
4149
4150   new = &music_file_info;
4151
4152 #if 0
4153   printf("::: num_music == %d\n", num_music);
4154 #endif
4155
4156   for (i = 0; i < num_music; i++)
4157   {
4158     music = getMusicListEntry(i);
4159
4160 #if 0
4161     printf("::: %d [%08x]\n", i, music->filename);
4162 #endif
4163
4164     if (music->filename == NULL)
4165       continue;
4166
4167     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4168       continue;
4169
4170     /* a configured file may be not recognized as music */
4171     if (!FileIsMusic(music->filename))
4172       continue;
4173
4174 #if 0
4175     printf("::: -> '%s' (configured)\n", music->filename);
4176 #endif
4177
4178     if (!music_info_listed(music_file_info, music->filename))
4179     {
4180       *new = get_music_file_info(music->filename, i);
4181       if (*new != NULL)
4182         new = &(*new)->next;
4183     }
4184   }
4185
4186   if ((dir = opendir(music_directory)) == NULL)
4187   {
4188     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4189     return;
4190   }
4191
4192   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
4193   {
4194     char *basename = dir_entry->d_name;
4195     boolean music_already_used = FALSE;
4196     int i;
4197
4198     /* skip all music files that are configured in music config file */
4199     for (i = 0; i < num_music; i++)
4200     {
4201       music = getMusicListEntry(i);
4202
4203       if (music->filename == NULL)
4204         continue;
4205
4206       if (strcmp(basename, music->filename) == 0)
4207       {
4208         music_already_used = TRUE;
4209         break;
4210       }
4211     }
4212
4213     if (music_already_used)
4214       continue;
4215
4216     if (!FileIsMusic(basename))
4217       continue;
4218
4219 #if 0
4220     printf("::: -> '%s' (found in directory)\n", basename);
4221 #endif
4222
4223     if (!music_info_listed(music_file_info, basename))
4224     {
4225       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4226       if (*new != NULL)
4227         new = &(*new)->next;
4228     }
4229
4230     num_music_noconf++;
4231   }
4232
4233   closedir(dir);
4234
4235   for (i = 0; i < num_sounds; i++)
4236   {
4237     sound = getSoundListEntry(i);
4238
4239     if (sound->filename == NULL)
4240       continue;
4241
4242     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4243       continue;
4244
4245     /* a configured file may be not recognized as sound */
4246     if (!FileIsSound(sound->filename))
4247       continue;
4248
4249 #if 0
4250     printf("::: -> '%s' (configured)\n", sound->filename);
4251 #endif
4252
4253     if (!sound_info_listed(music_file_info, sound->filename))
4254     {
4255       *new = get_sound_file_info(sound->filename, i);
4256       if (*new != NULL)
4257         new = &(*new)->next;
4258     }
4259   }
4260
4261 #if 0
4262   /* TEST-ONLY */
4263   for (next = music_file_info; next != NULL; next = next->next)
4264     printf("::: title == '%s'\n", next->title);
4265 #endif
4266 }
4267
4268 void add_helpanim_entry(int element, int action, int direction, int delay,
4269                         int *num_list_entries)
4270 {
4271   struct HelpAnimInfo *new_list_entry;
4272   (*num_list_entries)++;
4273
4274   helpanim_info =
4275     checked_realloc(helpanim_info,
4276                     *num_list_entries * sizeof(struct HelpAnimInfo));
4277   new_list_entry = &helpanim_info[*num_list_entries - 1];
4278
4279   new_list_entry->element = element;
4280   new_list_entry->action = action;
4281   new_list_entry->direction = direction;
4282   new_list_entry->delay = delay;
4283 }
4284
4285 void print_unknown_token(char *filename, char *token, int token_nr)
4286 {
4287   if (token_nr == 0)
4288   {
4289     Error(ERR_RETURN_LINE, "-");
4290     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4291     Error(ERR_RETURN, "- config file: '%s'", filename);
4292   }
4293
4294   Error(ERR_RETURN, "- token: '%s'", token);
4295 }
4296
4297 void print_unknown_token_end(int token_nr)
4298 {
4299   if (token_nr > 0)
4300     Error(ERR_RETURN_LINE, "-");
4301 }
4302
4303 void LoadHelpAnimInfo()
4304 {
4305   char *filename = getHelpAnimFilename();
4306   SetupFileList *setup_file_list = NULL, *list;
4307   SetupFileHash *element_hash, *action_hash, *direction_hash;
4308   int num_list_entries = 0;
4309   int num_unknown_tokens = 0;
4310   int i;
4311
4312   if (fileExists(filename))
4313     setup_file_list = loadSetupFileList(filename);
4314
4315   if (setup_file_list == NULL)
4316   {
4317     /* use reliable default values from static configuration */
4318     SetupFileList *insert_ptr;
4319
4320     insert_ptr = setup_file_list =
4321       newSetupFileList(helpanim_config[0].token,
4322                        helpanim_config[0].value);
4323
4324     for (i = 1; helpanim_config[i].token; i++)
4325       insert_ptr = addListEntry(insert_ptr,
4326                                 helpanim_config[i].token,
4327                                 helpanim_config[i].value);
4328   }
4329
4330   element_hash   = newSetupFileHash();
4331   action_hash    = newSetupFileHash();
4332   direction_hash = newSetupFileHash();
4333
4334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4335     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4336
4337   for (i = 0; i < NUM_ACTIONS; i++)
4338     setHashEntry(action_hash, element_action_info[i].suffix,
4339                  i_to_a(element_action_info[i].value));
4340
4341   /* do not store direction index (bit) here, but direction value! */
4342   for (i = 0; i < NUM_DIRECTIONS; i++)
4343     setHashEntry(direction_hash, element_direction_info[i].suffix,
4344                  i_to_a(1 << element_direction_info[i].value));
4345
4346   for (list = setup_file_list; list != NULL; list = list->next)
4347   {
4348     char *element_token, *action_token, *direction_token;
4349     char *element_value, *action_value, *direction_value;
4350     int delay = atoi(list->value);
4351
4352     if (strcmp(list->token, "end") == 0)
4353     {
4354       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4355
4356       continue;
4357     }
4358
4359     /* first try to break element into element/action/direction parts;
4360        if this does not work, also accept combined "element[.act][.dir]"
4361        elements (like "dynamite.active"), which are unique elements */
4362
4363     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
4364     {
4365       element_value = getHashEntry(element_hash, list->token);
4366       if (element_value != NULL)        /* element found */
4367         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4368                            &num_list_entries);
4369       else
4370       {
4371         /* no further suffixes found -- this is not an element */
4372         print_unknown_token(filename, list->token, num_unknown_tokens++);
4373       }
4374
4375       continue;
4376     }
4377
4378     /* token has format "<prefix>.<something>" */
4379
4380     action_token = strchr(list->token, '.');    /* suffix may be action ... */
4381     direction_token = action_token;             /* ... or direction */
4382
4383     element_token = getStringCopy(list->token);
4384     *strchr(element_token, '.') = '\0';
4385
4386     element_value = getHashEntry(element_hash, element_token);
4387
4388     if (element_value == NULL)          /* this is no element */
4389     {
4390       element_value = getHashEntry(element_hash, list->token);
4391       if (element_value != NULL)        /* combined element found */
4392         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4393                            &num_list_entries);
4394       else
4395         print_unknown_token(filename, list->token, num_unknown_tokens++);
4396
4397       free(element_token);
4398
4399       continue;
4400     }
4401
4402     action_value = getHashEntry(action_hash, action_token);
4403
4404     if (action_value != NULL)           /* action found */
4405     {
4406       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4407                     &num_list_entries);
4408
4409       free(element_token);
4410
4411       continue;
4412     }
4413
4414     direction_value = getHashEntry(direction_hash, direction_token);
4415
4416     if (direction_value != NULL)        /* direction found */
4417     {
4418       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4419                          &num_list_entries);
4420
4421       free(element_token);
4422
4423       continue;
4424     }
4425
4426     if (strchr(action_token + 1, '.') == NULL)
4427     {
4428       /* no further suffixes found -- this is not an action nor direction */
4429
4430       element_value = getHashEntry(element_hash, list->token);
4431       if (element_value != NULL)        /* combined element found */
4432         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4433                            &num_list_entries);
4434       else
4435         print_unknown_token(filename, list->token, num_unknown_tokens++);
4436
4437       free(element_token);
4438
4439       continue;
4440     }
4441
4442     /* token has format "<prefix>.<suffix>.<something>" */
4443
4444     direction_token = strchr(action_token + 1, '.');
4445
4446     action_token = getStringCopy(action_token);
4447     *strchr(action_token + 1, '.') = '\0';
4448
4449     action_value = getHashEntry(action_hash, action_token);
4450
4451     if (action_value == NULL)           /* this is no action */
4452     {
4453       element_value = getHashEntry(element_hash, list->token);
4454       if (element_value != NULL)        /* combined element found */
4455         add_helpanim_entry(atoi(element_value), -1, -1, delay,
4456                            &num_list_entries);
4457       else
4458         print_unknown_token(filename, list->token, num_unknown_tokens++);
4459
4460       free(element_token);
4461       free(action_token);
4462
4463       continue;
4464     }
4465
4466     direction_value = getHashEntry(direction_hash, direction_token);
4467
4468     if (direction_value != NULL)        /* direction found */
4469     {
4470       add_helpanim_entry(atoi(element_value), atoi(action_value),
4471                          atoi(direction_value), delay, &num_list_entries);
4472
4473       free(element_token);
4474       free(action_token);
4475
4476       continue;
4477     }
4478
4479     /* this is no direction */
4480
4481     element_value = getHashEntry(element_hash, list->token);
4482     if (element_value != NULL)          /* combined element found */
4483       add_helpanim_entry(atoi(element_value), -1, -1, delay,
4484                          &num_list_entries);
4485     else
4486       print_unknown_token(filename, list->token, num_unknown_tokens++);
4487
4488     free(element_token);
4489     free(action_token);
4490   }
4491
4492   print_unknown_token_end(num_unknown_tokens);
4493
4494   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4495   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
4496
4497   freeSetupFileList(setup_file_list);
4498   freeSetupFileHash(element_hash);
4499   freeSetupFileHash(action_hash);
4500   freeSetupFileHash(direction_hash);
4501
4502 #if 0
4503   /* TEST ONLY */
4504   for (i = 0; i < num_list_entries; i++)
4505     printf("::: %d, %d, %d => %d\n",
4506            helpanim_info[i].element,
4507            helpanim_info[i].action,
4508            helpanim_info[i].direction,
4509            helpanim_info[i].delay);
4510 #endif
4511 }
4512
4513 void LoadHelpTextInfo()
4514 {
4515   char *filename = getHelpTextFilename();
4516   int i;
4517
4518   if (helptext_info != NULL)
4519   {
4520     freeSetupFileHash(helptext_info);
4521     helptext_info = NULL;
4522   }
4523
4524   if (fileExists(filename))
4525     helptext_info = loadSetupFileHash(filename);
4526
4527   if (helptext_info == NULL)
4528   {
4529     /* use reliable default values from static configuration */
4530     helptext_info = newSetupFileHash();
4531
4532     for (i = 0; helptext_config[i].token; i++)
4533       setHashEntry(helptext_info,
4534                    helptext_config[i].token,
4535                    helptext_config[i].value);
4536   }
4537
4538 #if 0
4539   /* TEST ONLY */
4540   BEGIN_HASH_ITERATION(helptext_info, itr)
4541   {
4542     printf("::: '%s' => '%s'\n",
4543            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4544   }
4545   END_HASH_ITERATION(hash, itr)
4546 #endif
4547 }