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