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