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