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