e1cd2ed4016c7cdf6ea7093adbbcf90895f8b7bc
[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   if (level->game_version < VERSION_IDENT(3,1,1,0))
2764   {
2765     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2766     {
2767       int element = EL_CUSTOM_START + i;
2768       struct ElementInfo *ei = &element_info[element];
2769
2770       if (ei->access_direction == MV_NO_MOVING)
2771         ei->access_direction = MV_ALL_DIRECTIONS;
2772
2773       for (j = 0; j < ei->num_change_pages; j++)
2774       {
2775         struct ElementChangeInfo *change = &ei->change_page[j];
2776
2777         if (change->trigger_side == CH_SIDE_NONE)
2778           change->trigger_side = CH_SIDE_ANY;
2779       }
2780     }
2781   }
2782
2783   /* initialize "can_explode" field for old levels which did not store this */
2784   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
2785   if (level->game_version <= VERSION_IDENT(3,1,0,0))
2786   {
2787     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2788     {
2789       int element = EL_CUSTOM_START + i;
2790
2791       if (EXPLODES_1X1_OLD(element))
2792         element_info[element].explosion_type = EXPLODES_1X1;
2793
2794       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
2795                                              EXPLODES_SMASHED(element) ||
2796                                              EXPLODES_IMPACT(element)));
2797     }
2798   }
2799
2800   /* correct previously hard-coded move delay values for maze runner style */
2801   if (level->game_version < VERSION_IDENT(3,1,1,0))
2802   {
2803     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2804     {
2805       int element = EL_CUSTOM_START + i;
2806
2807       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
2808       {
2809         /* previously hard-coded and therefore ignored */
2810         element_info[element].move_delay_fixed = 9;
2811         element_info[element].move_delay_random = 0;
2812       }
2813     }
2814   }
2815
2816 #if 0
2817   /* set default push delay values (corrected since version 3.0.7-1) */
2818   if (level->game_version < VERSION_IDENT(3,0,7,1))
2819   {
2820     game.default_push_delay_fixed = 2;
2821     game.default_push_delay_random = 8;
2822   }
2823   else
2824   {
2825     game.default_push_delay_fixed = 8;
2826     game.default_push_delay_random = 8;
2827   }
2828
2829   /* set uninitialized push delay values of custom elements in older levels */
2830   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2831   {
2832     int element = EL_CUSTOM_START + i;
2833
2834     if (element_info[element].push_delay_fixed == -1)
2835       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2836     if (element_info[element].push_delay_random == -1)
2837       element_info[element].push_delay_random = game.default_push_delay_random;
2838   }
2839 #endif
2840
2841   /* map elements that have changed in newer versions */
2842   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2843                                                     level->game_version);
2844   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2845     for (x = 0; x < 3; x++)
2846       for (y = 0; y < 3; y++)
2847         level->yamyam_content[i][x][y] =
2848           getMappedElementByVersion(level->yamyam_content[i][x][y],
2849                                     level->game_version);
2850
2851   /* initialize element properties for level editor etc. */
2852   InitElementPropertiesEngine(level->game_version);
2853 }
2854
2855 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2856 {
2857   int x, y;
2858
2859   /* map elements that have changed in newer versions */
2860   for (y = 0; y < level->fieldy; y++)
2861   {
2862     for (x = 0; x < level->fieldx; x++)
2863     {
2864       int element = level->field[x][y];
2865
2866 #if 1
2867       element = getMappedElementByVersion(element, level->game_version);
2868 #else
2869       if (level->game_version <= VERSION_IDENT(2,2,0,0))
2870       {
2871         /* map game font elements */
2872         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2873                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2874                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2875                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2876       }
2877
2878       if (level->game_version < VERSION_IDENT(3,0,0,0))
2879       {
2880         /* map Supaplex gravity tube elements */
2881         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2882                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2883                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2884                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2885                    element);
2886       }
2887 #endif
2888
2889       level->field[x][y] = element;
2890     }
2891   }
2892
2893   /* copy elements to runtime playfield array */
2894   for (x = 0; x < MAX_LEV_FIELDX; x++)
2895     for (y = 0; y < MAX_LEV_FIELDY; y++)
2896       Feld[x][y] = level->field[x][y];
2897
2898   /* initialize level size variables for faster access */
2899   lev_fieldx = level->fieldx;
2900   lev_fieldy = level->fieldy;
2901
2902   /* determine border element for this level */
2903   SetBorderElement();
2904 }
2905
2906 void LoadLevelTemplate(int nr)
2907 {
2908 #if 1
2909   char *filename;
2910
2911   setLevelFileInfo(&level_template.file_info, nr);
2912   filename = level_template.file_info.filename;
2913
2914   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
2915
2916 #else
2917
2918 #if 1
2919   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2920   char *filename = level_file_info->filename;
2921
2922   LoadLevelFromFileInfo(&level_template, level_file_info);
2923 #else
2924   char *filename = getDefaultLevelFilename(nr);
2925
2926   LoadLevelFromFilename_RND(&level_template, filename);
2927 #endif
2928 #endif
2929
2930 #if 1
2931   LoadLevel_InitVersion(&level_template, filename);
2932   LoadLevel_InitElements(&level_template, filename);
2933 #else
2934   LoadLevel_InitVersion(&level, filename);
2935   LoadLevel_InitElements(&level, filename);
2936 #endif
2937
2938   ActivateLevelTemplate();
2939 }
2940
2941 void LoadLevel(int nr)
2942 {
2943 #if 1
2944   char *filename;
2945
2946   setLevelFileInfo(&level.file_info, nr);
2947   filename = level.file_info.filename;
2948
2949   LoadLevelFromFileInfo(&level, &level.file_info);
2950
2951 #else
2952
2953 #if 1
2954   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2955   char *filename = level_file_info->filename;
2956
2957   LoadLevelFromFileInfo(&level, level_file_info);
2958 #else
2959   char *filename = getLevelFilename(nr);
2960
2961   LoadLevelFromFilename_RND(&level, filename);
2962 #endif
2963 #endif
2964
2965   if (level.use_custom_template)
2966     LoadLevelTemplate(-1);
2967
2968 #if 1
2969   LoadLevel_InitVersion(&level, filename);
2970   LoadLevel_InitElements(&level, filename);
2971   LoadLevel_InitPlayfield(&level, filename);
2972 #else
2973   LoadLevel_InitLevel(&level, filename);
2974 #endif
2975 }
2976
2977 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2978 {
2979   putFileVersion(file, level->file_version);
2980   putFileVersion(file, level->game_version);
2981 }
2982
2983 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2984 {
2985   int i, x, y;
2986
2987   putFile8Bit(file, level->fieldx);
2988   putFile8Bit(file, level->fieldy);
2989
2990   putFile16BitBE(file, level->time);
2991   putFile16BitBE(file, level->gems_needed);
2992
2993   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2994     putFile8Bit(file, level->name[i]);
2995
2996   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2997     putFile8Bit(file, level->score[i]);
2998
2999   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3000     for (y = 0; y < 3; y++)
3001       for (x = 0; x < 3; x++)
3002         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
3003                            level->yamyam_content[i][x][y]));
3004   putFile8Bit(file, level->amoeba_speed);
3005   putFile8Bit(file, level->time_magic_wall);
3006   putFile8Bit(file, level->time_wheel);
3007   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
3008                      level->amoeba_content));
3009   putFile8Bit(file, (level->double_speed ? 1 : 0));
3010   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
3011   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
3012   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
3013
3014   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
3015
3016   putFile8Bit(file, (level->block_last_field ? 1 : 0));
3017   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
3018   putFile32BitBE(file, level->can_move_into_acid_bits);
3019   putFile8Bit(file, level->dont_collide_with_bits);
3020
3021   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
3022   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
3023
3024   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
3025   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
3026   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
3027
3028   putFile8Bit(file, level->game_engine_type);
3029
3030   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
3031 }
3032
3033 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
3034 {
3035   int i;
3036
3037   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3038     putFile8Bit(file, level->author[i]);
3039 }
3040
3041 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
3042 {
3043   int x, y;
3044
3045   for (y = 0; y < level->fieldy; y++) 
3046     for (x = 0; x < level->fieldx; x++) 
3047       if (level->encoding_16bit_field)
3048         putFile16BitBE(file, level->field[x][y]);
3049       else
3050         putFile8Bit(file, level->field[x][y]);
3051 }
3052
3053 #if 0
3054 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
3055 {
3056   int i, x, y;
3057
3058   putFile8Bit(file, EL_YAMYAM);
3059   putFile8Bit(file, level->num_yamyam_contents);
3060   putFile8Bit(file, 0);
3061   putFile8Bit(file, 0);
3062
3063   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3064     for (y = 0; y < 3; y++)
3065       for (x = 0; x < 3; x++)
3066         if (level->encoding_16bit_field)
3067           putFile16BitBE(file, level->yamyam_content[i][x][y]);
3068         else
3069           putFile8Bit(file, level->yamyam_content[i][x][y]);
3070 }
3071 #endif
3072
3073 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
3074 {
3075   int i, x, y;
3076   int num_contents, content_xsize, content_ysize;
3077   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3078
3079   if (element == EL_YAMYAM)
3080   {
3081     num_contents = level->num_yamyam_contents;
3082     content_xsize = 3;
3083     content_ysize = 3;
3084
3085     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3086       for (y = 0; y < 3; y++)
3087         for (x = 0; x < 3; x++)
3088           content_array[i][x][y] = level->yamyam_content[i][x][y];
3089   }
3090   else if (element == EL_BD_AMOEBA)
3091   {
3092     num_contents = 1;
3093     content_xsize = 1;
3094     content_ysize = 1;
3095
3096     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3097       for (y = 0; y < 3; y++)
3098         for (x = 0; x < 3; x++)
3099           content_array[i][x][y] = EL_EMPTY;
3100     content_array[0][0][0] = level->amoeba_content;
3101   }
3102   else
3103   {
3104     /* chunk header already written -- write empty chunk data */
3105     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
3106
3107     Error(ERR_WARN, "cannot save content for element '%d'", element);
3108     return;
3109   }
3110
3111   putFile16BitBE(file, element);
3112   putFile8Bit(file, num_contents);
3113   putFile8Bit(file, content_xsize);
3114   putFile8Bit(file, content_ysize);
3115
3116   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3117
3118   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3119     for (y = 0; y < 3; y++)
3120       for (x = 0; x < 3; x++)
3121         putFile16BitBE(file, content_array[i][x][y]);
3122 }
3123
3124 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
3125 {
3126   int i;
3127   int envelope_nr = element - EL_ENVELOPE_1;
3128   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
3129
3130   putFile16BitBE(file, element);
3131   putFile16BitBE(file, envelope_len);
3132   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
3133   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
3134
3135   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3136
3137   for (i = 0; i < envelope_len; i++)
3138     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
3139 }
3140
3141 #if 0
3142 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
3143                            int num_changed_custom_elements)
3144 {
3145   int i, check = 0;
3146
3147   putFile16BitBE(file, num_changed_custom_elements);
3148
3149   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3150   {
3151     int element = EL_CUSTOM_START + i;
3152
3153     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
3154     {
3155       if (check < num_changed_custom_elements)
3156       {
3157         putFile16BitBE(file, element);
3158         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3159       }
3160
3161       check++;
3162     }
3163   }
3164
3165   if (check != num_changed_custom_elements)     /* should not happen */
3166     Error(ERR_WARN, "inconsistent number of custom element properties");
3167 }
3168 #endif
3169
3170 #if 0
3171 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
3172                            int num_changed_custom_elements)
3173 {
3174   int i, check = 0;
3175
3176   putFile16BitBE(file, num_changed_custom_elements);
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     int element = EL_CUSTOM_START + i;
3181
3182     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
3183     {
3184       if (check < num_changed_custom_elements)
3185       {
3186         putFile16BitBE(file, element);
3187         putFile16BitBE(file, element_info[element].change->target_element);
3188       }
3189
3190       check++;
3191     }
3192   }
3193
3194   if (check != num_changed_custom_elements)     /* should not happen */
3195     Error(ERR_WARN, "inconsistent number of custom target elements");
3196 }
3197 #endif
3198
3199 #if 0
3200 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
3201                            int num_changed_custom_elements)
3202 {
3203   int i, j, x, y, check = 0;
3204
3205   putFile16BitBE(file, num_changed_custom_elements);
3206
3207   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3208   {
3209     int element = EL_CUSTOM_START + i;
3210
3211     if (element_info[element].modified_settings)
3212     {
3213       if (check < num_changed_custom_elements)
3214       {
3215         putFile16BitBE(file, element);
3216
3217         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3218           putFile8Bit(file, element_info[element].description[j]);
3219
3220         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3221
3222         /* some free bytes for future properties and padding */
3223         WriteUnusedBytesToFile(file, 7);
3224
3225         putFile8Bit(file, element_info[element].use_gfx_element);
3226         putFile16BitBE(file, element_info[element].gfx_element);
3227
3228         putFile8Bit(file, element_info[element].collect_score);
3229         putFile8Bit(file, element_info[element].collect_count);
3230
3231         putFile16BitBE(file, element_info[element].push_delay_fixed);
3232         putFile16BitBE(file, element_info[element].push_delay_random);
3233         putFile16BitBE(file, element_info[element].move_delay_fixed);
3234         putFile16BitBE(file, element_info[element].move_delay_random);
3235
3236         putFile16BitBE(file, element_info[element].move_pattern);
3237         putFile8Bit(file, element_info[element].move_direction_initial);
3238         putFile8Bit(file, element_info[element].move_stepsize);
3239
3240         for (y = 0; y < 3; y++)
3241           for (x = 0; x < 3; x++)
3242             putFile16BitBE(file, element_info[element].content[x][y]);
3243
3244         putFile32BitBE(file, element_info[element].change->events);
3245
3246         putFile16BitBE(file, element_info[element].change->target_element);
3247
3248         putFile16BitBE(file, element_info[element].change->delay_fixed);
3249         putFile16BitBE(file, element_info[element].change->delay_random);
3250         putFile16BitBE(file, element_info[element].change->delay_frames);
3251
3252         putFile16BitBE(file, element_info[element].change->trigger_element);
3253
3254         putFile8Bit(file, element_info[element].change->explode);
3255         putFile8Bit(file, element_info[element].change->use_target_content);
3256         putFile8Bit(file, element_info[element].change->only_if_complete);
3257         putFile8Bit(file, element_info[element].change->use_random_replace);
3258
3259         putFile8Bit(file, element_info[element].change->random_percentage);
3260         putFile8Bit(file, element_info[element].change->replace_when);
3261
3262         for (y = 0; y < 3; y++)
3263           for (x = 0; x < 3; x++)
3264             putFile16BitBE(file, element_info[element].change->content[x][y]);
3265
3266         putFile8Bit(file, element_info[element].slippery_type);
3267
3268         /* some free bytes for future properties and padding */
3269         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
3270       }
3271
3272       check++;
3273     }
3274   }
3275
3276   if (check != num_changed_custom_elements)     /* should not happen */
3277     Error(ERR_WARN, "inconsistent number of custom element properties");
3278 }
3279 #endif
3280
3281 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
3282 {
3283   struct ElementInfo *ei = &element_info[element];
3284   int i, j, x, y;
3285
3286   putFile16BitBE(file, element);
3287
3288   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3289     putFile8Bit(file, ei->description[i]);
3290
3291   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
3292   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
3293
3294   putFile8Bit(file, ei->num_change_pages);
3295
3296   /* some free bytes for future base property values and padding */
3297   WriteUnusedBytesToFile(file, 5);
3298
3299   /* write custom property values */
3300
3301   putFile8Bit(file, ei->use_gfx_element);
3302   putFile16BitBE(file, ei->gfx_element);
3303
3304   putFile8Bit(file, ei->collect_score);
3305   putFile8Bit(file, ei->collect_count);
3306
3307   putFile8Bit(file, ei->drop_delay_fixed);
3308   putFile8Bit(file, ei->push_delay_fixed);
3309   putFile8Bit(file, ei->drop_delay_random);
3310   putFile8Bit(file, ei->push_delay_random);
3311   putFile16BitBE(file, ei->move_delay_fixed);
3312   putFile16BitBE(file, ei->move_delay_random);
3313
3314   /* bits 0 - 15 of "move_pattern" ... */
3315   putFile16BitBE(file, ei->move_pattern & 0xffff);
3316   putFile8Bit(file, ei->move_direction_initial);
3317   putFile8Bit(file, ei->move_stepsize);
3318
3319   putFile8Bit(file, ei->slippery_type);
3320
3321   for (y = 0; y < 3; y++)
3322     for (x = 0; x < 3; x++)
3323       putFile16BitBE(file, ei->content[x][y]);
3324
3325   putFile16BitBE(file, ei->move_enter_element);
3326   putFile16BitBE(file, ei->move_leave_element);
3327   putFile8Bit(file, ei->move_leave_type);
3328
3329   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3330   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
3331
3332   putFile8Bit(file, ei->access_direction);
3333
3334   putFile8Bit(file, ei->explosion_delay);
3335   putFile8Bit(file, ei->ignition_delay);
3336   putFile8Bit(file, ei->explosion_type);
3337
3338   /* some free bytes for future custom property values and padding */
3339   WriteUnusedBytesToFile(file, 1);
3340
3341   /* write change property values */
3342
3343   for (i = 0; i < ei->num_change_pages; i++)
3344   {
3345     struct ElementChangeInfo *change = &ei->change_page[i];
3346     unsigned long event_bits = 0;
3347
3348     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3349       if (change->has_event[j])
3350         event_bits |= (1 << j);
3351
3352     putFile32BitBE(file, event_bits);
3353
3354     putFile16BitBE(file, change->target_element);
3355
3356     putFile16BitBE(file, change->delay_fixed);
3357     putFile16BitBE(file, change->delay_random);
3358     putFile16BitBE(file, change->delay_frames);
3359
3360     putFile16BitBE(file, change->trigger_element);
3361
3362     putFile8Bit(file, change->explode);
3363     putFile8Bit(file, change->use_target_content);
3364     putFile8Bit(file, change->only_if_complete);
3365     putFile8Bit(file, change->use_random_replace);
3366
3367     putFile8Bit(file, change->random_percentage);
3368     putFile8Bit(file, change->replace_when);
3369
3370     for (y = 0; y < 3; y++)
3371       for (x = 0; x < 3; x++)
3372         putFile16BitBE(file, change->target_content[x][y]);
3373
3374     putFile8Bit(file, change->can_change);
3375
3376     putFile8Bit(file, change->trigger_side);
3377
3378 #if 1
3379     putFile8Bit(file, change->trigger_player);
3380     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
3381                        log_2(change->trigger_page)));
3382
3383     /* some free bytes for future change property values and padding */
3384     WriteUnusedBytesToFile(file, 6);
3385
3386 #else
3387
3388     /* some free bytes for future change property values and padding */
3389     WriteUnusedBytesToFile(file, 8);
3390 #endif
3391   }
3392 }
3393
3394 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
3395 {
3396   struct ElementInfo *ei = &element_info[element];
3397   struct ElementGroupInfo *group = ei->group;
3398   int i;
3399
3400   putFile16BitBE(file, element);
3401
3402   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3403     putFile8Bit(file, ei->description[i]);
3404
3405   putFile8Bit(file, group->num_elements);
3406
3407   putFile8Bit(file, ei->use_gfx_element);
3408   putFile16BitBE(file, ei->gfx_element);
3409
3410   putFile8Bit(file, group->choice_mode);
3411
3412   /* some free bytes for future values and padding */
3413   WriteUnusedBytesToFile(file, 3);
3414
3415   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3416     putFile16BitBE(file, group->element[i]);
3417 }
3418
3419 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3420 {
3421   int body_chunk_size;
3422   int i, x, y;
3423   FILE *file;
3424
3425   if (!(file = fopen(filename, MODE_WRITE)))
3426   {
3427     Error(ERR_WARN, "cannot save level file '%s'", filename);
3428     return;
3429   }
3430
3431   level->file_version = FILE_VERSION_ACTUAL;
3432   level->game_version = GAME_VERSION_ACTUAL;
3433
3434   /* check level field for 16-bit elements */
3435   level->encoding_16bit_field = FALSE;
3436   for (y = 0; y < level->fieldy; y++) 
3437     for (x = 0; x < level->fieldx; x++) 
3438       if (level->field[x][y] > 255)
3439         level->encoding_16bit_field = TRUE;
3440
3441   /* check yamyam content for 16-bit elements */
3442   level->encoding_16bit_yamyam = FALSE;
3443   for (i = 0; i < level->num_yamyam_contents; i++)
3444     for (y = 0; y < 3; y++)
3445       for (x = 0; x < 3; x++)
3446         if (level->yamyam_content[i][x][y] > 255)
3447           level->encoding_16bit_yamyam = TRUE;
3448
3449   /* check amoeba content for 16-bit elements */
3450   level->encoding_16bit_amoeba = FALSE;
3451   if (level->amoeba_content > 255)
3452     level->encoding_16bit_amoeba = TRUE;
3453
3454   /* calculate size of "BODY" chunk */
3455   body_chunk_size =
3456     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3457
3458   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3459   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3460
3461   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3462   SaveLevel_VERS(file, level);
3463
3464   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3465   SaveLevel_HEAD(file, level);
3466
3467   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3468   SaveLevel_AUTH(file, level);
3469
3470   putFileChunkBE(file, "BODY", body_chunk_size);
3471   SaveLevel_BODY(file, level);
3472
3473   if (level->encoding_16bit_yamyam ||
3474       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3475   {
3476     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3477     SaveLevel_CNT2(file, level, EL_YAMYAM);
3478   }
3479
3480   if (level->encoding_16bit_amoeba)
3481   {
3482     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3483     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3484   }
3485
3486   /* check for envelope content */
3487   for (i = 0; i < 4; i++)
3488   {
3489     if (strlen(level->envelope_text[i]) > 0)
3490     {
3491       int envelope_len = strlen(level->envelope_text[i]) + 1;
3492
3493       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3494       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3495     }
3496   }
3497
3498   /* check for non-default custom elements (unless using template level) */
3499   if (!level->use_custom_template)
3500   {
3501     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3502     {
3503       int element = EL_CUSTOM_START + i;
3504
3505       if (element_info[element].modified_settings)
3506       {
3507         int num_change_pages = element_info[element].num_change_pages;
3508
3509         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3510         SaveLevel_CUS4(file, level, element);
3511       }
3512     }
3513   }
3514
3515   /* check for non-default group elements (unless using template level) */
3516   if (!level->use_custom_template)
3517   {
3518     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3519     {
3520       int element = EL_GROUP_START + i;
3521
3522       if (element_info[element].modified_settings)
3523       {
3524         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3525         SaveLevel_GRP1(file, level, element);
3526       }
3527     }
3528   }
3529
3530   fclose(file);
3531
3532   SetFilePermissions(filename, PERMS_PRIVATE);
3533 }
3534
3535 void SaveLevel(int nr)
3536 {
3537   char *filename = getDefaultLevelFilename(nr);
3538
3539   SaveLevelFromFilename(&level, filename);
3540 }
3541
3542 void SaveLevelTemplate()
3543 {
3544   char *filename = getDefaultLevelFilename(-1);
3545
3546   SaveLevelFromFilename(&level, filename);
3547 }
3548
3549 void DumpLevel(struct LevelInfo *level)
3550 {
3551   if (level->no_valid_file)
3552   {
3553     Error(ERR_WARN, "cannot dump -- no valid level file found");
3554
3555     return;
3556   }
3557
3558   printf_line("-", 79);
3559   printf("Level xxx (file version %08d, game version %08d)\n",
3560          level->file_version, level->game_version);
3561   printf_line("-", 79);
3562
3563   printf("Level author: '%s'\n", level->author);
3564   printf("Level title:  '%s'\n", level->name);
3565   printf("\n");
3566   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3567   printf("\n");
3568   printf("Level time:  %d seconds\n", level->time);
3569   printf("Gems needed: %d\n", level->gems_needed);
3570   printf("\n");
3571   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3572   printf("Time for wheel:      %d seconds\n", level->time_wheel);
3573   printf("Time for light:      %d seconds\n", level->time_light);
3574   printf("Time for timegate:   %d seconds\n", level->time_timegate);
3575   printf("\n");
3576   printf("Amoeba speed: %d\n", level->amoeba_speed);
3577   printf("\n");
3578   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
3579   printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
3580   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
3581   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
3582   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3583   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3584   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3585
3586   printf_line("-", 79);
3587 }
3588
3589
3590 /* ========================================================================= */
3591 /* tape file functions                                                       */
3592 /* ========================================================================= */
3593
3594 static void setTapeInfoToDefaults()
3595 {
3596   int i;
3597
3598   /* always start with reliable default values (empty tape) */
3599   TapeErase();
3600
3601   /* default values (also for pre-1.2 tapes) with only the first player */
3602   tape.player_participates[0] = TRUE;
3603   for (i = 1; i < MAX_PLAYERS; i++)
3604     tape.player_participates[i] = FALSE;
3605
3606   /* at least one (default: the first) player participates in every tape */
3607   tape.num_participating_players = 1;
3608
3609   tape.level_nr = level_nr;
3610   tape.counter = 0;
3611   tape.changed = FALSE;
3612
3613   tape.recording = FALSE;
3614   tape.playing = FALSE;
3615   tape.pausing = FALSE;
3616
3617   tape.no_valid_file = FALSE;
3618 }
3619
3620 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3621 {
3622   tape->file_version = getFileVersion(file);
3623   tape->game_version = getFileVersion(file);
3624
3625   return chunk_size;
3626 }
3627
3628 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3629 {
3630   int i;
3631
3632   tape->random_seed = getFile32BitBE(file);
3633   tape->date        = getFile32BitBE(file);
3634   tape->length      = getFile32BitBE(file);
3635
3636   /* read header fields that are new since version 1.2 */
3637   if (tape->file_version >= FILE_VERSION_1_2)
3638   {
3639     byte store_participating_players = getFile8Bit(file);
3640     int engine_version;
3641
3642     /* since version 1.2, tapes store which players participate in the tape */
3643     tape->num_participating_players = 0;
3644     for (i = 0; i < MAX_PLAYERS; i++)
3645     {
3646       tape->player_participates[i] = FALSE;
3647
3648       if (store_participating_players & (1 << i))
3649       {
3650         tape->player_participates[i] = TRUE;
3651         tape->num_participating_players++;
3652       }
3653     }
3654
3655     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3656
3657     engine_version = getFileVersion(file);
3658     if (engine_version > 0)
3659       tape->engine_version = engine_version;
3660     else
3661       tape->engine_version = tape->game_version;
3662   }
3663
3664   return chunk_size;
3665 }
3666
3667 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3668 {
3669   int level_identifier_size;
3670   int i;
3671
3672   level_identifier_size = getFile16BitBE(file);
3673
3674   tape->level_identifier =
3675     checked_realloc(tape->level_identifier, level_identifier_size);
3676
3677   for (i = 0; i < level_identifier_size; i++)
3678     tape->level_identifier[i] = getFile8Bit(file);
3679
3680   tape->level_nr = getFile16BitBE(file);
3681
3682   chunk_size = 2 + level_identifier_size + 2;
3683
3684   return chunk_size;
3685 }
3686
3687 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3688 {
3689   int i, j;
3690   int chunk_size_expected =
3691     (tape->num_participating_players + 1) * tape->length;
3692
3693   if (chunk_size_expected != chunk_size)
3694   {
3695     ReadUnusedBytesFromFile(file, chunk_size);
3696     return chunk_size_expected;
3697   }
3698
3699   for (i = 0; i < tape->length; i++)
3700   {
3701     if (i >= MAX_TAPE_LEN)
3702       break;
3703
3704     for (j = 0; j < MAX_PLAYERS; j++)
3705     {
3706       tape->pos[i].action[j] = MV_NO_MOVING;
3707
3708       if (tape->player_participates[j])
3709         tape->pos[i].action[j] = getFile8Bit(file);
3710     }
3711
3712     tape->pos[i].delay = getFile8Bit(file);
3713
3714     if (tape->file_version == FILE_VERSION_1_0)
3715     {
3716       /* eliminate possible diagonal moves in old tapes */
3717       /* this is only for backward compatibility */
3718
3719       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3720       byte action = tape->pos[i].action[0];
3721       int k, num_moves = 0;
3722
3723       for (k = 0; k<4; k++)
3724       {
3725         if (action & joy_dir[k])
3726         {
3727           tape->pos[i + num_moves].action[0] = joy_dir[k];
3728           if (num_moves > 0)
3729             tape->pos[i + num_moves].delay = 0;
3730           num_moves++;
3731         }
3732       }
3733
3734       if (num_moves > 1)
3735       {
3736         num_moves--;
3737         i += num_moves;
3738         tape->length += num_moves;
3739       }
3740     }
3741     else if (tape->file_version < FILE_VERSION_2_0)
3742     {
3743       /* convert pre-2.0 tapes to new tape format */
3744
3745       if (tape->pos[i].delay > 1)
3746       {
3747         /* action part */
3748         tape->pos[i + 1] = tape->pos[i];
3749         tape->pos[i + 1].delay = 1;
3750
3751         /* delay part */
3752         for (j = 0; j < MAX_PLAYERS; j++)
3753           tape->pos[i].action[j] = MV_NO_MOVING;
3754         tape->pos[i].delay--;
3755
3756         i++;
3757         tape->length++;
3758       }
3759     }
3760
3761     if (feof(file))
3762       break;
3763   }
3764
3765   if (i != tape->length)
3766     chunk_size = (tape->num_participating_players + 1) * i;
3767
3768   return chunk_size;
3769 }
3770
3771 void LoadTapeFromFilename(char *filename)
3772 {
3773   char cookie[MAX_LINE_LEN];
3774   char chunk_name[CHUNK_ID_LEN + 1];
3775   FILE *file;
3776   int chunk_size;
3777
3778   /* always start with reliable default values */
3779   setTapeInfoToDefaults();
3780
3781   if (!(file = fopen(filename, MODE_READ)))
3782   {
3783     tape.no_valid_file = TRUE;
3784
3785 #if 0
3786     Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
3787 #endif
3788
3789     return;
3790   }
3791
3792   getFileChunkBE(file, chunk_name, NULL);
3793   if (strcmp(chunk_name, "RND1") == 0)
3794   {
3795     getFile32BitBE(file);               /* not used */
3796
3797     getFileChunkBE(file, chunk_name, NULL);
3798     if (strcmp(chunk_name, "TAPE") != 0)
3799     {
3800       tape.no_valid_file = TRUE;
3801
3802       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3803       fclose(file);
3804       return;
3805     }
3806   }
3807   else  /* check for pre-2.0 file format with cookie string */
3808   {
3809     strcpy(cookie, chunk_name);
3810     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3811     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3812       cookie[strlen(cookie) - 1] = '\0';
3813
3814     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3815     {
3816       tape.no_valid_file = TRUE;
3817
3818       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3819       fclose(file);
3820       return;
3821     }
3822
3823     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3824     {
3825       tape.no_valid_file = TRUE;
3826
3827       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3828       fclose(file);
3829       return;
3830     }
3831
3832     /* pre-2.0 tape files have no game version, so use file version here */
3833     tape.game_version = tape.file_version;
3834   }
3835
3836   if (tape.file_version < FILE_VERSION_1_2)
3837   {
3838     /* tape files from versions before 1.2.0 without chunk structure */
3839     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3840     LoadTape_BODY(file, 2 * tape.length,  &tape);
3841   }
3842   else
3843   {
3844     static struct
3845     {
3846       char *name;
3847       int size;
3848       int (*loader)(FILE *, int, struct TapeInfo *);
3849     }
3850     chunk_info[] =
3851     {
3852       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
3853       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
3854       { "INFO", -1,                     LoadTape_INFO },
3855       { "BODY", -1,                     LoadTape_BODY },
3856       {  NULL,  0,                      NULL }
3857     };
3858
3859     while (getFileChunkBE(file, chunk_name, &chunk_size))
3860     {
3861       int i = 0;
3862
3863       while (chunk_info[i].name != NULL &&
3864              strcmp(chunk_name, chunk_info[i].name) != 0)
3865         i++;
3866
3867       if (chunk_info[i].name == NULL)
3868       {
3869         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3870               chunk_name, filename);
3871         ReadUnusedBytesFromFile(file, chunk_size);
3872       }
3873       else if (chunk_info[i].size != -1 &&
3874                chunk_info[i].size != chunk_size)
3875       {
3876         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3877               chunk_size, chunk_name, filename);
3878         ReadUnusedBytesFromFile(file, chunk_size);
3879       }
3880       else
3881       {
3882         /* call function to load this tape chunk */
3883         int chunk_size_expected =
3884           (chunk_info[i].loader)(file, chunk_size, &tape);
3885
3886         /* the size of some chunks cannot be checked before reading other
3887            chunks first (like "HEAD" and "BODY") that contain some header
3888            information, so check them here */
3889         if (chunk_size_expected != chunk_size)
3890         {
3891           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3892                 chunk_size, chunk_name, filename);
3893         }
3894       }
3895     }
3896   }
3897
3898   fclose(file);
3899
3900   tape.length_seconds = GetTapeLength();
3901
3902 #if 0
3903   printf("::: tape game version: %d\n", tape.game_version);
3904   printf("::: tape engine version: %d\n", tape.engine_version);
3905 #endif
3906 }
3907
3908 void LoadTape(int nr)
3909 {
3910   char *filename = getTapeFilename(nr);
3911
3912   LoadTapeFromFilename(filename);
3913 }
3914
3915 void LoadSolutionTape(int nr)
3916 {
3917   char *filename = getSolutionTapeFilename(nr);
3918
3919   LoadTapeFromFilename(filename);
3920 }
3921
3922 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3923 {
3924   putFileVersion(file, tape->file_version);
3925   putFileVersion(file, tape->game_version);
3926 }
3927
3928 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3929 {
3930   int i;
3931   byte store_participating_players = 0;
3932
3933   /* set bits for participating players for compact storage */
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935     if (tape->player_participates[i])
3936       store_participating_players |= (1 << i);
3937
3938   putFile32BitBE(file, tape->random_seed);
3939   putFile32BitBE(file, tape->date);
3940   putFile32BitBE(file, tape->length);
3941
3942   putFile8Bit(file, store_participating_players);
3943
3944   /* unused bytes not at the end here for 4-byte alignment of engine_version */
3945   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3946
3947   putFileVersion(file, tape->engine_version);
3948 }
3949
3950 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3951 {
3952   int level_identifier_size = strlen(tape->level_identifier) + 1;
3953   int i;
3954
3955   putFile16BitBE(file, level_identifier_size);
3956
3957   for (i = 0; i < level_identifier_size; i++)
3958     putFile8Bit(file, tape->level_identifier[i]);
3959
3960   putFile16BitBE(file, tape->level_nr);
3961 }
3962
3963 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3964 {
3965   int i, j;
3966
3967   for (i = 0; i < tape->length; i++)
3968   {
3969     for (j = 0; j < MAX_PLAYERS; j++)
3970       if (tape->player_participates[j])
3971         putFile8Bit(file, tape->pos[i].action[j]);
3972
3973     putFile8Bit(file, tape->pos[i].delay);
3974   }
3975 }
3976
3977 void SaveTape(int nr)
3978 {
3979   char *filename = getTapeFilename(nr);
3980   FILE *file;
3981   boolean new_tape = TRUE;
3982   int num_participating_players = 0;
3983   int info_chunk_size;
3984   int body_chunk_size;
3985   int i;
3986
3987   InitTapeDirectory(leveldir_current->subdir);
3988
3989   /* if a tape still exists, ask to overwrite it */
3990   if (fileExists(filename))
3991   {
3992     new_tape = FALSE;
3993     if (!Request("Replace old tape ?", REQ_ASK))
3994       return;
3995   }
3996
3997   if (!(file = fopen(filename, MODE_WRITE)))
3998   {
3999     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
4000     return;
4001   }
4002
4003   tape.file_version = FILE_VERSION_ACTUAL;
4004   tape.game_version = GAME_VERSION_ACTUAL;
4005
4006   /* count number of participating players  */
4007   for (i = 0; i < MAX_PLAYERS; i++)
4008     if (tape.player_participates[i])
4009       num_participating_players++;
4010
4011   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
4012   body_chunk_size = (num_participating_players + 1) * tape.length;
4013
4014   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
4015   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
4016
4017   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
4018   SaveTape_VERS(file, &tape);
4019
4020   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
4021   SaveTape_HEAD(file, &tape);
4022
4023   putFileChunkBE(file, "INFO", info_chunk_size);
4024   SaveTape_INFO(file, &tape);
4025
4026   putFileChunkBE(file, "BODY", body_chunk_size);
4027   SaveTape_BODY(file, &tape);
4028
4029   fclose(file);
4030
4031   SetFilePermissions(filename, PERMS_PRIVATE);
4032
4033   tape.changed = FALSE;
4034
4035   if (new_tape)
4036     Request("Tape saved !", REQ_CONFIRM);
4037 }
4038
4039 void DumpTape(struct TapeInfo *tape)
4040 {
4041   int i, j;
4042
4043 #if 1
4044   if (tape->no_valid_file)
4045   {
4046     Error(ERR_WARN, "cannot dump -- no valid tape file found");
4047
4048     return;
4049   }
4050 #else
4051   if (TAPE_IS_EMPTY(*tape))
4052   {
4053     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
4054
4055     return;
4056   }
4057 #endif
4058
4059   printf_line("-", 79);
4060   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
4061          tape->level_nr, tape->file_version, tape->game_version);
4062   printf("                  (effective engine version %08d)\n",
4063          tape->engine_version);
4064   printf("Level series identifier: '%s'\n", tape->level_identifier);
4065   printf_line("-", 79);
4066
4067   for (i = 0; i < tape->length; i++)
4068   {
4069     if (i >= MAX_TAPE_LEN)
4070       break;
4071
4072     printf("%03d: ", i);
4073
4074     for (j = 0; j < MAX_PLAYERS; j++)
4075     {
4076       if (tape->player_participates[j])
4077       {
4078         int action = tape->pos[i].action[j];
4079
4080         printf("%d:%02x ", j, action);
4081         printf("[%c%c%c%c|%c%c] - ",
4082                (action & JOY_LEFT ? '<' : ' '),
4083                (action & JOY_RIGHT ? '>' : ' '),
4084                (action & JOY_UP ? '^' : ' '),
4085                (action & JOY_DOWN ? 'v' : ' '),
4086                (action & JOY_BUTTON_1 ? '1' : ' '),
4087                (action & JOY_BUTTON_2 ? '2' : ' '));
4088       }
4089     }
4090
4091     printf("(%03d)\n", tape->pos[i].delay);
4092   }
4093
4094   printf_line("-", 79);
4095 }
4096
4097
4098 /* ========================================================================= */
4099 /* score file functions                                                      */
4100 /* ========================================================================= */
4101
4102 void LoadScore(int nr)
4103 {
4104   int i;
4105   char *filename = getScoreFilename(nr);
4106   char cookie[MAX_LINE_LEN];
4107   char line[MAX_LINE_LEN];
4108   char *line_ptr;
4109   FILE *file;
4110
4111   /* always start with reliable default values */
4112   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4113   {
4114     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
4115     highscore[i].Score = 0;
4116   }
4117
4118   if (!(file = fopen(filename, MODE_READ)))
4119     return;
4120
4121   /* check file identifier */
4122   fgets(cookie, MAX_LINE_LEN, file);
4123   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4124     cookie[strlen(cookie) - 1] = '\0';
4125
4126   if (!checkCookieString(cookie, SCORE_COOKIE))
4127   {
4128     Error(ERR_WARN, "unknown format of score file '%s'", filename);
4129     fclose(file);
4130     return;
4131   }
4132
4133   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4134   {
4135     fscanf(file, "%d", &highscore[i].Score);
4136     fgets(line, MAX_LINE_LEN, file);
4137
4138     if (line[strlen(line) - 1] == '\n')
4139       line[strlen(line) - 1] = '\0';
4140
4141     for (line_ptr = line; *line_ptr; line_ptr++)
4142     {
4143       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4144       {
4145         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
4146         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
4147         break;
4148       }
4149     }
4150   }
4151
4152   fclose(file);
4153 }
4154
4155 void SaveScore(int nr)
4156 {
4157   int i;
4158   char *filename = getScoreFilename(nr);
4159   FILE *file;
4160
4161   InitScoreDirectory(leveldir_current->subdir);
4162
4163   if (!(file = fopen(filename, MODE_WRITE)))
4164   {
4165     Error(ERR_WARN, "cannot save score for level %d", nr);
4166     return;
4167   }
4168
4169   fprintf(file, "%s\n\n", SCORE_COOKIE);
4170
4171   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
4172     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
4173
4174   fclose(file);
4175
4176   SetFilePermissions(filename, PERMS_PUBLIC);
4177 }
4178
4179
4180 /* ========================================================================= */
4181 /* setup file functions                                                      */
4182 /* ========================================================================= */
4183
4184 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
4185
4186 /* global setup */
4187 #define SETUP_TOKEN_PLAYER_NAME                 0
4188 #define SETUP_TOKEN_SOUND                       1
4189 #define SETUP_TOKEN_SOUND_LOOPS                 2
4190 #define SETUP_TOKEN_SOUND_MUSIC                 3
4191 #define SETUP_TOKEN_SOUND_SIMPLE                4
4192 #define SETUP_TOKEN_TOONS                       5
4193 #define SETUP_TOKEN_SCROLL_DELAY                6
4194 #define SETUP_TOKEN_SOFT_SCROLLING              7
4195 #define SETUP_TOKEN_FADING                      8
4196 #define SETUP_TOKEN_AUTORECORD                  9
4197 #define SETUP_TOKEN_QUICK_DOORS                 10
4198 #define SETUP_TOKEN_TEAM_MODE                   11
4199 #define SETUP_TOKEN_HANDICAP                    12
4200 #define SETUP_TOKEN_SKIP_LEVELS                 13
4201 #define SETUP_TOKEN_TIME_LIMIT                  14
4202 #define SETUP_TOKEN_FULLSCREEN                  15
4203 #define SETUP_TOKEN_ASK_ON_ESCAPE               16
4204 #define SETUP_TOKEN_GRAPHICS_SET                17
4205 #define SETUP_TOKEN_SOUNDS_SET                  18
4206 #define SETUP_TOKEN_MUSIC_SET                   19
4207 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     20
4208 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       21
4209 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        22
4210
4211 #define NUM_GLOBAL_SETUP_TOKENS                 23
4212
4213 /* editor setup */
4214 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
4215 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
4216 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
4217 #define SETUP_TOKEN_EDITOR_EL_MORE              3
4218 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           4
4219 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          5
4220 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     6
4221 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    7
4222 #define SETUP_TOKEN_EDITOR_EL_CHARS             8
4223 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            9
4224 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       10
4225 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         11
4226 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      12
4227
4228 #define NUM_EDITOR_SETUP_TOKENS                 13
4229
4230 /* shortcut setup */
4231 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
4232 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
4233 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
4234
4235 #define NUM_SHORTCUT_SETUP_TOKENS               3
4236
4237 /* player setup */
4238 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
4239 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
4240 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
4241 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
4242 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
4243 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
4244 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
4245 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
4246 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
4247 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
4248 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
4249 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
4250 #define SETUP_TOKEN_PLAYER_KEY_UP               12
4251 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
4252 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
4253 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
4254
4255 #define NUM_PLAYER_SETUP_TOKENS                 16
4256
4257 /* system setup */
4258 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
4259 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
4260
4261 #define NUM_SYSTEM_SETUP_TOKENS                 2
4262
4263 /* options setup */
4264 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
4265
4266 #define NUM_OPTIONS_SETUP_TOKENS                1
4267
4268
4269 static struct SetupInfo si;
4270 static struct SetupEditorInfo sei;
4271 static struct SetupShortcutInfo ssi;
4272 static struct SetupInputInfo sii;
4273 static struct SetupSystemInfo syi;
4274 static struct OptionInfo soi;
4275
4276 static struct TokenInfo global_setup_tokens[] =
4277 {
4278   { TYPE_STRING, &si.player_name,       "player_name"                   },
4279   { TYPE_SWITCH, &si.sound,             "sound"                         },
4280   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
4281   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
4282   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
4283   { TYPE_SWITCH, &si.toons,             "toons"                         },
4284   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
4285   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
4286   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
4287   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
4288   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
4289   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
4290   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
4291   { TYPE_SWITCH, &si.skip_levels,       "skip_levels"                   },
4292   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
4293   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
4294   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
4295   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
4296   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
4297   { TYPE_STRING, &si.music_set,         "music_set"                     },
4298   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
4299   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
4300   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
4301 };
4302
4303 static struct TokenInfo editor_setup_tokens[] =
4304 {
4305   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
4306   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
4307   { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
4308   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
4309   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
4310   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
4311   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
4312   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
4313   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
4314   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
4315   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
4316   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
4317   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
4318 };
4319
4320 static struct TokenInfo shortcut_setup_tokens[] =
4321 {
4322   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
4323   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
4324   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
4325 };
4326
4327 static struct TokenInfo player_setup_tokens[] =
4328 {
4329   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
4330   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
4331   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
4332   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
4333   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
4334   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
4335   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
4336   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
4337   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
4338   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
4339   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
4340   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
4341   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
4342   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
4343   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
4344   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
4345 };
4346
4347 static struct TokenInfo system_setup_tokens[] =
4348 {
4349   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
4350   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
4351 };
4352
4353 static struct TokenInfo options_setup_tokens[] =
4354 {
4355   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
4356 };
4357
4358 static char *get_corrected_login_name(char *login_name)
4359 {
4360   /* needed because player name must be a fixed length string */
4361   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
4362
4363   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
4364   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
4365
4366   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
4367     if (strchr(login_name_new, ' '))
4368       *strchr(login_name_new, ' ') = '\0';
4369
4370   return login_name_new;
4371 }
4372
4373 static void setSetupInfoToDefaults(struct SetupInfo *si)
4374 {
4375   int i;
4376
4377   si->player_name = get_corrected_login_name(getLoginName());
4378
4379   si->sound = TRUE;
4380   si->sound_loops = TRUE;
4381   si->sound_music = TRUE;
4382   si->sound_simple = TRUE;
4383   si->toons = TRUE;
4384   si->double_buffering = TRUE;
4385   si->direct_draw = !si->double_buffering;
4386   si->scroll_delay = TRUE;
4387   si->soft_scrolling = TRUE;
4388   si->fading = FALSE;
4389   si->autorecord = TRUE;
4390   si->quick_doors = FALSE;
4391   si->team_mode = FALSE;
4392   si->handicap = TRUE;
4393   si->skip_levels = TRUE;
4394   si->time_limit = TRUE;
4395   si->fullscreen = FALSE;
4396   si->ask_on_escape = TRUE;
4397
4398   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
4399   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
4400   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
4401   si->override_level_graphics = FALSE;
4402   si->override_level_sounds = FALSE;
4403   si->override_level_music = FALSE;
4404
4405   si->editor.el_boulderdash       = TRUE;
4406   si->editor.el_emerald_mine      = TRUE;
4407   si->editor.el_emerald_mine_club = TRUE;
4408   si->editor.el_more              = TRUE;
4409   si->editor.el_sokoban           = TRUE;
4410   si->editor.el_supaplex          = TRUE;
4411   si->editor.el_diamond_caves     = TRUE;
4412   si->editor.el_dx_boulderdash    = TRUE;
4413   si->editor.el_chars             = TRUE;
4414   si->editor.el_custom            = TRUE;
4415   si->editor.el_custom_more       = FALSE;
4416
4417   si->editor.el_headlines = TRUE;
4418   si->editor.el_user_defined = FALSE;
4419
4420   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4421   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4422   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4423
4424   for (i = 0; i < MAX_PLAYERS; i++)
4425   {
4426     si->input[i].use_joystick = FALSE;
4427     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4428     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
4429     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4430     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
4431     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
4432     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4433     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
4434     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
4435     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
4436     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
4437     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4438     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
4439     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
4440     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
4441     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
4442   }
4443
4444   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4445   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4446
4447   si->options.verbose = FALSE;
4448 }
4449
4450 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4451 {
4452   int i, pnr;
4453
4454   if (!setup_file_hash)
4455     return;
4456
4457   /* global setup */
4458   si = setup;
4459   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4460     setSetupInfo(global_setup_tokens, i,
4461                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4462   setup = si;
4463
4464   /* editor setup */
4465   sei = setup.editor;
4466   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4467     setSetupInfo(editor_setup_tokens, i,
4468                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4469   setup.editor = sei;
4470
4471   /* shortcut setup */
4472   ssi = setup.shortcut;
4473   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4474     setSetupInfo(shortcut_setup_tokens, i,
4475                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4476   setup.shortcut = ssi;
4477
4478   /* player setup */
4479   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4480   {
4481     char prefix[30];
4482
4483     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4484
4485     sii = setup.input[pnr];
4486     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4487     {
4488       char full_token[100];
4489
4490       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4491       setSetupInfo(player_setup_tokens, i,
4492                    getHashEntry(setup_file_hash, full_token));
4493     }
4494     setup.input[pnr] = sii;
4495   }
4496
4497   /* system setup */
4498   syi = setup.system;
4499   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4500     setSetupInfo(system_setup_tokens, i,
4501                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4502   setup.system = syi;
4503
4504   /* options setup */
4505   soi = setup.options;
4506   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4507     setSetupInfo(options_setup_tokens, i,
4508                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4509   setup.options = soi;
4510 }
4511
4512 void LoadSetup()
4513 {
4514   char *filename = getSetupFilename();
4515   SetupFileHash *setup_file_hash = NULL;
4516
4517   /* always start with reliable default values */
4518   setSetupInfoToDefaults(&setup);
4519
4520   setup_file_hash = loadSetupFileHash(filename);
4521
4522   if (setup_file_hash)
4523   {
4524     char *player_name_new;
4525
4526     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4527     decodeSetupFileHash(setup_file_hash);
4528
4529     setup.direct_draw = !setup.double_buffering;
4530
4531     freeSetupFileHash(setup_file_hash);
4532
4533     /* needed to work around problems with fixed length strings */
4534     player_name_new = get_corrected_login_name(setup.player_name);
4535     free(setup.player_name);
4536     setup.player_name = player_name_new;
4537   }
4538   else
4539     Error(ERR_WARN, "using default setup values");
4540 }
4541
4542 void SaveSetup()
4543 {
4544   char *filename = getSetupFilename();
4545   FILE *file;
4546   int i, pnr;
4547
4548   InitUserDataDirectory();
4549
4550   if (!(file = fopen(filename, MODE_WRITE)))
4551   {
4552     Error(ERR_WARN, "cannot write setup file '%s'", filename);
4553     return;
4554   }
4555
4556   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4557                                                getCookie("SETUP")));
4558   fprintf(file, "\n");
4559
4560   /* global setup */
4561   si = setup;
4562   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4563   {
4564     /* just to make things nicer :) */
4565     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4566         i == SETUP_TOKEN_GRAPHICS_SET)
4567       fprintf(file, "\n");
4568
4569     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4570   }
4571
4572   /* editor setup */
4573   sei = setup.editor;
4574   fprintf(file, "\n");
4575   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4576     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4577
4578   /* shortcut setup */
4579   ssi = setup.shortcut;
4580   fprintf(file, "\n");
4581   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4582     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4583
4584   /* player setup */
4585   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4586   {
4587     char prefix[30];
4588
4589     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4590     fprintf(file, "\n");
4591
4592     sii = setup.input[pnr];
4593     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4594       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4595   }
4596
4597   /* system setup */
4598   syi = setup.system;
4599   fprintf(file, "\n");
4600   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4601     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4602
4603   /* options setup */
4604   soi = setup.options;
4605   fprintf(file, "\n");
4606   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4607     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4608
4609   fclose(file);
4610
4611   SetFilePermissions(filename, PERMS_PRIVATE);
4612 }
4613
4614 void LoadCustomElementDescriptions()
4615 {
4616   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4617   SetupFileHash *setup_file_hash;
4618   int i;
4619
4620   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4621   {
4622     if (element_info[i].custom_description != NULL)
4623     {
4624       free(element_info[i].custom_description);
4625       element_info[i].custom_description = NULL;
4626     }
4627   }
4628
4629   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4630     return;
4631
4632   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4633   {
4634     char *token = getStringCat2(element_info[i].token_name, ".name");
4635     char *value = getHashEntry(setup_file_hash, token);
4636
4637     if (value != NULL)
4638       element_info[i].custom_description = getStringCopy(value);
4639
4640     free(token);
4641   }
4642
4643   freeSetupFileHash(setup_file_hash);
4644 }
4645
4646 void LoadSpecialMenuDesignSettings()
4647 {
4648   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4649   SetupFileHash *setup_file_hash;
4650   int i, j;
4651
4652   /* always start with reliable default values from default config */
4653   for (i = 0; image_config_vars[i].token != NULL; i++)
4654     for (j = 0; image_config[j].token != NULL; j++)
4655       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4656         *image_config_vars[i].value =
4657           get_auto_parameter_value(image_config_vars[i].token,
4658                                    image_config[j].value);
4659
4660   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4661     return;
4662
4663   /* special case: initialize with default values that may be overwritten */
4664   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4665   {
4666     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4667     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4668     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4669
4670     if (value_x != NULL)
4671       menu.draw_xoffset[i] = get_integer_from_string(value_x);
4672     if (value_y != NULL)
4673       menu.draw_yoffset[i] = get_integer_from_string(value_y);
4674     if (list_size != NULL)
4675       menu.list_size[i] = get_integer_from_string(list_size);
4676   }
4677
4678   /* read (and overwrite with) values that may be specified in config file */
4679   for (i = 0; image_config_vars[i].token != NULL; i++)
4680   {
4681     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4682
4683     if (value != NULL)
4684       *image_config_vars[i].value =
4685         get_auto_parameter_value(image_config_vars[i].token, value);
4686   }
4687
4688   freeSetupFileHash(setup_file_hash);
4689 }
4690
4691 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4692 {
4693   char *filename = getEditorSetupFilename();
4694   SetupFileList *setup_file_list, *list;
4695   SetupFileHash *element_hash;
4696   int num_unknown_tokens = 0;
4697   int i;
4698
4699   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4700     return;
4701
4702   element_hash = newSetupFileHash();
4703
4704   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4705     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4706
4707   /* determined size may be larger than needed (due to unknown elements) */
4708   *num_elements = 0;
4709   for (list = setup_file_list; list != NULL; list = list->next)
4710     (*num_elements)++;
4711
4712   /* add space for up to 3 more elements for padding that may be needed */
4713   *num_elements += 3;
4714
4715   *elements = checked_malloc(*num_elements * sizeof(int));
4716
4717   *num_elements = 0;
4718   for (list = setup_file_list; list != NULL; list = list->next)
4719   {
4720     char *value = getHashEntry(element_hash, list->token);
4721
4722     if (value == NULL)          /* try to find obsolete token mapping */
4723     {
4724       char *mapped_token = get_mapped_token(list->token);
4725
4726       if (mapped_token != NULL)
4727       {
4728         value = getHashEntry(element_hash, mapped_token);
4729
4730         free(mapped_token);
4731       }
4732     }
4733
4734     if (value != NULL)
4735     {
4736       (*elements)[(*num_elements)++] = atoi(value);
4737     }
4738     else
4739     {
4740       if (num_unknown_tokens == 0)
4741       {
4742         Error(ERR_RETURN_LINE, "-");
4743         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4744         Error(ERR_RETURN, "- config file: '%s'", filename);
4745
4746         num_unknown_tokens++;
4747       }
4748
4749       Error(ERR_RETURN, "- token: '%s'", list->token);
4750     }
4751   }
4752
4753   if (num_unknown_tokens > 0)
4754     Error(ERR_RETURN_LINE, "-");
4755
4756   while (*num_elements % 4)     /* pad with empty elements, if needed */
4757     (*elements)[(*num_elements)++] = EL_EMPTY;
4758
4759   freeSetupFileList(setup_file_list);
4760   freeSetupFileHash(element_hash);
4761
4762 #if 0
4763   /* TEST-ONLY */
4764   for (i = 0; i < *num_elements; i++)
4765     printf("editor: element '%s' [%d]\n",
4766            element_info[(*elements)[i]].token_name, (*elements)[i]);
4767 #endif
4768 }
4769
4770 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4771                                                      boolean is_sound)
4772 {
4773   SetupFileHash *setup_file_hash = NULL;
4774   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4775   char *filename_music, *filename_prefix, *filename_info;
4776   struct
4777   {
4778     char *token;
4779     char **value_ptr;
4780   }
4781   token_to_value_ptr[] =
4782   {
4783     { "title_header",   &tmp_music_file_info.title_header       },
4784     { "artist_header",  &tmp_music_file_info.artist_header      },
4785     { "album_header",   &tmp_music_file_info.album_header       },
4786     { "year_header",    &tmp_music_file_info.year_header        },
4787
4788     { "title",          &tmp_music_file_info.title              },
4789     { "artist",         &tmp_music_file_info.artist             },
4790     { "album",          &tmp_music_file_info.album              },
4791     { "year",           &tmp_music_file_info.year               },
4792
4793     { NULL,             NULL                                    },
4794   };
4795   int i;
4796
4797   filename_music = (is_sound ? getCustomSoundFilename(basename) :
4798                     getCustomMusicFilename(basename));
4799
4800   if (filename_music == NULL)
4801     return NULL;
4802
4803   /* ---------- try to replace file extension ---------- */
4804
4805   filename_prefix = getStringCopy(filename_music);
4806   if (strrchr(filename_prefix, '.') != NULL)
4807     *strrchr(filename_prefix, '.') = '\0';
4808   filename_info = getStringCat2(filename_prefix, ".txt");
4809
4810 #if 0
4811   printf("trying to load file '%s'...\n", filename_info);
4812 #endif
4813
4814   if (fileExists(filename_info))
4815     setup_file_hash = loadSetupFileHash(filename_info);
4816
4817   free(filename_prefix);
4818   free(filename_info);
4819
4820   if (setup_file_hash == NULL)
4821   {
4822     /* ---------- try to add file extension ---------- */
4823
4824     filename_prefix = getStringCopy(filename_music);
4825     filename_info = getStringCat2(filename_prefix, ".txt");
4826
4827 #if 0
4828     printf("trying to load file '%s'...\n", filename_info);
4829 #endif
4830
4831     if (fileExists(filename_info))
4832       setup_file_hash = loadSetupFileHash(filename_info);
4833
4834     free(filename_prefix);
4835     free(filename_info);
4836   }
4837
4838   if (setup_file_hash == NULL)
4839     return NULL;
4840
4841   /* ---------- music file info found ---------- */
4842
4843   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4844
4845   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4846   {
4847     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4848
4849     *token_to_value_ptr[i].value_ptr =
4850       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4851   }
4852
4853   tmp_music_file_info.basename = getStringCopy(basename);
4854   tmp_music_file_info.music = music;
4855   tmp_music_file_info.is_sound = is_sound;
4856
4857   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4858   *new_music_file_info = tmp_music_file_info;
4859
4860   return new_music_file_info;
4861 }
4862
4863 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4864 {
4865   return get_music_file_info_ext(basename, music, FALSE);
4866 }
4867
4868 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4869 {
4870   return get_music_file_info_ext(basename, sound, TRUE);
4871 }
4872
4873 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4874                                      char *basename, boolean is_sound)
4875 {
4876   for (; list != NULL; list = list->next)
4877     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4878       return TRUE;
4879
4880   return FALSE;
4881 }
4882
4883 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4884 {
4885   return music_info_listed_ext(list, basename, FALSE);
4886 }
4887
4888 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4889 {
4890   return music_info_listed_ext(list, basename, TRUE);
4891 }
4892
4893 void LoadMusicInfo()
4894 {
4895   char *music_directory = getCustomMusicDirectory();
4896   int num_music = getMusicListSize();
4897   int num_music_noconf = 0;
4898   int num_sounds = getSoundListSize();
4899   DIR *dir;
4900   struct dirent *dir_entry;
4901   struct FileInfo *music, *sound;
4902   struct MusicFileInfo *next, **new;
4903   int i;
4904
4905   while (music_file_info != NULL)
4906   {
4907     next = music_file_info->next;
4908
4909     checked_free(music_file_info->basename);
4910
4911     checked_free(music_file_info->title_header);
4912     checked_free(music_file_info->artist_header);
4913     checked_free(music_file_info->album_header);
4914     checked_free(music_file_info->year_header);
4915
4916     checked_free(music_file_info->title);
4917     checked_free(music_file_info->artist);
4918     checked_free(music_file_info->album);
4919     checked_free(music_file_info->year);
4920
4921     free(music_file_info);
4922
4923     music_file_info = next;
4924   }
4925
4926   new = &music_file_info;
4927
4928 #if 0
4929   printf("::: num_music == %d\n", num_music);
4930 #endif
4931
4932   for (i = 0; i < num_music; i++)
4933   {
4934     music = getMusicListEntry(i);
4935
4936 #if 0
4937     printf("::: %d [%08x]\n", i, music->filename);
4938 #endif
4939
4940     if (music->filename == NULL)
4941       continue;
4942
4943     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4944       continue;
4945
4946     /* a configured file may be not recognized as music */
4947     if (!FileIsMusic(music->filename))
4948       continue;
4949
4950 #if 0
4951     printf("::: -> '%s' (configured)\n", music->filename);
4952 #endif
4953
4954     if (!music_info_listed(music_file_info, music->filename))
4955     {
4956       *new = get_music_file_info(music->filename, i);
4957       if (*new != NULL)
4958         new = &(*new)->next;
4959     }
4960   }
4961
4962   if ((dir = opendir(music_directory)) == NULL)
4963   {
4964     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4965     return;
4966   }
4967
4968   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
4969   {
4970     char *basename = dir_entry->d_name;
4971     boolean music_already_used = FALSE;
4972     int i;
4973
4974     /* skip all music files that are configured in music config file */
4975     for (i = 0; i < num_music; i++)
4976     {
4977       music = getMusicListEntry(i);
4978
4979       if (music->filename == NULL)
4980         continue;
4981
4982       if (strcmp(basename, music->filename) == 0)
4983       {
4984         music_already_used = TRUE;
4985         break;
4986       }
4987     }
4988
4989     if (music_already_used)
4990       continue;
4991
4992     if (!FileIsMusic(basename))
4993       continue;
4994
4995 #if 0
4996     printf("::: -> '%s' (found in directory)\n", basename);
4997 #endif
4998
4999     if (!music_info_listed(music_file_info, basename))
5000     {
5001       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
5002       if (*new != NULL)
5003         new = &(*new)->next;
5004     }
5005
5006     num_music_noconf++;
5007   }
5008
5009   closedir(dir);
5010
5011   for (i = 0; i < num_sounds; i++)
5012   {
5013     sound = getSoundListEntry(i);
5014
5015     if (sound->filename == NULL)
5016       continue;
5017
5018     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
5019       continue;
5020
5021     /* a configured file may be not recognized as sound */
5022     if (!FileIsSound(sound->filename))
5023       continue;
5024
5025 #if 0
5026     printf("::: -> '%s' (configured)\n", sound->filename);
5027 #endif
5028
5029     if (!sound_info_listed(music_file_info, sound->filename))
5030     {
5031       *new = get_sound_file_info(sound->filename, i);
5032       if (*new != NULL)
5033         new = &(*new)->next;
5034     }
5035   }
5036
5037 #if 0
5038   /* TEST-ONLY */
5039   for (next = music_file_info; next != NULL; next = next->next)
5040     printf("::: title == '%s'\n", next->title);
5041 #endif
5042 }
5043
5044 void add_helpanim_entry(int element, int action, int direction, int delay,
5045                         int *num_list_entries)
5046 {
5047   struct HelpAnimInfo *new_list_entry;
5048   (*num_list_entries)++;
5049
5050   helpanim_info =
5051     checked_realloc(helpanim_info,
5052                     *num_list_entries * sizeof(struct HelpAnimInfo));
5053   new_list_entry = &helpanim_info[*num_list_entries - 1];
5054
5055   new_list_entry->element = element;
5056   new_list_entry->action = action;
5057   new_list_entry->direction = direction;
5058   new_list_entry->delay = delay;
5059 }
5060
5061 void print_unknown_token(char *filename, char *token, int token_nr)
5062 {
5063   if (token_nr == 0)
5064   {
5065     Error(ERR_RETURN_LINE, "-");
5066     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
5067     Error(ERR_RETURN, "- config file: '%s'", filename);
5068   }
5069
5070   Error(ERR_RETURN, "- token: '%s'", token);
5071 }
5072
5073 void print_unknown_token_end(int token_nr)
5074 {
5075   if (token_nr > 0)
5076     Error(ERR_RETURN_LINE, "-");
5077 }
5078
5079 void LoadHelpAnimInfo()
5080 {
5081   char *filename = getHelpAnimFilename();
5082   SetupFileList *setup_file_list = NULL, *list;
5083   SetupFileHash *element_hash, *action_hash, *direction_hash;
5084   int num_list_entries = 0;
5085   int num_unknown_tokens = 0;
5086   int i;
5087
5088   if (fileExists(filename))
5089     setup_file_list = loadSetupFileList(filename);
5090
5091   if (setup_file_list == NULL)
5092   {
5093     /* use reliable default values from static configuration */
5094     SetupFileList *insert_ptr;
5095
5096     insert_ptr = setup_file_list =
5097       newSetupFileList(helpanim_config[0].token,
5098                        helpanim_config[0].value);
5099
5100     for (i = 1; helpanim_config[i].token; i++)
5101       insert_ptr = addListEntry(insert_ptr,
5102                                 helpanim_config[i].token,
5103                                 helpanim_config[i].value);
5104   }
5105
5106   element_hash   = newSetupFileHash();
5107   action_hash    = newSetupFileHash();
5108   direction_hash = newSetupFileHash();
5109
5110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5111     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
5112
5113   for (i = 0; i < NUM_ACTIONS; i++)
5114     setHashEntry(action_hash, element_action_info[i].suffix,
5115                  i_to_a(element_action_info[i].value));
5116
5117   /* do not store direction index (bit) here, but direction value! */
5118   for (i = 0; i < NUM_DIRECTIONS; i++)
5119     setHashEntry(direction_hash, element_direction_info[i].suffix,
5120                  i_to_a(1 << element_direction_info[i].value));
5121
5122   for (list = setup_file_list; list != NULL; list = list->next)
5123   {
5124     char *element_token, *action_token, *direction_token;
5125     char *element_value, *action_value, *direction_value;
5126     int delay = atoi(list->value);
5127
5128     if (strcmp(list->token, "end") == 0)
5129     {
5130       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5131
5132       continue;
5133     }
5134
5135     /* first try to break element into element/action/direction parts;
5136        if this does not work, also accept combined "element[.act][.dir]"
5137        elements (like "dynamite.active"), which are unique elements */
5138
5139     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
5140     {
5141       element_value = getHashEntry(element_hash, list->token);
5142       if (element_value != NULL)        /* element found */
5143         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5144                            &num_list_entries);
5145       else
5146       {
5147         /* no further suffixes found -- this is not an element */
5148         print_unknown_token(filename, list->token, num_unknown_tokens++);
5149       }
5150
5151       continue;
5152     }
5153
5154     /* token has format "<prefix>.<something>" */
5155
5156     action_token = strchr(list->token, '.');    /* suffix may be action ... */
5157     direction_token = action_token;             /* ... or direction */
5158
5159     element_token = getStringCopy(list->token);
5160     *strchr(element_token, '.') = '\0';
5161
5162     element_value = getHashEntry(element_hash, element_token);
5163
5164     if (element_value == NULL)          /* this is no element */
5165     {
5166       element_value = getHashEntry(element_hash, list->token);
5167       if (element_value != NULL)        /* combined element found */
5168         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5169                            &num_list_entries);
5170       else
5171         print_unknown_token(filename, list->token, num_unknown_tokens++);
5172
5173       free(element_token);
5174
5175       continue;
5176     }
5177
5178     action_value = getHashEntry(action_hash, action_token);
5179
5180     if (action_value != NULL)           /* action found */
5181     {
5182       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
5183                     &num_list_entries);
5184
5185       free(element_token);
5186
5187       continue;
5188     }
5189
5190     direction_value = getHashEntry(direction_hash, direction_token);
5191
5192     if (direction_value != NULL)        /* direction found */
5193     {
5194       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
5195                          &num_list_entries);
5196
5197       free(element_token);
5198
5199       continue;
5200     }
5201
5202     if (strchr(action_token + 1, '.') == NULL)
5203     {
5204       /* no further suffixes found -- this is not an action nor direction */
5205
5206       element_value = getHashEntry(element_hash, list->token);
5207       if (element_value != NULL)        /* combined element found */
5208         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5209                            &num_list_entries);
5210       else
5211         print_unknown_token(filename, list->token, num_unknown_tokens++);
5212
5213       free(element_token);
5214
5215       continue;
5216     }
5217
5218     /* token has format "<prefix>.<suffix>.<something>" */
5219
5220     direction_token = strchr(action_token + 1, '.');
5221
5222     action_token = getStringCopy(action_token);
5223     *strchr(action_token + 1, '.') = '\0';
5224
5225     action_value = getHashEntry(action_hash, action_token);
5226
5227     if (action_value == NULL)           /* this is no action */
5228     {
5229       element_value = getHashEntry(element_hash, list->token);
5230       if (element_value != NULL)        /* combined element found */
5231         add_helpanim_entry(atoi(element_value), -1, -1, delay,
5232                            &num_list_entries);
5233       else
5234         print_unknown_token(filename, list->token, num_unknown_tokens++);
5235
5236       free(element_token);
5237       free(action_token);
5238
5239       continue;
5240     }
5241
5242     direction_value = getHashEntry(direction_hash, direction_token);
5243
5244     if (direction_value != NULL)        /* direction found */
5245     {
5246       add_helpanim_entry(atoi(element_value), atoi(action_value),
5247                          atoi(direction_value), delay, &num_list_entries);
5248
5249       free(element_token);
5250       free(action_token);
5251
5252       continue;
5253     }
5254
5255     /* this is no direction */
5256
5257     element_value = getHashEntry(element_hash, list->token);
5258     if (element_value != NULL)          /* combined element found */
5259       add_helpanim_entry(atoi(element_value), -1, -1, delay,
5260                          &num_list_entries);
5261     else
5262       print_unknown_token(filename, list->token, num_unknown_tokens++);
5263
5264     free(element_token);
5265     free(action_token);
5266   }
5267
5268   print_unknown_token_end(num_unknown_tokens);
5269
5270   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
5271   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
5272
5273   freeSetupFileList(setup_file_list);
5274   freeSetupFileHash(element_hash);
5275   freeSetupFileHash(action_hash);
5276   freeSetupFileHash(direction_hash);
5277
5278 #if 0
5279   /* TEST ONLY */
5280   for (i = 0; i < num_list_entries; i++)
5281     printf("::: %d, %d, %d => %d\n",
5282            helpanim_info[i].element,
5283            helpanim_info[i].action,
5284            helpanim_info[i].direction,
5285            helpanim_info[i].delay);
5286 #endif
5287 }
5288
5289 void LoadHelpTextInfo()
5290 {
5291   char *filename = getHelpTextFilename();
5292   int i;
5293
5294   if (helptext_info != NULL)
5295   {
5296     freeSetupFileHash(helptext_info);
5297     helptext_info = NULL;
5298   }
5299
5300   if (fileExists(filename))
5301     helptext_info = loadSetupFileHash(filename);
5302
5303   if (helptext_info == NULL)
5304   {
5305     /* use reliable default values from static configuration */
5306     helptext_info = newSetupFileHash();
5307
5308     for (i = 0; helptext_config[i].token; i++)
5309       setHashEntry(helptext_info,
5310                    helptext_config[i].token,
5311                    helptext_config[i].value);
5312   }
5313
5314 #if 0
5315   /* TEST ONLY */
5316   BEGIN_HASH_ITERATION(helptext_info, itr)
5317   {
5318     printf("::: '%s' => '%s'\n",
5319            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
5320   }
5321   END_HASH_ITERATION(hash, itr)
5322 #endif
5323 }
5324
5325
5326 /* ------------------------------------------------------------------------- *
5327  * convert levels
5328  * ------------------------------------------------------------------------- */
5329
5330 #define MAX_NUM_CONVERT_LEVELS          1000
5331
5332 void ConvertLevels()
5333 {
5334   static LevelDirTree *convert_leveldir = NULL;
5335   static int convert_level_nr = -1;
5336   static int num_levels_handled = 0;
5337   static int num_levels_converted = 0;
5338   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
5339   int i;
5340
5341   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
5342                                                global.convert_leveldir);
5343
5344   if (convert_leveldir == NULL)
5345     Error(ERR_EXIT, "no such level identifier: '%s'",
5346           global.convert_leveldir);
5347
5348   leveldir_current = convert_leveldir;
5349
5350   if (global.convert_level_nr != -1)
5351   {
5352     convert_leveldir->first_level = global.convert_level_nr;
5353     convert_leveldir->last_level  = global.convert_level_nr;
5354   }
5355
5356   convert_level_nr = convert_leveldir->first_level;
5357
5358   printf_line("=", 79);
5359   printf("Converting levels\n");
5360   printf_line("-", 79);
5361   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
5362   printf("Level series name:       '%s'\n", convert_leveldir->name);
5363   printf("Level series author:     '%s'\n", convert_leveldir->author);
5364   printf("Number of levels:        %d\n",   convert_leveldir->levels);
5365   printf_line("=", 79);
5366   printf("\n");
5367
5368   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5369     levels_failed[i] = FALSE;
5370
5371   while (convert_level_nr <= convert_leveldir->last_level)
5372   {
5373     char *level_filename;
5374     boolean new_level;
5375
5376     level_nr = convert_level_nr++;
5377
5378     printf("Level %03d: ", level_nr);
5379
5380     LoadLevel(level_nr);
5381     if (level.no_valid_file)
5382     {
5383       printf("(no level)\n");
5384       continue;
5385     }
5386
5387     printf("converting level ... ");
5388
5389     level_filename = getDefaultLevelFilename(level_nr);
5390     new_level = !fileExists(level_filename);
5391
5392     if (new_level)
5393     {
5394       SaveLevel(level_nr);
5395
5396       num_levels_converted++;
5397
5398       printf("converted.\n");
5399     }
5400     else
5401     {
5402       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
5403         levels_failed[level_nr] = TRUE;
5404
5405       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
5406     }
5407
5408     num_levels_handled++;
5409   }
5410
5411   printf("\n");
5412   printf_line("=", 79);
5413   printf("Number of levels handled: %d\n", num_levels_handled);
5414   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
5415          (num_levels_handled ?
5416           num_levels_converted * 100 / num_levels_handled : 0));
5417   printf_line("-", 79);
5418   printf("Summary (for automatic parsing by scripts):\n");
5419   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
5420          convert_leveldir->identifier, num_levels_converted,
5421          num_levels_handled,
5422          (num_levels_handled ?
5423           num_levels_converted * 100 / num_levels_handled : 0));
5424
5425   if (num_levels_handled != num_levels_converted)
5426   {
5427     printf(", FAILED:");
5428     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
5429       if (levels_failed[i])
5430         printf(" %03d", i);
5431   }
5432
5433   printf("\n");
5434   printf_line("=", 79);
5435
5436   CloseAllAndExit(0);
5437 }