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