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