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