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