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