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