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