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