rnd-20040113-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     13      /* unused level header bytes  */
33 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
35 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
37 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
38 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
39 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
40 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
41 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
42
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
46
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN         0
54 #define LEVEL_FILE_TYPE_RND             1
55 #define LEVEL_FILE_TYPE_EM              2
56
57 #define LEVEL_FILE_TYPE_RND_PACKED      (10 + LEVEL_FILE_TYPE_RND)
58 #define LEVEL_FILE_TYPE_EM_PACKED       (10 + LEVEL_FILE_TYPE_EM)
59
60 #define IS_SINGLE_LEVEL_FILE(x)         (x < 10)
61 #define IS_PACKED_LEVEL_FILE(x)         (x > 10)
62
63
64 /* ========================================================================= */
65 /* level file functions                                                      */
66 /* ========================================================================= */
67
68 void setElementChangePages(struct ElementInfo *ei, int change_pages)
69 {
70   int change_page_size = sizeof(struct ElementChangeInfo);
71
72   ei->num_change_pages = MAX(1, change_pages);
73
74   ei->change_page =
75     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
76
77   if (ei->current_change_page >= ei->num_change_pages)
78     ei->current_change_page = ei->num_change_pages - 1;
79
80   ei->change = &ei->change_page[ei->current_change_page];
81 }
82
83 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
84 {
85   int x, y;
86
87   change->can_change = FALSE;
88
89   change->events = CE_BITMASK_DEFAULT;
90   change->sides = CH_SIDE_ANY;
91
92   change->target_element = EL_EMPTY_SPACE;
93
94   change->delay_fixed = 0;
95   change->delay_random = 0;
96   change->delay_frames = 1;
97
98   change->trigger_element = EL_EMPTY_SPACE;
99
100   change->explode = FALSE;
101   change->use_content = FALSE;
102   change->only_complete = FALSE;
103   change->use_random_change = FALSE;
104   change->random = 100;
105   change->power = CP_NON_DESTRUCTIVE;
106
107   for (x = 0; x < 3; x++)
108     for (y = 0; y < 3; y++)
109       change->content[x][y] = EL_EMPTY_SPACE;
110
111   change->direct_action = 0;
112   change->other_action = 0;
113
114   change->pre_change_function = NULL;
115   change->change_function = NULL;
116   change->post_change_function = NULL;
117 }
118
119 static void setLevelInfoToDefaults(struct LevelInfo *level)
120 {
121   int i, j, x, y;
122
123   level->file_version = FILE_VERSION_ACTUAL;
124   level->game_version = GAME_VERSION_ACTUAL;
125
126   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
127   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
128   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
129
130   level->fieldx = STD_LEV_FIELDX;
131   level->fieldy = STD_LEV_FIELDY;
132
133   for (x = 0; x < MAX_LEV_FIELDX; x++)
134     for (y = 0; y < MAX_LEV_FIELDY; y++)
135       level->field[x][y] = EL_SAND;
136
137   level->time = 100;
138   level->gems_needed = 0;
139   level->amoeba_speed = 10;
140   level->time_magic_wall = 10;
141   level->time_wheel = 10;
142   level->time_light = 10;
143   level->time_timegate = 10;
144   level->amoeba_content = EL_DIAMOND;
145   level->double_speed = FALSE;
146   level->initial_gravity = FALSE;
147   level->em_slippery_gems = FALSE;
148
149   level->use_custom_template = FALSE;
150
151   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
152     level->name[i] = '\0';
153   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
154     level->author[i] = '\0';
155
156   strcpy(level->name, NAMELESS_LEVEL_NAME);
157   strcpy(level->author, ANONYMOUS_NAME);
158
159   for (i = 0; i < 4; i++)
160   {
161     level->envelope_text[i][0] = '\0';
162     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
163     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
164   }
165
166   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
167     level->score[i] = 10;
168
169   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
170   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
171     for (x = 0; x < 3; x++)
172       for (y = 0; y < 3; y++)
173         level->yamyam_content[i][x][y] =
174           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
175
176   level->field[0][0] = EL_PLAYER_1;
177   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
178
179   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
180   {
181     int element = i;
182
183     setElementChangePages(&element_info[element], 1);
184     setElementChangeInfoToDefaults(element_info[element].change);
185
186     if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
187     {
188       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
189         element_info[element].description[j] = '\0';
190
191       if (element_info[element].custom_description != NULL)
192         strncpy(element_info[element].description,
193                 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
194       else
195         strcpy(element_info[element].description,
196                element_info[element].editor_description);
197
198       element_info[element].use_gfx_element = FALSE;
199       element_info[element].gfx_element = EL_EMPTY_SPACE;
200     }
201
202     if (IS_CUSTOM_ELEMENT(element))
203     {
204       element_info[element].collect_score = 10;         /* special default */
205       element_info[element].collect_count = 1;          /* special default */
206
207       element_info[element].push_delay_fixed = -1;      /* initialize later */
208       element_info[element].push_delay_random = -1;     /* initialize later */
209       element_info[element].move_delay_fixed = 0;
210       element_info[element].move_delay_random = 0;
211
212       element_info[element].move_pattern = MV_ALL_DIRECTIONS;
213       element_info[element].move_direction_initial = MV_NO_MOVING;
214       element_info[element].move_stepsize = TILEX / 8;
215       element_info[element].move_enter_element = EL_EMPTY_SPACE;
216       element_info[element].move_leave_element = EL_EMPTY_SPACE;
217
218       element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
219
220       for (x = 0; x < 3; x++)
221         for (y = 0; y < 3; y++)
222           element_info[element].content[x][y] = EL_EMPTY_SPACE;
223
224       element_info[element].access_type = 0;
225       element_info[element].access_layer = 0;
226       element_info[element].walk_to_action = 0;
227       element_info[element].smash_targets = 0;
228       element_info[element].deadliness = 0;
229       element_info[element].consistency = 0;
230
231       element_info[element].can_explode_by_fire = FALSE;
232       element_info[element].can_explode_smashed = FALSE;
233       element_info[element].can_explode_impact = FALSE;
234
235       element_info[element].current_change_page = 0;
236
237       /* start with no properties at all */
238       for (j = 0; j < NUM_EP_BITFIELDS; j++)
239         Properties[element][j] = EP_BITMASK_DEFAULT;
240
241       element_info[element].modified_settings = FALSE;
242     }
243     else if (IS_GROUP_ELEMENT(element))
244     {
245       /* initialize memory for list of elements in group */
246       if (element_info[element].group == NULL)
247         element_info[element].group =
248           checked_malloc(sizeof(struct ElementGroupInfo));
249
250       for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
251         element_info[element].group->element[j] = EL_EMPTY_SPACE;
252
253       /* default: only one element in group */
254       element_info[element].group->num_elements = 1;
255     }
256   }
257
258   BorderElement = EL_STEELWALL;
259
260   level->no_level_file = FALSE;
261
262   if (leveldir_current == NULL)         /* only when dumping level */
263     return;
264
265   /* try to determine better author name than 'anonymous' */
266   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
267   {
268     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
269     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
270   }
271   else
272   {
273     switch (LEVELCLASS(leveldir_current))
274     {
275       case LEVELCLASS_TUTORIAL:
276         strcpy(level->author, PROGRAM_AUTHOR_STRING);
277         break;
278
279       case LEVELCLASS_CONTRIB:
280         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
281         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
282         break;
283
284       case LEVELCLASS_PRIVATE:
285         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
286         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
287         break;
288
289       default:
290         /* keep default value */
291         break;
292     }
293   }
294 }
295
296 static void ActivateLevelTemplate()
297 {
298   /* Currently there is no special action needed to activate the template
299      data, because 'element_info' and 'Properties' overwrite the original
300      level data, while all other variables do not change. */
301 }
302
303 static char *getLevelFilenameFromBasename(char *basename)
304 {
305   static char *filename = NULL;
306
307   checked_free(filename);
308
309   filename = getPath2(getCurrentLevelDir(), basename);
310
311   return filename;
312 }
313
314 static char *getSingleLevelBasename(int nr, int type)
315 {
316   static char basename[MAX_FILENAME_LEN];
317
318   switch (type)
319   {
320     case LEVEL_FILE_TYPE_RND:
321       if (nr < 0)
322         sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
323       else
324         sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
325       break;
326
327     case LEVEL_FILE_TYPE_EM:
328       sprintf(basename, "%d", nr);
329       break;
330
331     default:
332       strcpy(basename, UNDEFINED_FILENAME);
333       break;
334   }
335
336   return basename;
337 }
338
339 static char *getPackedLevelBasename(int type)
340 {
341   static char basename[MAX_FILENAME_LEN];
342
343   switch (type)
344   {
345     default:
346       strcpy(basename, UNDEFINED_FILENAME);
347       break;
348   }
349
350   return basename;
351 }
352
353 static char *getSingleLevelFilename(int nr, int type)
354 {
355   return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
356 }
357
358 static char *getPackedLevelFilename(int type)
359 {
360   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
361 }
362
363 char *getDefaultLevelFilename(int nr)
364 {
365   return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
366 }
367
368 static struct LevelFileInfo *getLevelFileInfo(int nr)
369 {
370   static struct LevelFileInfo level_file_info;
371
372   level_file_info.nr = nr;
373
374   /* special case: level template */
375   if (nr < 0)
376   {
377     level_file_info.type = LEVEL_FILE_TYPE_RND;
378     level_file_info.filename = getDefaultLevelFilename(nr);
379
380     return &level_file_info;
381   }
382
383   /* 1st try: check for native Rocks'n'Diamonds level file */
384   level_file_info.type = LEVEL_FILE_TYPE_RND;
385   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
386   if (fileExists(level_file_info.filename))
387     return &level_file_info;
388
389   /* 2nd try: check for classic Emerald Mine level file */
390   level_file_info.type = LEVEL_FILE_TYPE_EM;
391   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
392   if (fileExists(level_file_info.filename))
393     return &level_file_info;
394
395   /* no known level file found -- use default values */
396   level_file_info.type = LEVEL_FILE_TYPE_RND;
397   level_file_info.filename = getSingleLevelFilename(nr, level_file_info.type);
398
399   return &level_file_info;
400 }
401
402 /* ------------------------------------------------------------------------- */
403 /* functions for loading R'n'D level                                         */
404 /* ------------------------------------------------------------------------- */
405
406 static int checkLevelElement(int element)
407 {
408   /* map some (historic, now obsolete) elements */
409
410 #if 1
411   switch (element)
412   {
413     case EL_PLAYER_OBSOLETE:
414       element = EL_PLAYER_1;
415       break;
416
417     case EL_KEY_OBSOLETE:
418       element = EL_KEY_1;
419
420     case EL_EM_KEY_1_FILE_OBSOLETE:
421       element = EL_EM_KEY_1;
422       break;
423
424     case EL_EM_KEY_2_FILE_OBSOLETE:
425       element = EL_EM_KEY_2;
426       break;
427
428     case EL_EM_KEY_3_FILE_OBSOLETE:
429       element = EL_EM_KEY_3;
430       break;
431
432     case EL_EM_KEY_4_FILE_OBSOLETE:
433       element = EL_EM_KEY_4;
434       break;
435
436     case EL_ENVELOPE_OBSOLETE:
437       element = EL_ENVELOPE_1;
438       break;
439
440     case EL_SP_EMPTY:
441       element = EL_EMPTY;
442       break;
443
444     default:
445       if (element >= NUM_FILE_ELEMENTS)
446       {
447         Error(ERR_WARN, "invalid level element %d", element);
448
449         element = EL_CHAR_QUESTION;
450       }
451       break;
452   }
453 #else
454   if (element >= NUM_FILE_ELEMENTS)
455   {
456     Error(ERR_WARN, "invalid level element %d", element);
457
458     element = EL_CHAR_QUESTION;
459   }
460   else if (element == EL_PLAYER_OBSOLETE)
461     element = EL_PLAYER_1;
462   else if (element == EL_KEY_OBSOLETE)
463     element = EL_KEY_1;
464 #endif
465
466   return element;
467 }
468
469 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
470 {
471   level->file_version = getFileVersion(file);
472   level->game_version = getFileVersion(file);
473
474   return chunk_size;
475 }
476
477 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
478 {
479   int i, x, y;
480
481   level->fieldx = getFile8Bit(file);
482   level->fieldy = getFile8Bit(file);
483
484   level->time           = getFile16BitBE(file);
485   level->gems_needed    = getFile16BitBE(file);
486
487   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
488     level->name[i] = getFile8Bit(file);
489   level->name[MAX_LEVEL_NAME_LEN] = 0;
490
491   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
492     level->score[i] = getFile8Bit(file);
493
494   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
495   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
496     for (y = 0; y < 3; y++)
497       for (x = 0; x < 3; x++)
498         level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
499
500   level->amoeba_speed           = getFile8Bit(file);
501   level->time_magic_wall        = getFile8Bit(file);
502   level->time_wheel             = getFile8Bit(file);
503   level->amoeba_content         = checkLevelElement(getFile8Bit(file));
504   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
505   level->initial_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
506   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
507   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
508
509   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
510
511   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
512
513   return chunk_size;
514 }
515
516 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
517 {
518   int i;
519
520   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
521     level->author[i] = getFile8Bit(file);
522   level->author[MAX_LEVEL_NAME_LEN] = 0;
523
524   return chunk_size;
525 }
526
527 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
528 {
529   int x, y;
530   int chunk_size_expected = level->fieldx * level->fieldy;
531
532   /* Note: "chunk_size" was wrong before version 2.0 when elements are
533      stored with 16-bit encoding (and should be twice as big then).
534      Even worse, playfield data was stored 16-bit when only yamyam content
535      contained 16-bit elements and vice versa. */
536
537   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
538     chunk_size_expected *= 2;
539
540   if (chunk_size_expected != chunk_size)
541   {
542     ReadUnusedBytesFromFile(file, chunk_size);
543     return chunk_size_expected;
544   }
545
546   for (y = 0; y < level->fieldy; y++)
547     for (x = 0; x < level->fieldx; x++)
548       level->field[x][y] =
549         checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
550                           getFile8Bit(file));
551   return chunk_size;
552 }
553
554 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
555 {
556   int i, x, y;
557   int header_size = 4;
558   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
559   int chunk_size_expected = header_size + content_size;
560
561   /* Note: "chunk_size" was wrong before version 2.0 when elements are
562      stored with 16-bit encoding (and should be twice as big then).
563      Even worse, playfield data was stored 16-bit when only yamyam content
564      contained 16-bit elements and vice versa. */
565
566   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
567     chunk_size_expected += content_size;
568
569   if (chunk_size_expected != chunk_size)
570   {
571     ReadUnusedBytesFromFile(file, chunk_size);
572     return chunk_size_expected;
573   }
574
575   getFile8Bit(file);
576   level->num_yamyam_contents = getFile8Bit(file);
577   getFile8Bit(file);
578   getFile8Bit(file);
579
580   /* correct invalid number of content fields -- should never happen */
581   if (level->num_yamyam_contents < 1 ||
582       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
583     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
584
585   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
586     for (y = 0; y < 3; y++)
587       for (x = 0; x < 3; x++)
588         level->yamyam_content[i][x][y] =
589           checkLevelElement(level->encoding_16bit_field ?
590                             getFile16BitBE(file) : getFile8Bit(file));
591   return chunk_size;
592 }
593
594 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
595 {
596   int i, x, y;
597   int element;
598   int num_contents, content_xsize, content_ysize;
599   int content_array[MAX_ELEMENT_CONTENTS][3][3];
600
601   element = checkLevelElement(getFile16BitBE(file));
602   num_contents = getFile8Bit(file);
603   content_xsize = getFile8Bit(file);
604   content_ysize = getFile8Bit(file);
605
606   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
607
608   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
609     for (y = 0; y < 3; y++)
610       for (x = 0; x < 3; x++)
611         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
612
613   /* correct invalid number of content fields -- should never happen */
614   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
615     num_contents = STD_ELEMENT_CONTENTS;
616
617   if (element == EL_YAMYAM)
618   {
619     level->num_yamyam_contents = num_contents;
620
621     for (i = 0; i < num_contents; i++)
622       for (y = 0; y < 3; y++)
623         for (x = 0; x < 3; x++)
624           level->yamyam_content[i][x][y] = content_array[i][x][y];
625   }
626   else if (element == EL_BD_AMOEBA)
627   {
628     level->amoeba_content = content_array[0][0][0];
629   }
630   else
631   {
632     Error(ERR_WARN, "cannot load content for element '%d'", element);
633   }
634
635   return chunk_size;
636 }
637
638 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
639 {
640   int i;
641   int element;
642   int envelope_nr;
643   int envelope_len;
644   int chunk_size_expected;
645
646   element = checkLevelElement(getFile16BitBE(file));
647   if (!IS_ENVELOPE(element))
648     element = EL_ENVELOPE_1;
649
650   envelope_nr = element - EL_ENVELOPE_1;
651
652   envelope_len = getFile16BitBE(file);
653
654   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
655   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
656
657   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
658
659   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
660   if (chunk_size_expected != chunk_size)
661   {
662     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
663     return chunk_size_expected;
664   }
665
666   for (i = 0; i < envelope_len; i++)
667     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
668
669   return chunk_size;
670 }
671
672 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
673 {
674   int num_changed_custom_elements = getFile16BitBE(file);
675   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
676   int i;
677
678   if (chunk_size_expected != chunk_size)
679   {
680     ReadUnusedBytesFromFile(file, chunk_size - 2);
681     return chunk_size_expected;
682   }
683
684   for (i = 0; i < num_changed_custom_elements; i++)
685   {
686     int element = getFile16BitBE(file);
687     int properties = getFile32BitBE(file);
688
689     if (IS_CUSTOM_ELEMENT(element))
690       Properties[element][EP_BITFIELD_BASE] = properties;
691     else
692       Error(ERR_WARN, "invalid custom element number %d", element);
693   }
694
695   return chunk_size;
696 }
697
698 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
699 {
700   int num_changed_custom_elements = getFile16BitBE(file);
701   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
702   int i;
703
704   if (chunk_size_expected != chunk_size)
705   {
706     ReadUnusedBytesFromFile(file, chunk_size - 2);
707     return chunk_size_expected;
708   }
709
710   for (i = 0; i < num_changed_custom_elements; i++)
711   {
712     int element = getFile16BitBE(file);
713     int custom_target_element = getFile16BitBE(file);
714
715     if (IS_CUSTOM_ELEMENT(element))
716       element_info[element].change->target_element = custom_target_element;
717     else
718       Error(ERR_WARN, "invalid custom element number %d", element);
719   }
720
721   return chunk_size;
722 }
723
724 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
725 {
726   int num_changed_custom_elements = getFile16BitBE(file);
727   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
728   int i, j, x, y;
729
730   if (chunk_size_expected != chunk_size)
731   {
732     ReadUnusedBytesFromFile(file, chunk_size - 2);
733     return chunk_size_expected;
734   }
735
736   for (i = 0; i < num_changed_custom_elements; i++)
737   {
738     int element = getFile16BitBE(file);
739
740     if (!IS_CUSTOM_ELEMENT(element))
741     {
742       Error(ERR_WARN, "invalid custom element number %d", element);
743
744       element = EL_DUMMY;
745     }
746
747     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
748       element_info[element].description[j] = getFile8Bit(file);
749     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
750
751     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
752
753     /* some free bytes for future properties and padding */
754     ReadUnusedBytesFromFile(file, 7);
755
756     element_info[element].use_gfx_element = getFile8Bit(file);
757     element_info[element].gfx_element =
758       checkLevelElement(getFile16BitBE(file));
759
760     element_info[element].collect_score = getFile8Bit(file);
761     element_info[element].collect_count = getFile8Bit(file);
762
763     element_info[element].push_delay_fixed = getFile16BitBE(file);
764     element_info[element].push_delay_random = getFile16BitBE(file);
765     element_info[element].move_delay_fixed = getFile16BitBE(file);
766     element_info[element].move_delay_random = getFile16BitBE(file);
767
768     element_info[element].move_pattern = getFile16BitBE(file);
769     element_info[element].move_direction_initial = getFile8Bit(file);
770     element_info[element].move_stepsize = getFile8Bit(file);
771
772     for (y = 0; y < 3; y++)
773       for (x = 0; x < 3; x++)
774         element_info[element].content[x][y] =
775           checkLevelElement(getFile16BitBE(file));
776
777     element_info[element].change->events = getFile32BitBE(file);
778
779     element_info[element].change->target_element =
780       checkLevelElement(getFile16BitBE(file));
781
782     element_info[element].change->delay_fixed = getFile16BitBE(file);
783     element_info[element].change->delay_random = getFile16BitBE(file);
784     element_info[element].change->delay_frames = getFile16BitBE(file);
785
786     element_info[element].change->trigger_element =
787       checkLevelElement(getFile16BitBE(file));
788
789     element_info[element].change->explode = getFile8Bit(file);
790     element_info[element].change->use_content = getFile8Bit(file);
791     element_info[element].change->only_complete = getFile8Bit(file);
792     element_info[element].change->use_random_change = getFile8Bit(file);
793
794     element_info[element].change->random = getFile8Bit(file);
795     element_info[element].change->power = getFile8Bit(file);
796
797     for (y = 0; y < 3; y++)
798       for (x = 0; x < 3; x++)
799         element_info[element].change->content[x][y] =
800           checkLevelElement(getFile16BitBE(file));
801
802     element_info[element].slippery_type = getFile8Bit(file);
803
804     /* some free bytes for future properties and padding */
805     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
806
807     /* mark that this custom element has been modified */
808     element_info[element].modified_settings = TRUE;
809   }
810
811   return chunk_size;
812 }
813
814 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
815 {
816   struct ElementInfo *ei;
817   int chunk_size_expected;
818   int element;
819   int i, x, y;
820
821   element = getFile16BitBE(file);
822
823   if (!IS_CUSTOM_ELEMENT(element))
824   {
825     Error(ERR_WARN, "invalid custom element number %d", element);
826
827     ReadUnusedBytesFromFile(file, chunk_size - 2);
828     return chunk_size;
829   }
830
831   ei = &element_info[element];
832
833   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
834     ei->description[i] = getFile8Bit(file);
835   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
836
837   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
838   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
839
840   ei->num_change_pages = getFile8Bit(file);
841
842   /* some free bytes for future base property values and padding */
843   ReadUnusedBytesFromFile(file, 5);
844
845   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
846   if (chunk_size_expected != chunk_size)
847   {
848     ReadUnusedBytesFromFile(file, chunk_size - 48);
849     return chunk_size_expected;
850   }
851
852   /* read custom property values */
853
854   ei->use_gfx_element = getFile8Bit(file);
855   ei->gfx_element = checkLevelElement(getFile16BitBE(file));
856
857   ei->collect_score = getFile8Bit(file);
858   ei->collect_count = getFile8Bit(file);
859
860   ei->push_delay_fixed = getFile16BitBE(file);
861   ei->push_delay_random = getFile16BitBE(file);
862   ei->move_delay_fixed = getFile16BitBE(file);
863   ei->move_delay_random = getFile16BitBE(file);
864
865   ei->move_pattern = getFile16BitBE(file);
866   ei->move_direction_initial = getFile8Bit(file);
867   ei->move_stepsize = getFile8Bit(file);
868
869   ei->slippery_type = getFile8Bit(file);
870
871   for (y = 0; y < 3; y++)
872     for (x = 0; x < 3; x++)
873       ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
874
875   ei->move_enter_element = checkLevelElement(getFile16BitBE(file));
876   ei->move_leave_element = checkLevelElement(getFile16BitBE(file));
877
878   /* some free bytes for future custom property values and padding */
879   ReadUnusedBytesFromFile(file, 8);
880
881   /* read change property values */
882
883   setElementChangePages(ei, ei->num_change_pages);
884
885   for (i = 0; i < ei->num_change_pages; i++)
886   {
887     struct ElementChangeInfo *change = &ei->change_page[i];
888
889     /* always start with reliable default values */
890     setElementChangeInfoToDefaults(change);
891
892     change->events = getFile32BitBE(file);
893
894     change->target_element = checkLevelElement(getFile16BitBE(file));
895
896     change->delay_fixed = getFile16BitBE(file);
897     change->delay_random = getFile16BitBE(file);
898     change->delay_frames = getFile16BitBE(file);
899
900     change->trigger_element = checkLevelElement(getFile16BitBE(file));
901
902     change->explode = getFile8Bit(file);
903     change->use_content = getFile8Bit(file);
904     change->only_complete = getFile8Bit(file);
905     change->use_random_change = getFile8Bit(file);
906
907     change->random = getFile8Bit(file);
908     change->power = getFile8Bit(file);
909
910     for (y = 0; y < 3; y++)
911       for (x = 0; x < 3; x++)
912         change->content[x][y] = checkLevelElement(getFile16BitBE(file));
913
914     change->can_change = getFile8Bit(file);
915
916     change->sides = getFile8Bit(file);
917
918     if (change->sides == CH_SIDE_NONE)  /* correct empty sides field */
919       change->sides = CH_SIDE_ANY;
920
921     /* some free bytes for future change property values and padding */
922     ReadUnusedBytesFromFile(file, 8);
923   }
924
925   /* mark this custom element as modified */
926   ei->modified_settings = TRUE;
927
928   return chunk_size;
929 }
930
931 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
932 {
933   struct ElementInfo *ei;
934   struct ElementGroupInfo *group;
935   int element;
936   int i;
937
938   element = getFile16BitBE(file);
939
940   if (!IS_GROUP_ELEMENT(element))
941   {
942     Error(ERR_WARN, "invalid group element number %d", element);
943
944     ReadUnusedBytesFromFile(file, chunk_size - 2);
945     return chunk_size;
946   }
947
948   ei = &element_info[element];
949
950   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
951     ei->description[i] = getFile8Bit(file);
952   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
953
954   group = element_info[element].group;
955
956   group->num_elements = getFile8Bit(file);
957
958   ei->use_gfx_element = getFile8Bit(file);
959   ei->gfx_element = checkLevelElement(getFile16BitBE(file));
960
961   /* some free bytes for future values and padding */
962   ReadUnusedBytesFromFile(file, 4);
963
964   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
965     group->element[i] = checkLevelElement(getFile16BitBE(file));
966
967   /* mark this group element as modified */
968   element_info[element].modified_settings = TRUE;
969
970   return chunk_size;
971 }
972
973 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
974                                       struct LevelFileInfo *level_file_info)
975 {
976   char *filename = level_file_info->filename;
977   char cookie[MAX_LINE_LEN];
978   char chunk_name[CHUNK_ID_LEN + 1];
979   int chunk_size;
980   FILE *file;
981
982   /* always start with reliable default values */
983   setLevelInfoToDefaults(level);
984
985   if (!(file = fopen(filename, MODE_READ)))
986   {
987     level->no_level_file = TRUE;
988
989     if (level != &level_template)
990       Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
991
992     return;
993   }
994
995   getFileChunkBE(file, chunk_name, NULL);
996   if (strcmp(chunk_name, "RND1") == 0)
997   {
998     getFile32BitBE(file);               /* not used */
999
1000     getFileChunkBE(file, chunk_name, NULL);
1001     if (strcmp(chunk_name, "CAVE") != 0)
1002     {
1003       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1004       fclose(file);
1005       return;
1006     }
1007   }
1008   else  /* check for pre-2.0 file format with cookie string */
1009   {
1010     strcpy(cookie, chunk_name);
1011     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1012     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1013       cookie[strlen(cookie) - 1] = '\0';
1014
1015     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1016     {
1017       Error(ERR_WARN, "unknown format of level file '%s'", filename);
1018       fclose(file);
1019       return;
1020     }
1021
1022     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1023     {
1024       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1025       fclose(file);
1026       return;
1027     }
1028
1029     /* pre-2.0 level files have no game version, so use file version here */
1030     level->game_version = level->file_version;
1031   }
1032
1033   if (level->file_version < FILE_VERSION_1_2)
1034   {
1035     /* level files from versions before 1.2.0 without chunk structure */
1036     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
1037     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1038   }
1039   else
1040   {
1041     static struct
1042     {
1043       char *name;
1044       int size;
1045       int (*loader)(FILE *, int, struct LevelInfo *);
1046     }
1047     chunk_info[] =
1048     {
1049       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
1050       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
1051       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
1052       { "BODY", -1,                     LoadLevel_BODY },
1053       { "CONT", -1,                     LoadLevel_CONT },
1054       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
1055       { "CNT3", -1,                     LoadLevel_CNT3 },
1056       { "CUS1", -1,                     LoadLevel_CUS1 },
1057       { "CUS2", -1,                     LoadLevel_CUS2 },
1058       { "CUS3", -1,                     LoadLevel_CUS3 },
1059       { "CUS4", -1,                     LoadLevel_CUS4 },
1060       { "GRP1", -1,                     LoadLevel_GRP1 },
1061       {  NULL,  0,                      NULL }
1062     };
1063
1064     while (getFileChunkBE(file, chunk_name, &chunk_size))
1065     {
1066       int i = 0;
1067
1068       while (chunk_info[i].name != NULL &&
1069              strcmp(chunk_name, chunk_info[i].name) != 0)
1070         i++;
1071
1072       if (chunk_info[i].name == NULL)
1073       {
1074         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1075               chunk_name, filename);
1076         ReadUnusedBytesFromFile(file, chunk_size);
1077       }
1078       else if (chunk_info[i].size != -1 &&
1079                chunk_info[i].size != chunk_size)
1080       {
1081         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1082               chunk_size, chunk_name, filename);
1083         ReadUnusedBytesFromFile(file, chunk_size);
1084       }
1085       else
1086       {
1087         /* call function to load this level chunk */
1088         int chunk_size_expected =
1089           (chunk_info[i].loader)(file, chunk_size, level);
1090
1091         /* the size of some chunks cannot be checked before reading other
1092            chunks first (like "HEAD" and "BODY") that contain some header
1093            information, so check them here */
1094         if (chunk_size_expected != chunk_size)
1095         {
1096           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1097                 chunk_size, chunk_name, filename);
1098         }
1099       }
1100     }
1101   }
1102
1103   fclose(file);
1104 }
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* functions for loading EM level                                            */
1108 /* ------------------------------------------------------------------------- */
1109
1110 static int map_em_element_yam(int element)
1111 {
1112   switch (element)
1113   {
1114     case 0x00:  return EL_EMPTY;
1115     case 0x01:  return EL_EMERALD;
1116     case 0x02:  return EL_DIAMOND;
1117     case 0x03:  return EL_ROCK;
1118     case 0x04:  return EL_ROBOT;
1119     case 0x05:  return EL_SPACESHIP_UP;
1120     case 0x06:  return EL_BOMB;
1121     case 0x07:  return EL_BUG_UP;
1122     case 0x08:  return EL_AMOEBA_DROP;
1123     case 0x09:  return EL_NUT;
1124     case 0x0a:  return EL_YAMYAM;
1125     case 0x0b:  return EL_QUICKSAND_FULL;
1126     case 0x0c:  return EL_SAND;
1127     case 0x0d:  return EL_WALL_SLIPPERY;
1128     case 0x0e:  return EL_STEELWALL;
1129     case 0x0f:  return EL_WALL;
1130     case 0x10:  return EL_EM_KEY_1;
1131     case 0x11:  return EL_EM_KEY_2;
1132     case 0x12:  return EL_EM_KEY_4;
1133     case 0x13:  return EL_EM_KEY_3;
1134     case 0x14:  return EL_MAGIC_WALL;
1135     case 0x15:  return EL_ROBOT_WHEEL;
1136     case 0x16:  return EL_DYNAMITE;
1137
1138     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
1139     case 0x18:  return EL_BUG_UP;                       /* EMC */
1140     case 0x1a:  return EL_DIAMOND;                      /* EMC */
1141     case 0x1b:  return EL_EMERALD;                      /* EMC */
1142     case 0x25:  return EL_NUT;                          /* EMC */
1143     case 0x80:  return EL_EMPTY;                        /* EMC */
1144     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
1145     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
1146     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
1147     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
1148     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
1149     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
1150     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
1151     case 0xbd:  return EL_SAND;                         /* EMC */
1152
1153     default:
1154       Error(ERR_WARN, "invalid level element %d", element);
1155       return EL_CHAR_QUESTION;
1156   }
1157 }
1158
1159 static int map_em_element_field(int element)
1160 {
1161   if (element >= 0xc8 && element <= 0xe1)
1162     return EL_CHAR_A + (element - 0xc8);
1163   else if (element >= 0xe2 && element <= 0xeb)
1164     return EL_CHAR_0 + (element - 0xe2);
1165
1166   switch (element)
1167   {
1168     case 0x00:  return EL_ROCK;
1169     case 0x02:  return EL_DIAMOND;
1170     case 0x03:  return EL_DIAMOND;
1171     case 0x04:  return EL_ROBOT;
1172     case 0x05:  return EL_ROBOT;                        /* EMC */
1173     case 0x08:  return EL_SPACESHIP_UP;
1174     case 0x09:  return EL_SPACESHIP_RIGHT;
1175     case 0x0a:  return EL_SPACESHIP_DOWN;
1176     case 0x0b:  return EL_SPACESHIP_LEFT;
1177     case 0x0c:  return EL_SPACESHIP_UP;
1178     case 0x0d:  return EL_SPACESHIP_RIGHT;
1179     case 0x0e:  return EL_SPACESHIP_DOWN;
1180     case 0x0f:  return EL_SPACESHIP_LEFT;
1181     case 0x10:  return EL_BOMB;
1182     case 0x12:  return EL_EMERALD;
1183     case 0x13:  return EL_EMERALD;
1184     case 0x14:  return EL_BUG_UP;
1185     case 0x15:  return EL_BUG_RIGHT;
1186     case 0x16:  return EL_BUG_DOWN;
1187     case 0x17:  return EL_BUG_LEFT;
1188     case 0x18:  return EL_BUG_UP;
1189     case 0x19:  return EL_BUG_RIGHT;
1190     case 0x1a:  return EL_BUG_DOWN;
1191     case 0x1b:  return EL_BUG_LEFT;
1192     case 0x1c:  return EL_AMOEBA_DROP;
1193     case 0x20:  return EL_ROCK;
1194     case 0x24:  return EL_MAGIC_WALL;
1195     case 0x25:  return EL_NUT;
1196
1197       /* looks like magic wheel, but is _always_ activated */
1198     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
1199
1200     case 0x29:  return EL_YAMYAM;
1201     case 0x2a:  return EL_YAMYAM;
1202     case 0x2b:  return EL_YAMYAM;                       /* EMC */
1203     case 0x2c:  return EL_YAMYAM;                       /* EMC */
1204     case 0x2d:  return EL_QUICKSAND_FULL;
1205     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
1206     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
1207     case 0x3b:  return EL_DYNAMITE_ACTIVE;
1208     case 0x3c:  return EL_DYNAMITE_ACTIVE;
1209     case 0x3d:  return EL_DYNAMITE_ACTIVE;
1210     case 0x3e:  return EL_DYNAMITE_ACTIVE;
1211     case 0x3f:  return EL_ACID_POOL_BOTTOM;
1212     case 0x40:  return EL_EXIT_OPEN;
1213     case 0x41:  return EL_EXIT_OPEN;
1214     case 0x42:  return EL_EXIT_OPEN;
1215     case 0x43:  return EL_BALLOON;
1216     case 0x4e:  return EL_INVISIBLE_WALL;
1217     case 0x65:  return EL_ACID;                         /* EMC */
1218     case 0x73:  return EL_SAND;                         /* EMC */
1219     case 0x74:  return EL_STEELWALL;
1220     case 0x7b:  return EL_ACID;
1221     case 0x80:  return EL_EMPTY;
1222     case 0x81:  return EL_WALL_SLIPPERY;
1223     case 0x82:  return EL_SAND;
1224     case 0x83:  return EL_STEELWALL;
1225     case 0x84:  return EL_WALL;
1226     case 0x85:  return EL_EM_KEY_1;
1227     case 0x86:  return EL_EM_KEY_2;
1228     case 0x87:  return EL_EM_KEY_4;
1229     case 0x88:  return EL_EM_KEY_3;
1230     case 0x89:  return EL_EM_GATE_1;
1231     case 0x8a:  return EL_EM_GATE_2;
1232     case 0x8b:  return EL_EM_GATE_4;
1233     case 0x8c:  return EL_EM_GATE_3;
1234     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC */
1235     case 0x8e:  return EL_EM_GATE_1_GRAY;
1236     case 0x8f:  return EL_EM_GATE_2_GRAY;
1237     case 0x90:  return EL_EM_GATE_4_GRAY;
1238     case 0x91:  return EL_EM_GATE_3_GRAY;
1239     case 0x92:  return EL_MAGIC_WALL;
1240     case 0x94:  return EL_QUICKSAND_EMPTY;
1241     case 0x95:  return EL_ACID_POOL_TOPLEFT;
1242     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
1243     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
1244     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
1245     case 0x99:  return EL_ACID;
1246     case 0x9a:  return EL_AMOEBA_DEAD;
1247     case 0x9b:  return EL_AMOEBA_DEAD;
1248     case 0x9c:  return EL_AMOEBA_DEAD;
1249     case 0x9d:  return EL_AMOEBA_DEAD;
1250     case 0x9e:  return EL_EXIT_CLOSED;
1251     case 0x9f:  return EL_CHAR_LESS;                    /* EMC */
1252     case 0x93:  return EL_ROBOT_WHEEL;
1253
1254       /* looks like normal dust, but behaves like wall */
1255     case 0xa0:  return EL_WALL;                         /* EMC */
1256
1257     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC */
1258     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC */
1259     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC */
1260     case 0xab:  return EL_EMC_WALL_7;                   /* EMC */
1261     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
1262     case 0xaf:  return EL_DYNAMITE;
1263     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC */
1264     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC */
1265
1266       /* (exact steel wall) */
1267     case 0xb3:  return EL_STEELWALL;                    /* EMC */
1268
1269     case 0xb4:  return EL_WALL_SLIPPERY;                /* EMC */
1270     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC */
1271     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC */
1272     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC */
1273     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
1274     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
1275     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
1276     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
1277     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
1278     case 0xbd:  return EL_SAND;                         /* EMC */
1279     case 0xec:  return EL_CHAR_PERIOD;
1280     case 0xed:  return EL_CHAR_EXCLAM;
1281     case 0xee:  return EL_CHAR_COLON;
1282     case 0xef:  return EL_CHAR_QUESTION;
1283     case 0xf0:  return EL_CHAR_GREATER;
1284     case 0xf1:  return EL_CHAR_COPYRIGHT;
1285     case 0xfe:  return EL_PLAYER_1;
1286     case 0xff:  return EL_PLAYER_2;
1287
1288     default:
1289       Error(ERR_WARN, "invalid level element %d", element);
1290       return EL_CHAR_QUESTION;
1291   }
1292 }
1293
1294 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1295                                      struct LevelFileInfo *level_file_info)
1296 {
1297   char *filename = level_file_info->filename;
1298   FILE *file;
1299   unsigned char body[40][64];
1300   unsigned char *leveldata = &body[0][0];
1301   unsigned char *header = &leveldata[2048];
1302   unsigned char code0 = 0x65;
1303   unsigned char code1 = 0x11;
1304   boolean level_is_crypted = FALSE;
1305   int nr = level_file_info->nr;
1306   int jx, jy;
1307   int i, x, y;
1308
1309   /* always start with reliable default values */
1310   setLevelInfoToDefaults(level);
1311
1312   if (!(file = fopen(filename, MODE_READ)))
1313   {
1314     level->no_level_file = TRUE;
1315
1316     Error(ERR_WARN, "cannot read level '%s' - using empty level", filename);
1317
1318     return;
1319   }
1320
1321   for(i = 0; i < 2106; i++)
1322     leveldata[i] = fgetc(file);
1323
1324   fclose(file);
1325
1326   /* check if level data is crypted by testing against known starting bytes
1327      of the few existing crypted level files (from Emerald Mine 1 + 2) */
1328
1329   if ((leveldata[0] == 0xf1 ||
1330        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1331   {
1332     level_is_crypted = TRUE;
1333
1334     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
1335       leveldata[0] = 0xf1;
1336   }
1337
1338   if (level_is_crypted)         /* decode crypted level data */
1339   {
1340     for(i = 0; i < 2106; i++)
1341     {
1342       leveldata[i] ^= code0;
1343       leveldata[i] -= code1;
1344
1345       code0  = (code0 + 7) & 0xff;
1346     }
1347   }
1348
1349   level->fieldx = 64;
1350   level->fieldy = 32;
1351
1352   level->time           = header[46] * 10;
1353   level->gems_needed    = header[47];
1354
1355   /* The original Emerald Mine levels have their level number stored
1356      at the second byte of the level file...
1357      Do not trust this information at other level files, e.g. EMC,
1358      but correct it anyway (normally the first row is completely
1359      steel wall, so the correction does not hurt anyway). */
1360
1361   if (leveldata[1] == nr)
1362     leveldata[1] = leveldata[2];        /* correct level number field */
1363
1364   sprintf(level->name, "Level %d", nr);
1365
1366   level->score[SC_EMERALD]      = header[36];
1367   level->score[SC_DIAMOND]      = header[37];
1368   level->score[SC_ROBOT]        = header[38];
1369   level->score[SC_SPACESHIP]    = header[39];
1370   level->score[SC_BUG]          = header[40];
1371   level->score[SC_YAMYAM]       = header[41];
1372   level->score[SC_NUT]          = header[42];
1373   level->score[SC_DYNAMITE]     = header[43];
1374   level->score[SC_TIME_BONUS]   = header[44];
1375
1376   level->num_yamyam_contents = 4;
1377
1378   for(i = 0; i < level->num_yamyam_contents; i++)
1379     for(y = 0; y < 3; y++)
1380       for(x = 0; x < 3; x++)
1381         level->yamyam_content[i][x][y] =
1382           map_em_element_yam(header[i * 9 + y * 3 + x]);
1383
1384   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
1385   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
1386   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
1387   level->amoeba_content         = EL_DIAMOND;
1388
1389   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1390   {
1391     int new_element = map_em_element_field(body[y][x]);
1392
1393     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1394       new_element = EL_AMOEBA_WET;
1395
1396     level->field[x][y] = new_element;
1397   }
1398
1399   jx = (header[48] * 256 + header[49]) % 64;
1400   jy = (header[48] * 256 + header[49]) / 64;
1401   level->field[jx][jy] = EL_PLAYER_1;
1402
1403   jx = (header[50] * 256 + header[51]) % 64;
1404   jy = (header[50] * 256 + header[51]) / 64;
1405   level->field[jx][jy] = EL_PLAYER_2;
1406 }
1407
1408 void LoadLevelFromFileInfo(struct LevelInfo *level,
1409                            struct LevelFileInfo *level_file_info)
1410 {
1411   switch (level_file_info->type)
1412   {
1413     case LEVEL_FILE_TYPE_RND:
1414       LoadLevelFromFileInfo_RND(level, level_file_info);
1415       break;
1416
1417     case LEVEL_FILE_TYPE_EM:
1418       LoadLevelFromFileInfo_EM(level, level_file_info);
1419       break;
1420
1421     default:
1422       LoadLevelFromFileInfo_RND(level, level_file_info);
1423       break;
1424   }
1425 }
1426
1427 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1428 {
1429   static struct LevelFileInfo level_file_info;
1430
1431   level_file_info.nr = 0;                       /* unknown */
1432   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
1433   level_file_info.filename = filename;
1434
1435   LoadLevelFromFileInfo(level, &level_file_info);
1436 }
1437
1438 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1439 {
1440   if (leveldir_current == NULL)         /* only when dumping level */
1441     return;
1442
1443 #if 0
1444   printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1445 #endif
1446
1447   /* determine correct game engine version of current level */
1448 #if 1
1449   if (!leveldir_current->latest_engine)
1450 #else
1451   if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1452       IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1453       IS_LEVELCLASS_UNDEFINED(leveldir_current))
1454 #endif
1455   {
1456 #if 0
1457     printf("\n::: This level is private or contributed: '%s'\n", filename);
1458 #endif
1459
1460 #if 0
1461     printf("\n::: Use the stored game engine version for this level\n");
1462 #endif
1463
1464     /* For all levels which are not forced to use the latest game engine
1465        version (normally user contributed, private and undefined levels),
1466        use the version of the game engine the levels were created for.
1467
1468        Since 2.0.1, the game engine version is now directly stored
1469        in the level file (chunk "VERS"), so there is no need anymore
1470        to set the game version from the file version (except for old,
1471        pre-2.0 levels, where the game version is still taken from the
1472        file format version used to store the level -- see above). */
1473
1474     /* do some special adjustments to support older level versions */
1475     if (level->file_version == FILE_VERSION_1_0)
1476     {
1477       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1478       Error(ERR_WARN, "using high speed movement for player");
1479
1480       /* player was faster than monsters in (pre-)1.0 levels */
1481       level->double_speed = TRUE;
1482     }
1483
1484     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1485     if (level->game_version == VERSION_IDENT(2,0,1,0))
1486       level->em_slippery_gems = TRUE;
1487   }
1488   else
1489   {
1490 #if 0
1491     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1492            leveldir_current->sort_priority, filename);
1493 #endif
1494
1495 #if 0
1496     printf("\n::: Use latest game engine version for this level.\n");
1497 #endif
1498
1499     /* For all levels which are forced to use the latest game engine version
1500        (normally all but user contributed, private and undefined levels), set
1501        the game engine version to the actual version; this allows for actual
1502        corrections in the game engine to take effect for existing, converted
1503        levels (from "classic" or other existing games) to make the emulation
1504        of the corresponding game more accurate, while (hopefully) not breaking
1505        existing levels created from other players. */
1506
1507 #if 0
1508     printf("::: changing engine from %d to %d\n",
1509            level->game_version, GAME_VERSION_ACTUAL);
1510 #endif
1511
1512     level->game_version = GAME_VERSION_ACTUAL;
1513
1514     /* Set special EM style gems behaviour: EM style gems slip down from
1515        normal, steel and growing wall. As this is a more fundamental change,
1516        it seems better to set the default behaviour to "off" (as it is more
1517        natural) and make it configurable in the level editor (as a property
1518        of gem style elements). Already existing converted levels (neither
1519        private nor contributed levels) are changed to the new behaviour. */
1520
1521     if (level->file_version < FILE_VERSION_2_0)
1522       level->em_slippery_gems = TRUE;
1523   }
1524
1525 #if 0
1526   printf("::: => %d\n", level->game_version);
1527 #endif
1528 }
1529
1530 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1531 {
1532   int i, j;
1533
1534   /* map custom element change events that have changed in newer versions
1535      (these following values were accidentally changed in version 3.0.1) */
1536   if (level->game_version <= VERSION_IDENT(3,0,0,0))
1537   {
1538     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1539     {
1540       int element = EL_CUSTOM_START + i;
1541
1542       /* order of checking and copying events to be mapped is important */
1543       for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1544       {
1545         if (HAS_CHANGE_EVENT(element, j - 2))
1546         {
1547           SET_CHANGE_EVENT(element, j - 2, FALSE);
1548           SET_CHANGE_EVENT(element, j, TRUE);
1549         }
1550       }
1551
1552       /* order of checking and copying events to be mapped is important */
1553       for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1554       {
1555         if (HAS_CHANGE_EVENT(element, j - 1))
1556         {
1557           SET_CHANGE_EVENT(element, j - 1, FALSE);
1558           SET_CHANGE_EVENT(element, j, TRUE);
1559         }
1560       }
1561     }
1562   }
1563
1564   /* some custom element change events get mapped since version 3.0.3 */
1565   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1566   {
1567     int element = EL_CUSTOM_START + i;
1568
1569     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1570         HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1571     {
1572       SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1573       SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1574
1575       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1576     }
1577   }
1578
1579   /* initialize "can_change" field for old levels with only one change page */
1580   if (level->game_version <= VERSION_IDENT(3,0,2,0))
1581   {
1582     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1583     {
1584       int element = EL_CUSTOM_START + i;
1585
1586       if (CAN_CHANGE(element))
1587         element_info[element].change->can_change = TRUE;
1588     }
1589   }
1590
1591 #if 0
1592   /* set default push delay values (corrected since version 3.0.7-1) */
1593   if (level->game_version < VERSION_IDENT(3,0,7,1))
1594   {
1595     game.default_push_delay_fixed = 2;
1596     game.default_push_delay_random = 8;
1597   }
1598   else
1599   {
1600     game.default_push_delay_fixed = 8;
1601     game.default_push_delay_random = 8;
1602   }
1603
1604   /* set uninitialized push delay values of custom elements in older levels */
1605   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1606   {
1607     int element = EL_CUSTOM_START + i;
1608
1609     if (element_info[element].push_delay_fixed == -1)
1610       element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1611     if (element_info[element].push_delay_random == -1)
1612       element_info[element].push_delay_random = game.default_push_delay_random;
1613   }
1614 #endif
1615
1616   /* initialize element properties for level editor etc. */
1617   InitElementPropertiesEngine(level->game_version);
1618 }
1619
1620 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1621 {
1622   int x, y;
1623
1624   /* map elements that have changed in newer versions */
1625   for (y = 0; y < level->fieldy; y++)
1626   {
1627     for (x = 0; x < level->fieldx; x++)
1628     {
1629       int element = level->field[x][y];
1630
1631       if (level->game_version <= VERSION_IDENT(2,2,0,0))
1632       {
1633         /* map game font elements */
1634         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1635                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1636                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1637                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1638       }
1639
1640       if (level->game_version < VERSION_IDENT(3,0,0,0))
1641       {
1642         /* map Supaplex gravity tube elements */
1643         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1644                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1645                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1646                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1647                    element);
1648       }
1649
1650       level->field[x][y] = element;
1651     }
1652   }
1653
1654   /* copy elements to runtime playfield array */
1655   for (x = 0; x < MAX_LEV_FIELDX; x++)
1656     for (y = 0; y < MAX_LEV_FIELDY; y++)
1657       Feld[x][y] = level->field[x][y];
1658
1659   /* initialize level size variables for faster access */
1660   lev_fieldx = level->fieldx;
1661   lev_fieldy = level->fieldy;
1662
1663   /* determine border element for this level */
1664   SetBorderElement();
1665 }
1666
1667 void LoadLevelTemplate(int nr)
1668 {
1669 #if 1
1670   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1671   char *filename = level_file_info->filename;
1672
1673   LoadLevelFromFileInfo(&level_template, level_file_info);
1674 #else
1675   char *filename = getDefaultLevelFilename(nr);
1676
1677   LoadLevelFromFilename_RND(&level_template, filename);
1678 #endif
1679
1680   LoadLevel_InitVersion(&level, filename);
1681   LoadLevel_InitElements(&level, filename);
1682
1683   ActivateLevelTemplate();
1684 }
1685
1686 void LoadLevel(int nr)
1687 {
1688 #if 1
1689   struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
1690   char *filename = level_file_info->filename;
1691
1692   LoadLevelFromFileInfo(&level, level_file_info);
1693 #else
1694   char *filename = getLevelFilename(nr);
1695
1696   LoadLevelFromFilename_RND(&level, filename);
1697 #endif
1698
1699   if (level.use_custom_template)
1700     LoadLevelTemplate(-1);
1701
1702 #if 1
1703   LoadLevel_InitVersion(&level, filename);
1704   LoadLevel_InitElements(&level, filename);
1705   LoadLevel_InitPlayfield(&level, filename);
1706 #else
1707   LoadLevel_InitLevel(&level, filename);
1708 #endif
1709 }
1710
1711 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1712 {
1713   putFileVersion(file, level->file_version);
1714   putFileVersion(file, level->game_version);
1715 }
1716
1717 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1718 {
1719   int i, x, y;
1720
1721   putFile8Bit(file, level->fieldx);
1722   putFile8Bit(file, level->fieldy);
1723
1724   putFile16BitBE(file, level->time);
1725   putFile16BitBE(file, level->gems_needed);
1726
1727   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1728     putFile8Bit(file, level->name[i]);
1729
1730   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1731     putFile8Bit(file, level->score[i]);
1732
1733   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1734     for (y = 0; y < 3; y++)
1735       for (x = 0; x < 3; x++)
1736         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1737                            level->yamyam_content[i][x][y]));
1738   putFile8Bit(file, level->amoeba_speed);
1739   putFile8Bit(file, level->time_magic_wall);
1740   putFile8Bit(file, level->time_wheel);
1741   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1742                      level->amoeba_content));
1743   putFile8Bit(file, (level->double_speed ? 1 : 0));
1744   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1745   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1746   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1747
1748   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1749
1750   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1751 }
1752
1753 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1754 {
1755   int i;
1756
1757   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1758     putFile8Bit(file, level->author[i]);
1759 }
1760
1761 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1762 {
1763   int x, y;
1764
1765   for (y = 0; y < level->fieldy; y++) 
1766     for (x = 0; x < level->fieldx; x++) 
1767       if (level->encoding_16bit_field)
1768         putFile16BitBE(file, level->field[x][y]);
1769       else
1770         putFile8Bit(file, level->field[x][y]);
1771 }
1772
1773 #if 0
1774 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1775 {
1776   int i, x, y;
1777
1778   putFile8Bit(file, EL_YAMYAM);
1779   putFile8Bit(file, level->num_yamyam_contents);
1780   putFile8Bit(file, 0);
1781   putFile8Bit(file, 0);
1782
1783   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1784     for (y = 0; y < 3; y++)
1785       for (x = 0; x < 3; x++)
1786         if (level->encoding_16bit_field)
1787           putFile16BitBE(file, level->yamyam_content[i][x][y]);
1788         else
1789           putFile8Bit(file, level->yamyam_content[i][x][y]);
1790 }
1791 #endif
1792
1793 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1794 {
1795   int i, x, y;
1796   int num_contents, content_xsize, content_ysize;
1797   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1798
1799   if (element == EL_YAMYAM)
1800   {
1801     num_contents = level->num_yamyam_contents;
1802     content_xsize = 3;
1803     content_ysize = 3;
1804
1805     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1806       for (y = 0; y < 3; y++)
1807         for (x = 0; x < 3; x++)
1808           content_array[i][x][y] = level->yamyam_content[i][x][y];
1809   }
1810   else if (element == EL_BD_AMOEBA)
1811   {
1812     num_contents = 1;
1813     content_xsize = 1;
1814     content_ysize = 1;
1815
1816     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1817       for (y = 0; y < 3; y++)
1818         for (x = 0; x < 3; x++)
1819           content_array[i][x][y] = EL_EMPTY;
1820     content_array[0][0][0] = level->amoeba_content;
1821   }
1822   else
1823   {
1824     /* chunk header already written -- write empty chunk data */
1825     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1826
1827     Error(ERR_WARN, "cannot save content for element '%d'", element);
1828     return;
1829   }
1830
1831   putFile16BitBE(file, element);
1832   putFile8Bit(file, num_contents);
1833   putFile8Bit(file, content_xsize);
1834   putFile8Bit(file, content_ysize);
1835
1836   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1837
1838   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1839     for (y = 0; y < 3; y++)
1840       for (x = 0; x < 3; x++)
1841         putFile16BitBE(file, content_array[i][x][y]);
1842 }
1843
1844 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1845 {
1846   int i;
1847   int envelope_nr = element - EL_ENVELOPE_1;
1848   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1849
1850   putFile16BitBE(file, element);
1851   putFile16BitBE(file, envelope_len);
1852   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1853   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1854
1855   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1856
1857   for (i = 0; i < envelope_len; i++)
1858     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1859 }
1860
1861 #if 0
1862 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1863                            int num_changed_custom_elements)
1864 {
1865   int i, check = 0;
1866
1867   putFile16BitBE(file, num_changed_custom_elements);
1868
1869   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1870   {
1871     int element = EL_CUSTOM_START + i;
1872
1873     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1874     {
1875       if (check < num_changed_custom_elements)
1876       {
1877         putFile16BitBE(file, element);
1878         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1879       }
1880
1881       check++;
1882     }
1883   }
1884
1885   if (check != num_changed_custom_elements)     /* should not happen */
1886     Error(ERR_WARN, "inconsistent number of custom element properties");
1887 }
1888 #endif
1889
1890 #if 0
1891 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1892                            int num_changed_custom_elements)
1893 {
1894   int i, check = 0;
1895
1896   putFile16BitBE(file, num_changed_custom_elements);
1897
1898   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1899   {
1900     int element = EL_CUSTOM_START + i;
1901
1902     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1903     {
1904       if (check < num_changed_custom_elements)
1905       {
1906         putFile16BitBE(file, element);
1907         putFile16BitBE(file, element_info[element].change->target_element);
1908       }
1909
1910       check++;
1911     }
1912   }
1913
1914   if (check != num_changed_custom_elements)     /* should not happen */
1915     Error(ERR_WARN, "inconsistent number of custom target elements");
1916 }
1917 #endif
1918
1919 #if 0
1920 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1921                            int num_changed_custom_elements)
1922 {
1923   int i, j, x, y, check = 0;
1924
1925   putFile16BitBE(file, num_changed_custom_elements);
1926
1927   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1928   {
1929     int element = EL_CUSTOM_START + i;
1930
1931     if (element_info[element].modified_settings)
1932     {
1933       if (check < num_changed_custom_elements)
1934       {
1935         putFile16BitBE(file, element);
1936
1937         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1938           putFile8Bit(file, element_info[element].description[j]);
1939
1940         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1941
1942         /* some free bytes for future properties and padding */
1943         WriteUnusedBytesToFile(file, 7);
1944
1945         putFile8Bit(file, element_info[element].use_gfx_element);
1946         putFile16BitBE(file, element_info[element].gfx_element);
1947
1948         putFile8Bit(file, element_info[element].collect_score);
1949         putFile8Bit(file, element_info[element].collect_count);
1950
1951         putFile16BitBE(file, element_info[element].push_delay_fixed);
1952         putFile16BitBE(file, element_info[element].push_delay_random);
1953         putFile16BitBE(file, element_info[element].move_delay_fixed);
1954         putFile16BitBE(file, element_info[element].move_delay_random);
1955
1956         putFile16BitBE(file, element_info[element].move_pattern);
1957         putFile8Bit(file, element_info[element].move_direction_initial);
1958         putFile8Bit(file, element_info[element].move_stepsize);
1959
1960         for (y = 0; y < 3; y++)
1961           for (x = 0; x < 3; x++)
1962             putFile16BitBE(file, element_info[element].content[x][y]);
1963
1964         putFile32BitBE(file, element_info[element].change->events);
1965
1966         putFile16BitBE(file, element_info[element].change->target_element);
1967
1968         putFile16BitBE(file, element_info[element].change->delay_fixed);
1969         putFile16BitBE(file, element_info[element].change->delay_random);
1970         putFile16BitBE(file, element_info[element].change->delay_frames);
1971
1972         putFile16BitBE(file, element_info[element].change->trigger_element);
1973
1974         putFile8Bit(file, element_info[element].change->explode);
1975         putFile8Bit(file, element_info[element].change->use_content);
1976         putFile8Bit(file, element_info[element].change->only_complete);
1977         putFile8Bit(file, element_info[element].change->use_random_change);
1978
1979         putFile8Bit(file, element_info[element].change->random);
1980         putFile8Bit(file, element_info[element].change->power);
1981
1982         for (y = 0; y < 3; y++)
1983           for (x = 0; x < 3; x++)
1984             putFile16BitBE(file, element_info[element].change->content[x][y]);
1985
1986         putFile8Bit(file, element_info[element].slippery_type);
1987
1988         /* some free bytes for future properties and padding */
1989         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1990       }
1991
1992       check++;
1993     }
1994   }
1995
1996   if (check != num_changed_custom_elements)     /* should not happen */
1997     Error(ERR_WARN, "inconsistent number of custom element properties");
1998 }
1999 #endif
2000
2001 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2002 {
2003   struct ElementInfo *ei = &element_info[element];
2004   int i, x, y;
2005
2006   putFile16BitBE(file, element);
2007
2008   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2009     putFile8Bit(file, ei->description[i]);
2010
2011   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2012   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
2013
2014   putFile8Bit(file, ei->num_change_pages);
2015
2016   /* some free bytes for future base property values and padding */
2017   WriteUnusedBytesToFile(file, 5);
2018
2019   /* write custom property values */
2020
2021   putFile8Bit(file, ei->use_gfx_element);
2022   putFile16BitBE(file, ei->gfx_element);
2023
2024   putFile8Bit(file, ei->collect_score);
2025   putFile8Bit(file, ei->collect_count);
2026
2027   putFile16BitBE(file, ei->push_delay_fixed);
2028   putFile16BitBE(file, ei->push_delay_random);
2029   putFile16BitBE(file, ei->move_delay_fixed);
2030   putFile16BitBE(file, ei->move_delay_random);
2031
2032   putFile16BitBE(file, ei->move_pattern);
2033   putFile8Bit(file, ei->move_direction_initial);
2034   putFile8Bit(file, ei->move_stepsize);
2035
2036   putFile8Bit(file, ei->slippery_type);
2037
2038   for (y = 0; y < 3; y++)
2039     for (x = 0; x < 3; x++)
2040       putFile16BitBE(file, ei->content[x][y]);
2041
2042   putFile16BitBE(file, ei->move_enter_element);
2043   putFile16BitBE(file, ei->move_leave_element);
2044
2045   /* some free bytes for future custom property values and padding */
2046   WriteUnusedBytesToFile(file, 8);
2047
2048   /* write change property values */
2049
2050   for (i = 0; i < ei->num_change_pages; i++)
2051   {
2052     struct ElementChangeInfo *change = &ei->change_page[i];
2053
2054     putFile32BitBE(file, change->events);
2055
2056     putFile16BitBE(file, change->target_element);
2057
2058     putFile16BitBE(file, change->delay_fixed);
2059     putFile16BitBE(file, change->delay_random);
2060     putFile16BitBE(file, change->delay_frames);
2061
2062     putFile16BitBE(file, change->trigger_element);
2063
2064     putFile8Bit(file, change->explode);
2065     putFile8Bit(file, change->use_content);
2066     putFile8Bit(file, change->only_complete);
2067     putFile8Bit(file, change->use_random_change);
2068
2069     putFile8Bit(file, change->random);
2070     putFile8Bit(file, change->power);
2071
2072     for (y = 0; y < 3; y++)
2073       for (x = 0; x < 3; x++)
2074         putFile16BitBE(file, change->content[x][y]);
2075
2076     putFile8Bit(file, change->can_change);
2077
2078     putFile8Bit(file, change->sides);
2079
2080     /* some free bytes for future change property values and padding */
2081     WriteUnusedBytesToFile(file, 8);
2082   }
2083 }
2084
2085 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2086 {
2087   struct ElementInfo *ei = &element_info[element];
2088   struct ElementGroupInfo *group = ei->group;
2089   int i;
2090
2091   putFile16BitBE(file, element);
2092
2093   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2094     putFile8Bit(file, ei->description[i]);
2095
2096   putFile8Bit(file, group->num_elements);
2097
2098   putFile8Bit(file, ei->use_gfx_element);
2099   putFile16BitBE(file, ei->gfx_element);
2100
2101   /* some free bytes for future values and padding */
2102   WriteUnusedBytesToFile(file, 4);
2103
2104   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2105     putFile16BitBE(file, group->element[i]);
2106 }
2107
2108 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2109 {
2110   int body_chunk_size;
2111   int i, x, y;
2112   FILE *file;
2113
2114   if (!(file = fopen(filename, MODE_WRITE)))
2115   {
2116     Error(ERR_WARN, "cannot save level file '%s'", filename);
2117     return;
2118   }
2119
2120   level->file_version = FILE_VERSION_ACTUAL;
2121   level->game_version = GAME_VERSION_ACTUAL;
2122
2123   /* check level field for 16-bit elements */
2124   level->encoding_16bit_field = FALSE;
2125   for (y = 0; y < level->fieldy; y++) 
2126     for (x = 0; x < level->fieldx; x++) 
2127       if (level->field[x][y] > 255)
2128         level->encoding_16bit_field = TRUE;
2129
2130   /* check yamyam content for 16-bit elements */
2131   level->encoding_16bit_yamyam = FALSE;
2132   for (i = 0; i < level->num_yamyam_contents; i++)
2133     for (y = 0; y < 3; y++)
2134       for (x = 0; x < 3; x++)
2135         if (level->yamyam_content[i][x][y] > 255)
2136           level->encoding_16bit_yamyam = TRUE;
2137
2138   /* check amoeba content for 16-bit elements */
2139   level->encoding_16bit_amoeba = FALSE;
2140   if (level->amoeba_content > 255)
2141     level->encoding_16bit_amoeba = TRUE;
2142
2143   /* calculate size of "BODY" chunk */
2144   body_chunk_size =
2145     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2146
2147   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2148   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2149
2150   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2151   SaveLevel_VERS(file, level);
2152
2153   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2154   SaveLevel_HEAD(file, level);
2155
2156   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2157   SaveLevel_AUTH(file, level);
2158
2159   putFileChunkBE(file, "BODY", body_chunk_size);
2160   SaveLevel_BODY(file, level);
2161
2162   if (level->encoding_16bit_yamyam ||
2163       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2164   {
2165     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2166     SaveLevel_CNT2(file, level, EL_YAMYAM);
2167   }
2168
2169   if (level->encoding_16bit_amoeba)
2170   {
2171     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2172     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2173   }
2174
2175   /* check for envelope content */
2176   for (i = 0; i < 4; i++)
2177   {
2178     if (strlen(level->envelope_text[i]) > 0)
2179     {
2180       int envelope_len = strlen(level->envelope_text[i]) + 1;
2181
2182       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2183       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2184     }
2185   }
2186
2187   /* check for non-default custom elements (unless using template level) */
2188   if (!level->use_custom_template)
2189   {
2190     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2191     {
2192       int element = EL_CUSTOM_START + i;
2193
2194       if (element_info[element].modified_settings)
2195       {
2196         int num_change_pages = element_info[element].num_change_pages;
2197
2198         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2199         SaveLevel_CUS4(file, level, element);
2200       }
2201     }
2202   }
2203
2204   /* check for non-default group elements (unless using template level) */
2205   if (!level->use_custom_template)
2206   {
2207     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2208     {
2209       int element = EL_GROUP_START + i;
2210
2211       if (element_info[element].modified_settings)
2212       {
2213         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2214         SaveLevel_GRP1(file, level, element);
2215       }
2216     }
2217   }
2218
2219   fclose(file);
2220
2221   SetFilePermissions(filename, PERMS_PRIVATE);
2222 }
2223
2224 void SaveLevel(int nr)
2225 {
2226   char *filename = getDefaultLevelFilename(nr);
2227
2228   SaveLevelFromFilename(&level, filename);
2229 }
2230
2231 void SaveLevelTemplate()
2232 {
2233   char *filename = getDefaultLevelFilename(-1);
2234
2235   SaveLevelFromFilename(&level, filename);
2236 }
2237
2238 void DumpLevel(struct LevelInfo *level)
2239 {
2240   printf_line("-", 79);
2241   printf("Level xxx (file version %08d, game version %08d)\n",
2242          level->file_version, level->game_version);
2243   printf_line("-", 79);
2244
2245   printf("Level Author: '%s'\n", level->author);
2246   printf("Level Title:  '%s'\n", level->name);
2247   printf("\n");
2248   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
2249   printf("\n");
2250   printf("Level Time:  %d seconds\n", level->time);
2251   printf("Gems needed: %d\n", level->gems_needed);
2252   printf("\n");
2253   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
2254   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
2255   printf("Time for Light:      %d seconds\n", level->time_light);
2256   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
2257   printf("\n");
2258   printf("Amoeba Speed: %d\n", level->amoeba_speed);
2259   printf("\n");
2260   printf("Gravity:                %s\n", (level->initial_gravity ? "yes" : "no"));
2261   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
2262   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2263
2264   printf_line("-", 79);
2265 }
2266
2267
2268 /* ========================================================================= */
2269 /* tape file functions                                                       */
2270 /* ========================================================================= */
2271
2272 static void setTapeInfoToDefaults()
2273 {
2274   int i;
2275
2276   /* always start with reliable default values (empty tape) */
2277   TapeErase();
2278
2279   /* default values (also for pre-1.2 tapes) with only the first player */
2280   tape.player_participates[0] = TRUE;
2281   for (i = 1; i < MAX_PLAYERS; i++)
2282     tape.player_participates[i] = FALSE;
2283
2284   /* at least one (default: the first) player participates in every tape */
2285   tape.num_participating_players = 1;
2286
2287   tape.level_nr = level_nr;
2288   tape.counter = 0;
2289   tape.changed = FALSE;
2290
2291   tape.recording = FALSE;
2292   tape.playing = FALSE;
2293   tape.pausing = FALSE;
2294 }
2295
2296 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2297 {
2298   tape->file_version = getFileVersion(file);
2299   tape->game_version = getFileVersion(file);
2300
2301   return chunk_size;
2302 }
2303
2304 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2305 {
2306   int i;
2307
2308   tape->random_seed = getFile32BitBE(file);
2309   tape->date        = getFile32BitBE(file);
2310   tape->length      = getFile32BitBE(file);
2311
2312   /* read header fields that are new since version 1.2 */
2313   if (tape->file_version >= FILE_VERSION_1_2)
2314   {
2315     byte store_participating_players = getFile8Bit(file);
2316     int engine_version;
2317
2318     /* since version 1.2, tapes store which players participate in the tape */
2319     tape->num_participating_players = 0;
2320     for (i = 0; i < MAX_PLAYERS; i++)
2321     {
2322       tape->player_participates[i] = FALSE;
2323
2324       if (store_participating_players & (1 << i))
2325       {
2326         tape->player_participates[i] = TRUE;
2327         tape->num_participating_players++;
2328       }
2329     }
2330
2331     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2332
2333     engine_version = getFileVersion(file);
2334     if (engine_version > 0)
2335       tape->engine_version = engine_version;
2336     else
2337       tape->engine_version = tape->game_version;
2338   }
2339
2340   return chunk_size;
2341 }
2342
2343 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2344 {
2345   int level_identifier_size;
2346   int i;
2347
2348   level_identifier_size = getFile16BitBE(file);
2349
2350   tape->level_identifier =
2351     checked_realloc(tape->level_identifier, level_identifier_size);
2352
2353   for (i = 0; i < level_identifier_size; i++)
2354     tape->level_identifier[i] = getFile8Bit(file);
2355
2356   tape->level_nr = getFile16BitBE(file);
2357
2358   chunk_size = 2 + level_identifier_size + 2;
2359
2360   return chunk_size;
2361 }
2362
2363 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2364 {
2365   int i, j;
2366   int chunk_size_expected =
2367     (tape->num_participating_players + 1) * tape->length;
2368
2369   if (chunk_size_expected != chunk_size)
2370   {
2371     ReadUnusedBytesFromFile(file, chunk_size);
2372     return chunk_size_expected;
2373   }
2374
2375   for (i = 0; i < tape->length; i++)
2376   {
2377     if (i >= MAX_TAPELEN)
2378       break;
2379
2380     for (j = 0; j < MAX_PLAYERS; j++)
2381     {
2382       tape->pos[i].action[j] = MV_NO_MOVING;
2383
2384       if (tape->player_participates[j])
2385         tape->pos[i].action[j] = getFile8Bit(file);
2386     }
2387
2388     tape->pos[i].delay = getFile8Bit(file);
2389
2390     if (tape->file_version == FILE_VERSION_1_0)
2391     {
2392       /* eliminate possible diagonal moves in old tapes */
2393       /* this is only for backward compatibility */
2394
2395       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2396       byte action = tape->pos[i].action[0];
2397       int k, num_moves = 0;
2398
2399       for (k = 0; k<4; k++)
2400       {
2401         if (action & joy_dir[k])
2402         {
2403           tape->pos[i + num_moves].action[0] = joy_dir[k];
2404           if (num_moves > 0)
2405             tape->pos[i + num_moves].delay = 0;
2406           num_moves++;
2407         }
2408       }
2409
2410       if (num_moves > 1)
2411       {
2412         num_moves--;
2413         i += num_moves;
2414         tape->length += num_moves;
2415       }
2416     }
2417     else if (tape->file_version < FILE_VERSION_2_0)
2418     {
2419       /* convert pre-2.0 tapes to new tape format */
2420
2421       if (tape->pos[i].delay > 1)
2422       {
2423         /* action part */
2424         tape->pos[i + 1] = tape->pos[i];
2425         tape->pos[i + 1].delay = 1;
2426
2427         /* delay part */
2428         for (j = 0; j < MAX_PLAYERS; j++)
2429           tape->pos[i].action[j] = MV_NO_MOVING;
2430         tape->pos[i].delay--;
2431
2432         i++;
2433         tape->length++;
2434       }
2435     }
2436
2437     if (feof(file))
2438       break;
2439   }
2440
2441   if (i != tape->length)
2442     chunk_size = (tape->num_participating_players + 1) * i;
2443
2444   return chunk_size;
2445 }
2446
2447 void LoadTapeFromFilename(char *filename)
2448 {
2449   char cookie[MAX_LINE_LEN];
2450   char chunk_name[CHUNK_ID_LEN + 1];
2451   FILE *file;
2452   int chunk_size;
2453
2454   /* always start with reliable default values */
2455   setTapeInfoToDefaults();
2456
2457   if (!(file = fopen(filename, MODE_READ)))
2458     return;
2459
2460   getFileChunkBE(file, chunk_name, NULL);
2461   if (strcmp(chunk_name, "RND1") == 0)
2462   {
2463     getFile32BitBE(file);               /* not used */
2464
2465     getFileChunkBE(file, chunk_name, NULL);
2466     if (strcmp(chunk_name, "TAPE") != 0)
2467     {
2468       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2469       fclose(file);
2470       return;
2471     }
2472   }
2473   else  /* check for pre-2.0 file format with cookie string */
2474   {
2475     strcpy(cookie, chunk_name);
2476     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2477     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2478       cookie[strlen(cookie) - 1] = '\0';
2479
2480     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2481     {
2482       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2483       fclose(file);
2484       return;
2485     }
2486
2487     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2488     {
2489       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2490       fclose(file);
2491       return;
2492     }
2493
2494     /* pre-2.0 tape files have no game version, so use file version here */
2495     tape.game_version = tape.file_version;
2496   }
2497
2498   if (tape.file_version < FILE_VERSION_1_2)
2499   {
2500     /* tape files from versions before 1.2.0 without chunk structure */
2501     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2502     LoadTape_BODY(file, 2 * tape.length,  &tape);
2503   }
2504   else
2505   {
2506     static struct
2507     {
2508       char *name;
2509       int size;
2510       int (*loader)(FILE *, int, struct TapeInfo *);
2511     }
2512     chunk_info[] =
2513     {
2514       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
2515       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
2516       { "INFO", -1,                     LoadTape_INFO },
2517       { "BODY", -1,                     LoadTape_BODY },
2518       {  NULL,  0,                      NULL }
2519     };
2520
2521     while (getFileChunkBE(file, chunk_name, &chunk_size))
2522     {
2523       int i = 0;
2524
2525       while (chunk_info[i].name != NULL &&
2526              strcmp(chunk_name, chunk_info[i].name) != 0)
2527         i++;
2528
2529       if (chunk_info[i].name == NULL)
2530       {
2531         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2532               chunk_name, filename);
2533         ReadUnusedBytesFromFile(file, chunk_size);
2534       }
2535       else if (chunk_info[i].size != -1 &&
2536                chunk_info[i].size != chunk_size)
2537       {
2538         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2539               chunk_size, chunk_name, filename);
2540         ReadUnusedBytesFromFile(file, chunk_size);
2541       }
2542       else
2543       {
2544         /* call function to load this tape chunk */
2545         int chunk_size_expected =
2546           (chunk_info[i].loader)(file, chunk_size, &tape);
2547
2548         /* the size of some chunks cannot be checked before reading other
2549            chunks first (like "HEAD" and "BODY") that contain some header
2550            information, so check them here */
2551         if (chunk_size_expected != chunk_size)
2552         {
2553           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2554                 chunk_size, chunk_name, filename);
2555         }
2556       }
2557     }
2558   }
2559
2560   fclose(file);
2561
2562   tape.length_seconds = GetTapeLength();
2563
2564 #if 0
2565   printf("::: tape game version: %d\n", tape.game_version);
2566   printf("::: tape engine version: %d\n", tape.engine_version);
2567 #endif
2568 }
2569
2570 void LoadTape(int nr)
2571 {
2572   char *filename = getTapeFilename(nr);
2573
2574   LoadTapeFromFilename(filename);
2575 }
2576
2577 void LoadSolutionTape(int nr)
2578 {
2579   char *filename = getSolutionTapeFilename(nr);
2580
2581   LoadTapeFromFilename(filename);
2582 }
2583
2584 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2585 {
2586   putFileVersion(file, tape->file_version);
2587   putFileVersion(file, tape->game_version);
2588 }
2589
2590 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2591 {
2592   int i;
2593   byte store_participating_players = 0;
2594
2595   /* set bits for participating players for compact storage */
2596   for (i = 0; i < MAX_PLAYERS; i++)
2597     if (tape->player_participates[i])
2598       store_participating_players |= (1 << i);
2599
2600   putFile32BitBE(file, tape->random_seed);
2601   putFile32BitBE(file, tape->date);
2602   putFile32BitBE(file, tape->length);
2603
2604   putFile8Bit(file, store_participating_players);
2605
2606   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2607   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2608
2609   putFileVersion(file, tape->engine_version);
2610 }
2611
2612 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2613 {
2614   int level_identifier_size = strlen(tape->level_identifier) + 1;
2615   int i;
2616
2617   putFile16BitBE(file, level_identifier_size);
2618
2619   for (i = 0; i < level_identifier_size; i++)
2620     putFile8Bit(file, tape->level_identifier[i]);
2621
2622   putFile16BitBE(file, tape->level_nr);
2623 }
2624
2625 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2626 {
2627   int i, j;
2628
2629   for (i = 0; i < tape->length; i++)
2630   {
2631     for (j = 0; j < MAX_PLAYERS; j++)
2632       if (tape->player_participates[j])
2633         putFile8Bit(file, tape->pos[i].action[j]);
2634
2635     putFile8Bit(file, tape->pos[i].delay);
2636   }
2637 }
2638
2639 void SaveTape(int nr)
2640 {
2641   char *filename = getTapeFilename(nr);
2642   FILE *file;
2643   boolean new_tape = TRUE;
2644   int num_participating_players = 0;
2645   int info_chunk_size;
2646   int body_chunk_size;
2647   int i;
2648
2649   InitTapeDirectory(leveldir_current->filename);
2650
2651   /* if a tape still exists, ask to overwrite it */
2652   if (access(filename, F_OK) == 0)
2653   {
2654     new_tape = FALSE;
2655     if (!Request("Replace old tape ?", REQ_ASK))
2656       return;
2657   }
2658
2659   if (!(file = fopen(filename, MODE_WRITE)))
2660   {
2661     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2662     return;
2663   }
2664
2665   tape.file_version = FILE_VERSION_ACTUAL;
2666   tape.game_version = GAME_VERSION_ACTUAL;
2667
2668   /* count number of participating players  */
2669   for (i = 0; i < MAX_PLAYERS; i++)
2670     if (tape.player_participates[i])
2671       num_participating_players++;
2672
2673   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2674   body_chunk_size = (num_participating_players + 1) * tape.length;
2675
2676   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2677   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2678
2679   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2680   SaveTape_VERS(file, &tape);
2681
2682   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2683   SaveTape_HEAD(file, &tape);
2684
2685   putFileChunkBE(file, "INFO", info_chunk_size);
2686   SaveTape_INFO(file, &tape);
2687
2688   putFileChunkBE(file, "BODY", body_chunk_size);
2689   SaveTape_BODY(file, &tape);
2690
2691   fclose(file);
2692
2693   SetFilePermissions(filename, PERMS_PRIVATE);
2694
2695   tape.changed = FALSE;
2696
2697   if (new_tape)
2698     Request("tape saved !", REQ_CONFIRM);
2699 }
2700
2701 void DumpTape(struct TapeInfo *tape)
2702 {
2703   int i, j;
2704
2705   if (TAPE_IS_EMPTY(*tape))
2706   {
2707     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2708     return;
2709   }
2710
2711   printf_line("-", 79);
2712   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2713          tape->level_nr, tape->file_version, tape->game_version);
2714   printf("Level series identifier: '%s'\n", tape->level_identifier);
2715   printf_line("-", 79);
2716
2717   for (i = 0; i < tape->length; i++)
2718   {
2719     if (i >= MAX_TAPELEN)
2720       break;
2721
2722     printf("%03d: ", i);
2723
2724     for (j = 0; j < MAX_PLAYERS; j++)
2725     {
2726       if (tape->player_participates[j])
2727       {
2728         int action = tape->pos[i].action[j];
2729
2730         printf("%d:%02x ", j, action);
2731         printf("[%c%c%c%c|%c%c] - ",
2732                (action & JOY_LEFT ? '<' : ' '),
2733                (action & JOY_RIGHT ? '>' : ' '),
2734                (action & JOY_UP ? '^' : ' '),
2735                (action & JOY_DOWN ? 'v' : ' '),
2736                (action & JOY_BUTTON_1 ? '1' : ' '),
2737                (action & JOY_BUTTON_2 ? '2' : ' '));
2738       }
2739     }
2740
2741     printf("(%03d)\n", tape->pos[i].delay);
2742   }
2743
2744   printf_line("-", 79);
2745 }
2746
2747
2748 /* ========================================================================= */
2749 /* score file functions                                                      */
2750 /* ========================================================================= */
2751
2752 void LoadScore(int nr)
2753 {
2754   int i;
2755   char *filename = getScoreFilename(nr);
2756   char cookie[MAX_LINE_LEN];
2757   char line[MAX_LINE_LEN];
2758   char *line_ptr;
2759   FILE *file;
2760
2761   /* always start with reliable default values */
2762   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2763   {
2764     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2765     highscore[i].Score = 0;
2766   }
2767
2768   if (!(file = fopen(filename, MODE_READ)))
2769     return;
2770
2771   /* check file identifier */
2772   fgets(cookie, MAX_LINE_LEN, file);
2773   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2774     cookie[strlen(cookie) - 1] = '\0';
2775
2776   if (!checkCookieString(cookie, SCORE_COOKIE))
2777   {
2778     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2779     fclose(file);
2780     return;
2781   }
2782
2783   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2784   {
2785     fscanf(file, "%d", &highscore[i].Score);
2786     fgets(line, MAX_LINE_LEN, file);
2787
2788     if (line[strlen(line) - 1] == '\n')
2789       line[strlen(line) - 1] = '\0';
2790
2791     for (line_ptr = line; *line_ptr; line_ptr++)
2792     {
2793       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2794       {
2795         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2796         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2797         break;
2798       }
2799     }
2800   }
2801
2802   fclose(file);
2803 }
2804
2805 void SaveScore(int nr)
2806 {
2807   int i;
2808   char *filename = getScoreFilename(nr);
2809   FILE *file;
2810
2811   InitScoreDirectory(leveldir_current->filename);
2812
2813   if (!(file = fopen(filename, MODE_WRITE)))
2814   {
2815     Error(ERR_WARN, "cannot save score for level %d", nr);
2816     return;
2817   }
2818
2819   fprintf(file, "%s\n\n", SCORE_COOKIE);
2820
2821   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2822     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2823
2824   fclose(file);
2825
2826   SetFilePermissions(filename, PERMS_PUBLIC);
2827 }
2828
2829
2830 /* ========================================================================= */
2831 /* setup file functions                                                      */
2832 /* ========================================================================= */
2833
2834 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2835
2836 /* global setup */
2837 #define SETUP_TOKEN_PLAYER_NAME                 0
2838 #define SETUP_TOKEN_SOUND                       1
2839 #define SETUP_TOKEN_SOUND_LOOPS                 2
2840 #define SETUP_TOKEN_SOUND_MUSIC                 3
2841 #define SETUP_TOKEN_SOUND_SIMPLE                4
2842 #define SETUP_TOKEN_TOONS                       5
2843 #define SETUP_TOKEN_SCROLL_DELAY                6
2844 #define SETUP_TOKEN_SOFT_SCROLLING              7
2845 #define SETUP_TOKEN_FADING                      8
2846 #define SETUP_TOKEN_AUTORECORD                  9
2847 #define SETUP_TOKEN_QUICK_DOORS                 10
2848 #define SETUP_TOKEN_TEAM_MODE                   11
2849 #define SETUP_TOKEN_HANDICAP                    12
2850 #define SETUP_TOKEN_TIME_LIMIT                  13
2851 #define SETUP_TOKEN_FULLSCREEN                  14
2852 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2853 #define SETUP_TOKEN_GRAPHICS_SET                16
2854 #define SETUP_TOKEN_SOUNDS_SET                  17
2855 #define SETUP_TOKEN_MUSIC_SET                   18
2856 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2857 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2858 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2859
2860 #define NUM_GLOBAL_SETUP_TOKENS                 22
2861
2862 /* editor setup */
2863 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2864 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2865 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2866 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2867 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2868 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2869 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2870 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2871 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2872 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2873 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2874 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
2875
2876 #define NUM_EDITOR_SETUP_TOKENS                 12
2877
2878 /* shortcut setup */
2879 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2880 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2881 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2882
2883 #define NUM_SHORTCUT_SETUP_TOKENS               3
2884
2885 /* player setup */
2886 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2887 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2888 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2889 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2890 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2891 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2892 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2893 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2894 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2895 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
2896 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2897 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2898 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2899 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2900 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2901 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
2902
2903 #define NUM_PLAYER_SETUP_TOKENS                 16
2904
2905 /* system setup */
2906 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2907 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2908
2909 #define NUM_SYSTEM_SETUP_TOKENS                 2
2910
2911 /* options setup */
2912 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2913
2914 #define NUM_OPTIONS_SETUP_TOKENS                1
2915
2916
2917 static struct SetupInfo si;
2918 static struct SetupEditorInfo sei;
2919 static struct SetupShortcutInfo ssi;
2920 static struct SetupInputInfo sii;
2921 static struct SetupSystemInfo syi;
2922 static struct OptionInfo soi;
2923
2924 static struct TokenInfo global_setup_tokens[] =
2925 {
2926   { TYPE_STRING, &si.player_name,       "player_name"                   },
2927   { TYPE_SWITCH, &si.sound,             "sound"                         },
2928   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2929   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2930   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2931   { TYPE_SWITCH, &si.toons,             "toons"                         },
2932   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2933   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2934   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2935   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2936   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2937   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2938   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2939   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2940   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2941   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2942   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2943   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2944   { TYPE_STRING, &si.music_set,         "music_set"                     },
2945   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2946   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2947   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2948 };
2949
2950 static struct TokenInfo editor_setup_tokens[] =
2951 {
2952   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2953   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2954   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2955   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2956   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2957   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2958   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2959   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2960   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2961   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2962   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2963   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
2964 };
2965
2966 static struct TokenInfo shortcut_setup_tokens[] =
2967 {
2968   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2969   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2970   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2971 };
2972
2973 static struct TokenInfo player_setup_tokens[] =
2974 {
2975   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2976   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2977   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2978   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2979   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2980   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2981   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2982   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2983   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2984   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
2985   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2986   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2987   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2988   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2989   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2990   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               }
2991 };
2992
2993 static struct TokenInfo system_setup_tokens[] =
2994 {
2995   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2996   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2997 };
2998
2999 static struct TokenInfo options_setup_tokens[] =
3000 {
3001   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
3002 };
3003
3004 static char *get_corrected_login_name(char *login_name)
3005 {
3006   /* needed because player name must be a fixed length string */
3007   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3008
3009   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3010   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3011
3012   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
3013     if (strchr(login_name_new, ' '))
3014       *strchr(login_name_new, ' ') = '\0';
3015
3016   return login_name_new;
3017 }
3018
3019 static void setSetupInfoToDefaults(struct SetupInfo *si)
3020 {
3021   int i;
3022
3023   si->player_name = get_corrected_login_name(getLoginName());
3024
3025   si->sound = TRUE;
3026   si->sound_loops = TRUE;
3027   si->sound_music = TRUE;
3028   si->sound_simple = TRUE;
3029   si->toons = TRUE;
3030   si->double_buffering = TRUE;
3031   si->direct_draw = !si->double_buffering;
3032   si->scroll_delay = TRUE;
3033   si->soft_scrolling = TRUE;
3034   si->fading = FALSE;
3035   si->autorecord = TRUE;
3036   si->quick_doors = FALSE;
3037   si->team_mode = FALSE;
3038   si->handicap = TRUE;
3039   si->time_limit = TRUE;
3040   si->fullscreen = FALSE;
3041   si->ask_on_escape = TRUE;
3042
3043   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3044   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3045   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3046   si->override_level_graphics = FALSE;
3047   si->override_level_sounds = FALSE;
3048   si->override_level_music = FALSE;
3049
3050   si->editor.el_boulderdash = TRUE;
3051   si->editor.el_emerald_mine = TRUE;
3052   si->editor.el_more = TRUE;
3053   si->editor.el_sokoban = TRUE;
3054   si->editor.el_supaplex = TRUE;
3055   si->editor.el_diamond_caves = TRUE;
3056   si->editor.el_dx_boulderdash = TRUE;
3057   si->editor.el_chars = TRUE;
3058   si->editor.el_custom = TRUE;
3059   si->editor.el_custom_more = FALSE;
3060
3061   si->editor.el_headlines = TRUE;
3062   si->editor.el_user_defined = FALSE;
3063
3064   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3065   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3066   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3067
3068   for (i = 0; i < MAX_PLAYERS; i++)
3069   {
3070     si->input[i].use_joystick = FALSE;
3071     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3072     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
3073     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3074     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
3075     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
3076     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3077     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
3078     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
3079     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
3080     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
3081     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3082     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
3083     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
3084     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
3085     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
3086   }
3087
3088   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3089   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3090
3091   si->options.verbose = FALSE;
3092 }
3093
3094 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3095 {
3096   int i, pnr;
3097
3098   if (!setup_file_hash)
3099     return;
3100
3101   /* global setup */
3102   si = setup;
3103   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3104     setSetupInfo(global_setup_tokens, i,
3105                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3106   setup = si;
3107
3108   /* editor setup */
3109   sei = setup.editor;
3110   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3111     setSetupInfo(editor_setup_tokens, i,
3112                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3113   setup.editor = sei;
3114
3115   /* shortcut setup */
3116   ssi = setup.shortcut;
3117   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3118     setSetupInfo(shortcut_setup_tokens, i,
3119                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3120   setup.shortcut = ssi;
3121
3122   /* player setup */
3123   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3124   {
3125     char prefix[30];
3126
3127     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3128
3129     sii = setup.input[pnr];
3130     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3131     {
3132       char full_token[100];
3133
3134       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3135       setSetupInfo(player_setup_tokens, i,
3136                    getHashEntry(setup_file_hash, full_token));
3137     }
3138     setup.input[pnr] = sii;
3139   }
3140
3141   /* system setup */
3142   syi = setup.system;
3143   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3144     setSetupInfo(system_setup_tokens, i,
3145                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3146   setup.system = syi;
3147
3148   /* options setup */
3149   soi = setup.options;
3150   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3151     setSetupInfo(options_setup_tokens, i,
3152                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3153   setup.options = soi;
3154 }
3155
3156 void LoadSetup()
3157 {
3158   char *filename = getSetupFilename();
3159   SetupFileHash *setup_file_hash = NULL;
3160
3161   /* always start with reliable default values */
3162   setSetupInfoToDefaults(&setup);
3163
3164   setup_file_hash = loadSetupFileHash(filename);
3165
3166   if (setup_file_hash)
3167   {
3168     char *player_name_new;
3169
3170     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3171     decodeSetupFileHash(setup_file_hash);
3172
3173     setup.direct_draw = !setup.double_buffering;
3174
3175     freeSetupFileHash(setup_file_hash);
3176
3177     /* needed to work around problems with fixed length strings */
3178     player_name_new = get_corrected_login_name(setup.player_name);
3179     free(setup.player_name);
3180     setup.player_name = player_name_new;
3181   }
3182   else
3183     Error(ERR_WARN, "using default setup values");
3184 }
3185
3186 void SaveSetup()
3187 {
3188   char *filename = getSetupFilename();
3189   FILE *file;
3190   int i, pnr;
3191
3192   InitUserDataDirectory();
3193
3194   if (!(file = fopen(filename, MODE_WRITE)))
3195   {
3196     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3197     return;
3198   }
3199
3200   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3201                                                getCookie("SETUP")));
3202   fprintf(file, "\n");
3203
3204   /* global setup */
3205   si = setup;
3206   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3207   {
3208     /* just to make things nicer :) */
3209     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3210         i == SETUP_TOKEN_GRAPHICS_SET)
3211       fprintf(file, "\n");
3212
3213     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3214   }
3215
3216   /* editor setup */
3217   sei = setup.editor;
3218   fprintf(file, "\n");
3219   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3220     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3221
3222   /* shortcut setup */
3223   ssi = setup.shortcut;
3224   fprintf(file, "\n");
3225   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3226     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3227
3228   /* player setup */
3229   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3230   {
3231     char prefix[30];
3232
3233     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3234     fprintf(file, "\n");
3235
3236     sii = setup.input[pnr];
3237     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3238       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3239   }
3240
3241   /* system setup */
3242   syi = setup.system;
3243   fprintf(file, "\n");
3244   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3245     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3246
3247   /* options setup */
3248   soi = setup.options;
3249   fprintf(file, "\n");
3250   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3251     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3252
3253   fclose(file);
3254
3255   SetFilePermissions(filename, PERMS_PRIVATE);
3256 }
3257
3258 void LoadCustomElementDescriptions()
3259 {
3260   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3261   SetupFileHash *setup_file_hash;
3262   int i;
3263
3264   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3265   {
3266     if (element_info[i].custom_description != NULL)
3267     {
3268       free(element_info[i].custom_description);
3269       element_info[i].custom_description = NULL;
3270     }
3271   }
3272
3273   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3274     return;
3275
3276   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3277   {
3278     char *token = getStringCat2(element_info[i].token_name, ".name");
3279     char *value = getHashEntry(setup_file_hash, token);
3280
3281     if (value != NULL)
3282       element_info[i].custom_description = getStringCopy(value);
3283
3284     free(token);
3285   }
3286
3287   freeSetupFileHash(setup_file_hash);
3288 }
3289
3290 void LoadSpecialMenuDesignSettings()
3291 {
3292   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3293   SetupFileHash *setup_file_hash;
3294   int i, j;
3295
3296   /* always start with reliable default values from default config */
3297   for (i = 0; image_config_vars[i].token != NULL; i++)
3298     for (j = 0; image_config[j].token != NULL; j++)
3299       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3300         *image_config_vars[i].value =
3301           get_auto_parameter_value(image_config_vars[i].token,
3302                                    image_config[j].value);
3303
3304   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3305     return;
3306
3307   /* special case: initialize with default values that may be overwritten */
3308   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3309   {
3310     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3311     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3312     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3313
3314     if (value_x != NULL)
3315       menu.draw_xoffset[i] = get_integer_from_string(value_x);
3316     if (value_y != NULL)
3317       menu.draw_yoffset[i] = get_integer_from_string(value_y);
3318     if (list_size != NULL)
3319       menu.list_size[i] = get_integer_from_string(list_size);
3320   }
3321
3322   /* read (and overwrite with) values that may be specified in config file */
3323   for (i = 0; image_config_vars[i].token != NULL; i++)
3324   {
3325     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3326
3327     if (value != NULL)
3328       *image_config_vars[i].value =
3329         get_auto_parameter_value(image_config_vars[i].token, value);
3330   }
3331
3332   freeSetupFileHash(setup_file_hash);
3333 }
3334
3335 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3336 {
3337   char *filename = getEditorSetupFilename();
3338   SetupFileList *setup_file_list, *list;
3339   SetupFileHash *element_hash;
3340   int num_unknown_tokens = 0;
3341   int i;
3342
3343   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3344     return;
3345
3346   element_hash = newSetupFileHash();
3347
3348   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3349     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3350
3351   /* determined size may be larger than needed (due to unknown elements) */
3352   *num_elements = 0;
3353   for (list = setup_file_list; list != NULL; list = list->next)
3354     (*num_elements)++;
3355
3356   /* add space for up to 3 more elements for padding that may be needed */
3357   *num_elements += 3;
3358
3359   *elements = checked_malloc(*num_elements * sizeof(int));
3360
3361   *num_elements = 0;
3362   for (list = setup_file_list; list != NULL; list = list->next)
3363   {
3364     char *value = getHashEntry(element_hash, list->token);
3365
3366     if (value)
3367     {
3368       (*elements)[(*num_elements)++] = atoi(value);
3369     }
3370     else
3371     {
3372       if (num_unknown_tokens == 0)
3373       {
3374         Error(ERR_RETURN_LINE, "-");
3375         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3376         Error(ERR_RETURN, "- config file: '%s'", filename);
3377
3378         num_unknown_tokens++;
3379       }
3380
3381       Error(ERR_RETURN, "- token: '%s'", list->token);
3382     }
3383   }
3384
3385   if (num_unknown_tokens > 0)
3386     Error(ERR_RETURN_LINE, "-");
3387
3388   while (*num_elements % 4)     /* pad with empty elements, if needed */
3389     (*elements)[(*num_elements)++] = EL_EMPTY;
3390
3391   freeSetupFileList(setup_file_list);
3392   freeSetupFileHash(element_hash);
3393
3394 #if 0
3395   /* TEST-ONLY */
3396   for (i = 0; i < *num_elements; i++)
3397     printf("editor: element '%s' [%d]\n",
3398            element_info[(*elements)[i]].token_name, (*elements)[i]);
3399 #endif
3400 }
3401
3402 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3403                                                      boolean is_sound)
3404 {
3405   SetupFileHash *setup_file_hash = NULL;
3406   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3407   char *filename_music, *filename_prefix, *filename_info;
3408   struct
3409   {
3410     char *token;
3411     char **value_ptr;
3412   }
3413   token_to_value_ptr[] =
3414   {
3415     { "title_header",   &tmp_music_file_info.title_header       },
3416     { "artist_header",  &tmp_music_file_info.artist_header      },
3417     { "album_header",   &tmp_music_file_info.album_header       },
3418     { "year_header",    &tmp_music_file_info.year_header        },
3419
3420     { "title",          &tmp_music_file_info.title              },
3421     { "artist",         &tmp_music_file_info.artist             },
3422     { "album",          &tmp_music_file_info.album              },
3423     { "year",           &tmp_music_file_info.year               },
3424
3425     { NULL,             NULL                                    },
3426   };
3427   int i;
3428
3429   filename_music = (is_sound ? getCustomSoundFilename(basename) :
3430                     getCustomMusicFilename(basename));
3431
3432   if (filename_music == NULL)
3433     return NULL;
3434
3435   /* ---------- try to replace file extension ---------- */
3436
3437   filename_prefix = getStringCopy(filename_music);
3438   if (strrchr(filename_prefix, '.') != NULL)
3439     *strrchr(filename_prefix, '.') = '\0';
3440   filename_info = getStringCat2(filename_prefix, ".txt");
3441
3442 #if 0
3443   printf("trying to load file '%s'...\n", filename_info);
3444 #endif
3445
3446   if (fileExists(filename_info))
3447     setup_file_hash = loadSetupFileHash(filename_info);
3448
3449   free(filename_prefix);
3450   free(filename_info);
3451
3452   if (setup_file_hash == NULL)
3453   {
3454     /* ---------- try to add file extension ---------- */
3455
3456     filename_prefix = getStringCopy(filename_music);
3457     filename_info = getStringCat2(filename_prefix, ".txt");
3458
3459 #if 0
3460     printf("trying to load file '%s'...\n", filename_info);
3461 #endif
3462
3463     if (fileExists(filename_info))
3464       setup_file_hash = loadSetupFileHash(filename_info);
3465
3466     free(filename_prefix);
3467     free(filename_info);
3468   }
3469
3470   if (setup_file_hash == NULL)
3471     return NULL;
3472
3473   /* ---------- music file info found ---------- */
3474
3475   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3476
3477   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3478   {
3479     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3480
3481     *token_to_value_ptr[i].value_ptr =
3482       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3483   }
3484
3485   tmp_music_file_info.basename = getStringCopy(basename);
3486   tmp_music_file_info.music = music;
3487   tmp_music_file_info.is_sound = is_sound;
3488
3489   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3490   *new_music_file_info = tmp_music_file_info;
3491
3492   return new_music_file_info;
3493 }
3494
3495 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3496 {
3497   return get_music_file_info_ext(basename, music, FALSE);
3498 }
3499
3500 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3501 {
3502   return get_music_file_info_ext(basename, sound, TRUE);
3503 }
3504
3505 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3506                                      char *basename, boolean is_sound)
3507 {
3508   for (; list != NULL; list = list->next)
3509     if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3510       return TRUE;
3511
3512   return FALSE;
3513 }
3514
3515 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3516 {
3517   return music_info_listed_ext(list, basename, FALSE);
3518 }
3519
3520 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3521 {
3522   return music_info_listed_ext(list, basename, TRUE);
3523 }
3524
3525 void LoadMusicInfo()
3526 {
3527   char *music_directory = getCustomMusicDirectory();
3528   int num_music = getMusicListSize();
3529   int num_music_noconf = 0;
3530   int num_sounds = getSoundListSize();
3531   DIR *dir;
3532   struct dirent *dir_entry;
3533   struct FileInfo *music, *sound;
3534   struct MusicFileInfo *next, **new;
3535   int i;
3536
3537   while (music_file_info != NULL)
3538   {
3539     next = music_file_info->next;
3540
3541     checked_free(music_file_info->basename);
3542
3543     checked_free(music_file_info->title_header);
3544     checked_free(music_file_info->artist_header);
3545     checked_free(music_file_info->album_header);
3546     checked_free(music_file_info->year_header);
3547
3548     checked_free(music_file_info->title);
3549     checked_free(music_file_info->artist);
3550     checked_free(music_file_info->album);
3551     checked_free(music_file_info->year);
3552
3553     free(music_file_info);
3554
3555     music_file_info = next;
3556   }
3557
3558   new = &music_file_info;
3559
3560 #if 0
3561   printf("::: num_music == %d\n", num_music);
3562 #endif
3563
3564   for (i = 0; i < num_music; i++)
3565   {
3566     music = getMusicListEntry(i);
3567
3568 #if 0
3569     printf("::: %d [%08x]\n", i, music->filename);
3570 #endif
3571
3572     if (music->filename == NULL)
3573       continue;
3574
3575     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3576       continue;
3577
3578     /* a configured file may be not recognized as music */
3579     if (!FileIsMusic(music->filename))
3580       continue;
3581
3582 #if 0
3583     printf("::: -> '%s' (configured)\n", music->filename);
3584 #endif
3585
3586     if (!music_info_listed(music_file_info, music->filename))
3587     {
3588       *new = get_music_file_info(music->filename, i);
3589       if (*new != NULL)
3590         new = &(*new)->next;
3591     }
3592   }
3593
3594   if ((dir = opendir(music_directory)) == NULL)
3595   {
3596     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3597     return;
3598   }
3599
3600   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
3601   {
3602     char *basename = dir_entry->d_name;
3603     boolean music_already_used = FALSE;
3604     int i;
3605
3606     /* skip all music files that are configured in music config file */
3607     for (i = 0; i < num_music; i++)
3608     {
3609       music = getMusicListEntry(i);
3610
3611       if (music->filename == NULL)
3612         continue;
3613
3614       if (strcmp(basename, music->filename) == 0)
3615       {
3616         music_already_used = TRUE;
3617         break;
3618       }
3619     }
3620
3621     if (music_already_used)
3622       continue;
3623
3624     if (!FileIsMusic(basename))
3625       continue;
3626
3627 #if 0
3628     printf("::: -> '%s' (found in directory)\n", basename);
3629 #endif
3630
3631     if (!music_info_listed(music_file_info, basename))
3632     {
3633       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3634       if (*new != NULL)
3635         new = &(*new)->next;
3636     }
3637
3638     num_music_noconf++;
3639   }
3640
3641   closedir(dir);
3642
3643   for (i = 0; i < num_sounds; i++)
3644   {
3645     sound = getSoundListEntry(i);
3646
3647     if (sound->filename == NULL)
3648       continue;
3649
3650     if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3651       continue;
3652
3653     /* a configured file may be not recognized as sound */
3654     if (!FileIsSound(sound->filename))
3655       continue;
3656
3657 #if 0
3658     printf("::: -> '%s' (configured)\n", sound->filename);
3659 #endif
3660
3661     if (!sound_info_listed(music_file_info, sound->filename))
3662     {
3663       *new = get_sound_file_info(sound->filename, i);
3664       if (*new != NULL)
3665         new = &(*new)->next;
3666     }
3667   }
3668
3669 #if 0
3670   /* TEST-ONLY */
3671   for (next = music_file_info; next != NULL; next = next->next)
3672     printf("::: title == '%s'\n", next->title);
3673 #endif
3674 }
3675
3676 void add_helpanim_entry(int element, int action, int direction, int delay,
3677                         int *num_list_entries)
3678 {
3679   struct HelpAnimInfo *new_list_entry;
3680   (*num_list_entries)++;
3681
3682   helpanim_info =
3683     checked_realloc(helpanim_info,
3684                     *num_list_entries * sizeof(struct HelpAnimInfo));
3685   new_list_entry = &helpanim_info[*num_list_entries - 1];
3686
3687   new_list_entry->element = element;
3688   new_list_entry->action = action;
3689   new_list_entry->direction = direction;
3690   new_list_entry->delay = delay;
3691 }
3692
3693 void print_unknown_token(char *filename, char *token, int token_nr)
3694 {
3695   if (token_nr == 0)
3696   {
3697     Error(ERR_RETURN_LINE, "-");
3698     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3699     Error(ERR_RETURN, "- config file: '%s'", filename);
3700   }
3701
3702   Error(ERR_RETURN, "- token: '%s'", token);
3703 }
3704
3705 void print_unknown_token_end(int token_nr)
3706 {
3707   if (token_nr > 0)
3708     Error(ERR_RETURN_LINE, "-");
3709 }
3710
3711 void LoadHelpAnimInfo()
3712 {
3713   char *filename = getHelpAnimFilename();
3714   SetupFileList *setup_file_list = NULL, *list;
3715   SetupFileHash *element_hash, *action_hash, *direction_hash;
3716   int num_list_entries = 0;
3717   int num_unknown_tokens = 0;
3718   int i;
3719
3720   if (fileExists(filename))
3721     setup_file_list = loadSetupFileList(filename);
3722
3723   if (setup_file_list == NULL)
3724   {
3725     /* use reliable default values from static configuration */
3726     SetupFileList *insert_ptr;
3727
3728     insert_ptr = setup_file_list =
3729       newSetupFileList(helpanim_config[0].token,
3730                        helpanim_config[0].value);
3731
3732     for (i = 1; helpanim_config[i].token; i++)
3733       insert_ptr = addListEntry(insert_ptr,
3734                                 helpanim_config[i].token,
3735                                 helpanim_config[i].value);
3736   }
3737
3738   element_hash   = newSetupFileHash();
3739   action_hash    = newSetupFileHash();
3740   direction_hash = newSetupFileHash();
3741
3742   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3743     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3744
3745   for (i = 0; i < NUM_ACTIONS; i++)
3746     setHashEntry(action_hash, element_action_info[i].suffix,
3747                  i_to_a(element_action_info[i].value));
3748
3749   /* do not store direction index (bit) here, but direction value! */
3750   for (i = 0; i < NUM_DIRECTIONS; i++)
3751     setHashEntry(direction_hash, element_direction_info[i].suffix,
3752                  i_to_a(1 << element_direction_info[i].value));
3753
3754   for (list = setup_file_list; list != NULL; list = list->next)
3755   {
3756     char *element_token, *action_token, *direction_token;
3757     char *element_value, *action_value, *direction_value;
3758     int delay = atoi(list->value);
3759
3760     if (strcmp(list->token, "end") == 0)
3761     {
3762       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3763
3764       continue;
3765     }
3766
3767     /* first try to break element into element/action/direction parts;
3768        if this does not work, also accept combined "element[.act][.dir]"
3769        elements (like "dynamite.active"), which are unique elements */
3770
3771     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
3772     {
3773       element_value = getHashEntry(element_hash, list->token);
3774       if (element_value != NULL)        /* element found */
3775         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3776                            &num_list_entries);
3777       else
3778       {
3779         /* no further suffixes found -- this is not an element */
3780         print_unknown_token(filename, list->token, num_unknown_tokens++);
3781       }
3782
3783       continue;
3784     }
3785
3786     /* token has format "<prefix>.<something>" */
3787
3788     action_token = strchr(list->token, '.');    /* suffix may be action ... */
3789     direction_token = action_token;             /* ... or direction */
3790
3791     element_token = getStringCopy(list->token);
3792     *strchr(element_token, '.') = '\0';
3793
3794     element_value = getHashEntry(element_hash, element_token);
3795
3796     if (element_value == NULL)          /* this is no element */
3797     {
3798       element_value = getHashEntry(element_hash, list->token);
3799       if (element_value != NULL)        /* combined element found */
3800         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3801                            &num_list_entries);
3802       else
3803         print_unknown_token(filename, list->token, num_unknown_tokens++);
3804
3805       free(element_token);
3806
3807       continue;
3808     }
3809
3810     action_value = getHashEntry(action_hash, action_token);
3811
3812     if (action_value != NULL)           /* action found */
3813     {
3814       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3815                     &num_list_entries);
3816
3817       free(element_token);
3818
3819       continue;
3820     }
3821
3822     direction_value = getHashEntry(direction_hash, direction_token);
3823
3824     if (direction_value != NULL)        /* direction found */
3825     {
3826       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3827                          &num_list_entries);
3828
3829       free(element_token);
3830
3831       continue;
3832     }
3833
3834     if (strchr(action_token + 1, '.') == NULL)
3835     {
3836       /* no further suffixes found -- this is not an action nor direction */
3837
3838       element_value = getHashEntry(element_hash, list->token);
3839       if (element_value != NULL)        /* combined element found */
3840         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3841                            &num_list_entries);
3842       else
3843         print_unknown_token(filename, list->token, num_unknown_tokens++);
3844
3845       free(element_token);
3846
3847       continue;
3848     }
3849
3850     /* token has format "<prefix>.<suffix>.<something>" */
3851
3852     direction_token = strchr(action_token + 1, '.');
3853
3854     action_token = getStringCopy(action_token);
3855     *strchr(action_token + 1, '.') = '\0';
3856
3857     action_value = getHashEntry(action_hash, action_token);
3858
3859     if (action_value == NULL)           /* this is no action */
3860     {
3861       element_value = getHashEntry(element_hash, list->token);
3862       if (element_value != NULL)        /* combined element found */
3863         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3864                            &num_list_entries);
3865       else
3866         print_unknown_token(filename, list->token, num_unknown_tokens++);
3867
3868       free(element_token);
3869       free(action_token);
3870
3871       continue;
3872     }
3873
3874     direction_value = getHashEntry(direction_hash, direction_token);
3875
3876     if (direction_value != NULL)        /* direction found */
3877     {
3878       add_helpanim_entry(atoi(element_value), atoi(action_value),
3879                          atoi(direction_value), delay, &num_list_entries);
3880
3881       free(element_token);
3882       free(action_token);
3883
3884       continue;
3885     }
3886
3887     /* this is no direction */
3888
3889     element_value = getHashEntry(element_hash, list->token);
3890     if (element_value != NULL)          /* combined element found */
3891       add_helpanim_entry(atoi(element_value), -1, -1, delay,
3892                          &num_list_entries);
3893     else
3894       print_unknown_token(filename, list->token, num_unknown_tokens++);
3895
3896     free(element_token);
3897     free(action_token);
3898   }
3899
3900   print_unknown_token_end(num_unknown_tokens);
3901
3902   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3903   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
3904
3905   freeSetupFileList(setup_file_list);
3906   freeSetupFileHash(element_hash);
3907   freeSetupFileHash(action_hash);
3908   freeSetupFileHash(direction_hash);
3909
3910 #if 0
3911   /* TEST ONLY */
3912   for (i = 0; i < num_list_entries; i++)
3913     printf("::: %d, %d, %d => %d\n",
3914            helpanim_info[i].element,
3915            helpanim_info[i].action,
3916            helpanim_info[i].direction,
3917            helpanim_info[i].delay);
3918 #endif
3919 }
3920
3921 void LoadHelpTextInfo()
3922 {
3923   char *filename = getHelpTextFilename();
3924   int i;
3925
3926   if (helptext_info != NULL)
3927   {
3928     freeSetupFileHash(helptext_info);
3929     helptext_info = NULL;
3930   }
3931
3932   if (fileExists(filename))
3933     helptext_info = loadSetupFileHash(filename);
3934
3935   if (helptext_info == NULL)
3936   {
3937     /* use reliable default values from static configuration */
3938     helptext_info = newSetupFileHash();
3939
3940     for (i = 0; helptext_config[i].token; i++)
3941       setHashEntry(helptext_info,
3942                    helptext_config[i].token,
3943                    helptext_config[i].value);
3944   }
3945
3946 #if 0
3947   /* TEST ONLY */
3948   BEGIN_HASH_ITERATION(helptext_info, itr)
3949   {
3950     printf("::: '%s' => '%s'\n",
3951            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3952   }
3953   END_HASH_ITERATION(hash, itr)
3954 #endif
3955 }