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