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