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