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