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