e65f387a3c461528dcd31064b5eaf06cf1d620bc
[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; 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; 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) ||
1062         HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1063     {
1064       SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1065       SET_CHANGE_EVENT(element, CE_BY_COLLISION, 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 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2015 {
2016   putFileVersion(file, tape->file_version);
2017   putFileVersion(file, tape->game_version);
2018 }
2019
2020 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2021 {
2022   int i;
2023   byte store_participating_players = 0;
2024
2025   /* set bits for participating players for compact storage */
2026   for(i=0; i<MAX_PLAYERS; i++)
2027     if (tape->player_participates[i])
2028       store_participating_players |= (1 << i);
2029
2030   putFile32BitBE(file, tape->random_seed);
2031   putFile32BitBE(file, tape->date);
2032   putFile32BitBE(file, tape->length);
2033
2034   putFile8Bit(file, store_participating_players);
2035
2036   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2037   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2038
2039   putFileVersion(file, tape->engine_version);
2040 }
2041
2042 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2043 {
2044   int level_identifier_size = strlen(tape->level_identifier) + 1;
2045   int i;
2046
2047   putFile16BitBE(file, level_identifier_size);
2048
2049   for(i=0; i < level_identifier_size; i++)
2050     putFile8Bit(file, tape->level_identifier[i]);
2051
2052   putFile16BitBE(file, tape->level_nr);
2053 }
2054
2055 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2056 {
2057   int i, j;
2058
2059   for(i=0; i<tape->length; i++)
2060   {
2061     for(j=0; j<MAX_PLAYERS; j++)
2062       if (tape->player_participates[j])
2063         putFile8Bit(file, tape->pos[i].action[j]);
2064
2065     putFile8Bit(file, tape->pos[i].delay);
2066   }
2067 }
2068
2069 void SaveTape(int level_nr)
2070 {
2071   char *filename = getTapeFilename(level_nr);
2072   FILE *file;
2073   boolean new_tape = TRUE;
2074   int num_participating_players = 0;
2075   int info_chunk_size;
2076   int body_chunk_size;
2077   int i;
2078
2079   InitTapeDirectory(leveldir_current->filename);
2080
2081   /* if a tape still exists, ask to overwrite it */
2082   if (access(filename, F_OK) == 0)
2083   {
2084     new_tape = FALSE;
2085     if (!Request("Replace old tape ?", REQ_ASK))
2086       return;
2087   }
2088
2089   if (!(file = fopen(filename, MODE_WRITE)))
2090   {
2091     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2092     return;
2093   }
2094
2095   tape.file_version = FILE_VERSION_ACTUAL;
2096   tape.game_version = GAME_VERSION_ACTUAL;
2097
2098   /* count number of participating players  */
2099   for(i=0; i<MAX_PLAYERS; i++)
2100     if (tape.player_participates[i])
2101       num_participating_players++;
2102
2103   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2104   body_chunk_size = (num_participating_players + 1) * tape.length;
2105
2106   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2107   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2108
2109   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2110   SaveTape_VERS(file, &tape);
2111
2112   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2113   SaveTape_HEAD(file, &tape);
2114
2115   putFileChunkBE(file, "INFO", info_chunk_size);
2116   SaveTape_INFO(file, &tape);
2117
2118   putFileChunkBE(file, "BODY", body_chunk_size);
2119   SaveTape_BODY(file, &tape);
2120
2121   fclose(file);
2122
2123   SetFilePermissions(filename, PERMS_PRIVATE);
2124
2125   tape.changed = FALSE;
2126
2127   if (new_tape)
2128     Request("tape saved !", REQ_CONFIRM);
2129 }
2130
2131 void DumpTape(struct TapeInfo *tape)
2132 {
2133   int i, j;
2134
2135   if (TAPE_IS_EMPTY(*tape))
2136   {
2137     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2138     return;
2139   }
2140
2141   printf_line("-", 79);
2142   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2143          tape->level_nr, tape->file_version, tape->game_version);
2144   printf("Level series identifier: '%s'\n", tape->level_identifier);
2145   printf_line("-", 79);
2146
2147   for(i=0; i<tape->length; i++)
2148   {
2149     if (i >= MAX_TAPELEN)
2150       break;
2151
2152     printf("%03d: ", i);
2153
2154     for(j=0; j<MAX_PLAYERS; j++)
2155     {
2156       if (tape->player_participates[j])
2157       {
2158         int action = tape->pos[i].action[j];
2159
2160         printf("%d:%02x ", j, action);
2161         printf("[%c%c%c%c|%c%c] - ",
2162                (action & JOY_LEFT ? '<' : ' '),
2163                (action & JOY_RIGHT ? '>' : ' '),
2164                (action & JOY_UP ? '^' : ' '),
2165                (action & JOY_DOWN ? 'v' : ' '),
2166                (action & JOY_BUTTON_1 ? '1' : ' '),
2167                (action & JOY_BUTTON_2 ? '2' : ' '));
2168       }
2169     }
2170
2171     printf("(%03d)\n", tape->pos[i].delay);
2172   }
2173
2174   printf_line("-", 79);
2175 }
2176
2177
2178 /* ========================================================================= */
2179 /* score file functions                                                      */
2180 /* ========================================================================= */
2181
2182 void LoadScore(int level_nr)
2183 {
2184   int i;
2185   char *filename = getScoreFilename(level_nr);
2186   char cookie[MAX_LINE_LEN];
2187   char line[MAX_LINE_LEN];
2188   char *line_ptr;
2189   FILE *file;
2190
2191   /* always start with reliable default values */
2192   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2193   {
2194     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2195     highscore[i].Score = 0;
2196   }
2197
2198   if (!(file = fopen(filename, MODE_READ)))
2199     return;
2200
2201   /* check file identifier */
2202   fgets(cookie, MAX_LINE_LEN, file);
2203   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2204     cookie[strlen(cookie) - 1] = '\0';
2205
2206   if (!checkCookieString(cookie, SCORE_COOKIE))
2207   {
2208     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2209     fclose(file);
2210     return;
2211   }
2212
2213   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2214   {
2215     fscanf(file, "%d", &highscore[i].Score);
2216     fgets(line, MAX_LINE_LEN, file);
2217
2218     if (line[strlen(line) - 1] == '\n')
2219       line[strlen(line) - 1] = '\0';
2220
2221     for (line_ptr = line; *line_ptr; line_ptr++)
2222     {
2223       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2224       {
2225         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2226         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2227         break;
2228       }
2229     }
2230   }
2231
2232   fclose(file);
2233 }
2234
2235 void SaveScore(int level_nr)
2236 {
2237   int i;
2238   char *filename = getScoreFilename(level_nr);
2239   FILE *file;
2240
2241   InitScoreDirectory(leveldir_current->filename);
2242
2243   if (!(file = fopen(filename, MODE_WRITE)))
2244   {
2245     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2246     return;
2247   }
2248
2249   fprintf(file, "%s\n\n", SCORE_COOKIE);
2250
2251   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2252     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2253
2254   fclose(file);
2255
2256   SetFilePermissions(filename, PERMS_PUBLIC);
2257 }
2258
2259
2260 /* ========================================================================= */
2261 /* setup file functions                                                      */
2262 /* ========================================================================= */
2263
2264 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2265
2266 /* global setup */
2267 #define SETUP_TOKEN_PLAYER_NAME                 0
2268 #define SETUP_TOKEN_SOUND                       1
2269 #define SETUP_TOKEN_SOUND_LOOPS                 2
2270 #define SETUP_TOKEN_SOUND_MUSIC                 3
2271 #define SETUP_TOKEN_SOUND_SIMPLE                4
2272 #define SETUP_TOKEN_TOONS                       5
2273 #define SETUP_TOKEN_SCROLL_DELAY                6
2274 #define SETUP_TOKEN_SOFT_SCROLLING              7
2275 #define SETUP_TOKEN_FADING                      8
2276 #define SETUP_TOKEN_AUTORECORD                  9
2277 #define SETUP_TOKEN_QUICK_DOORS                 10
2278 #define SETUP_TOKEN_TEAM_MODE                   11
2279 #define SETUP_TOKEN_HANDICAP                    12
2280 #define SETUP_TOKEN_TIME_LIMIT                  13
2281 #define SETUP_TOKEN_FULLSCREEN                  14
2282 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2283 #define SETUP_TOKEN_GRAPHICS_SET                16
2284 #define SETUP_TOKEN_SOUNDS_SET                  17
2285 #define SETUP_TOKEN_MUSIC_SET                   18
2286 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2287 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2288 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2289
2290 #define NUM_GLOBAL_SETUP_TOKENS                 22
2291
2292 /* editor setup */
2293 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2294 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2295 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2296 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2297 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2298 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2299 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2300 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2301 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2302 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2303 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2304 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
2305
2306 #define NUM_EDITOR_SETUP_TOKENS                 12
2307
2308 /* shortcut setup */
2309 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2310 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2311 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2312
2313 #define NUM_SHORTCUT_SETUP_TOKENS               3
2314
2315 /* player setup */
2316 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2317 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2318 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2319 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2320 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2321 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2322 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2323 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2324 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2325 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
2326 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2327 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2328 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2329 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2330 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2331 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
2332
2333 #define NUM_PLAYER_SETUP_TOKENS                 16
2334
2335 /* system setup */
2336 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2337 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2338
2339 #define NUM_SYSTEM_SETUP_TOKENS                 2
2340
2341 /* options setup */
2342 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2343
2344 #define NUM_OPTIONS_SETUP_TOKENS                1
2345
2346
2347 static struct SetupInfo si;
2348 static struct SetupEditorInfo sei;
2349 static struct SetupShortcutInfo ssi;
2350 static struct SetupInputInfo sii;
2351 static struct SetupSystemInfo syi;
2352 static struct OptionInfo soi;
2353
2354 static struct TokenInfo global_setup_tokens[] =
2355 {
2356   { TYPE_STRING, &si.player_name,       "player_name"                   },
2357   { TYPE_SWITCH, &si.sound,             "sound"                         },
2358   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2359   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2360   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2361   { TYPE_SWITCH, &si.toons,             "toons"                         },
2362   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2363   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2364   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2365   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2366   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2367   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2368   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2369   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2370   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2371   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2372   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2373   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2374   { TYPE_STRING, &si.music_set,         "music_set"                     },
2375   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2376   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2377   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2378 };
2379
2380 static struct TokenInfo editor_setup_tokens[] =
2381 {
2382   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2383   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2384   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2385   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2386   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2387   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2388   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2389   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2390   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2391   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2392   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2393   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
2394 };
2395
2396 static struct TokenInfo shortcut_setup_tokens[] =
2397 {
2398   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2399   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2400   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2401 };
2402
2403 static struct TokenInfo player_setup_tokens[] =
2404 {
2405   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2406   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2407   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2408   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2409   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2410   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2411   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2412   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2413   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2414   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2415   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2416   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2417   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2418   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2419   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2420   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
2421 };
2422
2423 static struct TokenInfo system_setup_tokens[] =
2424 {
2425   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2426   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2427 };
2428
2429 static struct TokenInfo options_setup_tokens[] =
2430 {
2431   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2432 };
2433
2434 static char *get_corrected_login_name(char *login_name)
2435 {
2436   /* needed because player name must be a fixed length string */
2437   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2438
2439   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2440   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2441
2442   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2443     if (strchr(login_name_new, ' '))
2444       *strchr(login_name_new, ' ') = '\0';
2445
2446   return login_name_new;
2447 }
2448
2449 static void setSetupInfoToDefaults(struct SetupInfo *si)
2450 {
2451   int i;
2452
2453   si->player_name = get_corrected_login_name(getLoginName());
2454
2455   si->sound = TRUE;
2456   si->sound_loops = TRUE;
2457   si->sound_music = TRUE;
2458   si->sound_simple = TRUE;
2459   si->toons = TRUE;
2460   si->double_buffering = TRUE;
2461   si->direct_draw = !si->double_buffering;
2462   si->scroll_delay = TRUE;
2463   si->soft_scrolling = TRUE;
2464   si->fading = FALSE;
2465   si->autorecord = TRUE;
2466   si->quick_doors = FALSE;
2467   si->team_mode = FALSE;
2468   si->handicap = TRUE;
2469   si->time_limit = TRUE;
2470   si->fullscreen = FALSE;
2471   si->ask_on_escape = TRUE;
2472
2473   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2474   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2475   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2476   si->override_level_graphics = FALSE;
2477   si->override_level_sounds = FALSE;
2478   si->override_level_music = FALSE;
2479
2480   si->editor.el_boulderdash = TRUE;
2481   si->editor.el_emerald_mine = TRUE;
2482   si->editor.el_more = TRUE;
2483   si->editor.el_sokoban = TRUE;
2484   si->editor.el_supaplex = TRUE;
2485   si->editor.el_diamond_caves = TRUE;
2486   si->editor.el_dx_boulderdash = TRUE;
2487   si->editor.el_chars = TRUE;
2488   si->editor.el_custom = TRUE;
2489   si->editor.el_custom_more = FALSE;
2490
2491   si->editor.el_headlines = TRUE;
2492   si->editor.el_user_defined = FALSE;
2493
2494   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2495   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2496   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2497
2498   for (i=0; i<MAX_PLAYERS; i++)
2499   {
2500     si->input[i].use_joystick = FALSE;
2501     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2502     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2503     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2504     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2505     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2506     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2507     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2508     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2509     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2510     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2511     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2512     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2513     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2514     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2515     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2516   }
2517
2518   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2519   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2520
2521   si->options.verbose = FALSE;
2522 }
2523
2524 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2525 {
2526   int i, pnr;
2527
2528   if (!setup_file_hash)
2529     return;
2530
2531   /* global setup */
2532   si = setup;
2533   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2534     setSetupInfo(global_setup_tokens, i,
2535                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2536   setup = si;
2537
2538   /* editor setup */
2539   sei = setup.editor;
2540   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2541     setSetupInfo(editor_setup_tokens, i,
2542                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2543   setup.editor = sei;
2544
2545   /* shortcut setup */
2546   ssi = setup.shortcut;
2547   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2548     setSetupInfo(shortcut_setup_tokens, i,
2549                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2550   setup.shortcut = ssi;
2551
2552   /* player setup */
2553   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2554   {
2555     char prefix[30];
2556
2557     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2558
2559     sii = setup.input[pnr];
2560     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2561     {
2562       char full_token[100];
2563
2564       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2565       setSetupInfo(player_setup_tokens, i,
2566                    getHashEntry(setup_file_hash, full_token));
2567     }
2568     setup.input[pnr] = sii;
2569   }
2570
2571   /* system setup */
2572   syi = setup.system;
2573   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2574     setSetupInfo(system_setup_tokens, i,
2575                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2576   setup.system = syi;
2577
2578   /* options setup */
2579   soi = setup.options;
2580   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2581     setSetupInfo(options_setup_tokens, i,
2582                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2583   setup.options = soi;
2584 }
2585
2586 void LoadSetup()
2587 {
2588   char *filename = getSetupFilename();
2589   SetupFileHash *setup_file_hash = NULL;
2590
2591   /* always start with reliable default values */
2592   setSetupInfoToDefaults(&setup);
2593
2594   setup_file_hash = loadSetupFileHash(filename);
2595
2596   if (setup_file_hash)
2597   {
2598     char *player_name_new;
2599
2600     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2601     decodeSetupFileHash(setup_file_hash);
2602
2603     setup.direct_draw = !setup.double_buffering;
2604
2605     freeSetupFileHash(setup_file_hash);
2606
2607     /* needed to work around problems with fixed length strings */
2608     player_name_new = get_corrected_login_name(setup.player_name);
2609     free(setup.player_name);
2610     setup.player_name = player_name_new;
2611   }
2612   else
2613     Error(ERR_WARN, "using default setup values");
2614 }
2615
2616 void SaveSetup()
2617 {
2618   char *filename = getSetupFilename();
2619   FILE *file;
2620   int i, pnr;
2621
2622   InitUserDataDirectory();
2623
2624   if (!(file = fopen(filename, MODE_WRITE)))
2625   {
2626     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2627     return;
2628   }
2629
2630   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2631                                                getCookie("SETUP")));
2632   fprintf(file, "\n");
2633
2634   /* global setup */
2635   si = setup;
2636   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2637   {
2638     /* just to make things nicer :) */
2639     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2640         i == SETUP_TOKEN_GRAPHICS_SET)
2641       fprintf(file, "\n");
2642
2643     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2644   }
2645
2646   /* editor setup */
2647   sei = setup.editor;
2648   fprintf(file, "\n");
2649   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2650     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2651
2652   /* shortcut setup */
2653   ssi = setup.shortcut;
2654   fprintf(file, "\n");
2655   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2656     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2657
2658   /* player setup */
2659   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2660   {
2661     char prefix[30];
2662
2663     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2664     fprintf(file, "\n");
2665
2666     sii = setup.input[pnr];
2667     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2668       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2669   }
2670
2671   /* system setup */
2672   syi = setup.system;
2673   fprintf(file, "\n");
2674   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2675     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2676
2677   /* options setup */
2678   soi = setup.options;
2679   fprintf(file, "\n");
2680   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2681     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2682
2683   fclose(file);
2684
2685   SetFilePermissions(filename, PERMS_PRIVATE);
2686 }
2687
2688 void LoadCustomElementDescriptions()
2689 {
2690   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2691   SetupFileHash *setup_file_hash;
2692   int i;
2693
2694   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2695   {
2696     if (element_info[i].custom_description != NULL)
2697     {
2698       free(element_info[i].custom_description);
2699       element_info[i].custom_description = NULL;
2700     }
2701   }
2702
2703   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2704     return;
2705
2706   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2707   {
2708     char *token = getStringCat2(element_info[i].token_name, ".name");
2709     char *value = getHashEntry(setup_file_hash, token);
2710
2711     if (value != NULL)
2712       element_info[i].custom_description = getStringCopy(value);
2713
2714     free(token);
2715   }
2716
2717   freeSetupFileHash(setup_file_hash);
2718 }
2719
2720 void LoadSpecialMenuDesignSettings()
2721 {
2722   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2723   SetupFileHash *setup_file_hash;
2724   int i, j;
2725
2726   /* always start with reliable default values from default config */
2727   for (i=0; image_config_vars[i].token != NULL; i++)
2728     for (j=0; image_config[j].token != NULL; j++)
2729       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2730         *image_config_vars[i].value =
2731           get_auto_parameter_value(image_config_vars[i].token,
2732                                    image_config[j].value);
2733
2734   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2735     return;
2736
2737   /* special case: initialize with default values that may be overwritten */
2738   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2739   {
2740     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2741     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2742     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2743
2744     if (value_x != NULL)
2745       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2746     if (value_y != NULL)
2747       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2748     if (list_size != NULL)
2749       menu.list_size[i] = get_integer_from_string(list_size);
2750   }
2751
2752   /* read (and overwrite with) values that may be specified in config file */
2753   for (i=0; image_config_vars[i].token != NULL; i++)
2754   {
2755     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2756
2757     if (value != NULL)
2758       *image_config_vars[i].value =
2759         get_auto_parameter_value(image_config_vars[i].token, value);
2760   }
2761
2762   freeSetupFileHash(setup_file_hash);
2763 }
2764
2765 static char *itoa(unsigned int i)
2766 {
2767   static char *a = NULL;
2768
2769   if (a != NULL)
2770     free(a);
2771
2772   if (i > 2147483647)   /* yes, this is a kludge */
2773     i = 2147483647;
2774
2775   a = checked_malloc(10 + 1);
2776
2777   sprintf(a, "%d", i);
2778
2779   return a;
2780 }
2781
2782 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
2783 {
2784   char *filename = getEditorSetupFilename();
2785   SetupFileList *setup_file_list, *list;
2786   SetupFileHash *element_hash;
2787   int num_unknown_tokens = 0;
2788   int i;
2789
2790   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
2791     return;
2792
2793   element_hash = newSetupFileHash();
2794
2795   for (i=0; i < NUM_FILE_ELEMENTS; i++)
2796     setHashEntry(element_hash, element_info[i].token_name, itoa(i));
2797
2798   /* determined size may be larger than needed (due to unknown elements) */
2799   *num_elements = 0;
2800   for (list = setup_file_list; list != NULL; list = list->next)
2801     (*num_elements)++;
2802
2803   /* add space for up to 3 more elements for padding that may be needed */
2804   *num_elements += 3;
2805
2806   *elements = checked_malloc(*num_elements * sizeof(int));
2807
2808   *num_elements = 0;
2809   for (list = setup_file_list; list != NULL; list = list->next)
2810   {
2811     char *value = getHashEntry(element_hash, list->token);
2812
2813     if (value)
2814     {
2815       (*elements)[(*num_elements)++] = atoi(value);
2816     }
2817     else
2818     {
2819       if (num_unknown_tokens == 0)
2820       {
2821         Error(ERR_RETURN_LINE, "-");
2822         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
2823         Error(ERR_RETURN, "- config file: '%s'", filename);
2824
2825         num_unknown_tokens++;
2826       }
2827
2828       Error(ERR_RETURN, "- token: '%s'", list->token);
2829     }
2830   }
2831
2832   if (num_unknown_tokens > 0)
2833     Error(ERR_RETURN_LINE, "-");
2834
2835   while (*num_elements % 4)     /* pad with empty elements, if needed */
2836     (*elements)[(*num_elements)++] = EL_EMPTY;
2837
2838   freeSetupFileList(setup_file_list);
2839   freeSetupFileHash(element_hash);
2840
2841 #if 0
2842   /* TEST-ONLY */
2843   for (i=0; i < *num_elements; i++)
2844     printf("editor: element '%s' [%d]\n",
2845            element_info[(*elements)[i]].token_name, (*elements)[i]);
2846 #endif
2847 }
2848
2849 static struct MusicFileInfo *get_music_file_info(char *basename)
2850 {
2851   SetupFileHash *setup_file_hash = NULL;
2852   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
2853   char *filename_music = getCustomMusicFilename(basename);
2854   char *filename_prefix, *filename_info;
2855   struct
2856   {
2857     char *token;
2858     char **value_ptr;
2859   }
2860   token_to_value_ptr[] =
2861   {
2862     { "context",        &tmp_music_file_info.context    },
2863     { "title",          &tmp_music_file_info.title      },
2864     { "artist",         &tmp_music_file_info.artist     },
2865     { "album",          &tmp_music_file_info.album      },
2866     { "year",           &tmp_music_file_info.year       },
2867     { NULL,             NULL                            },
2868   };
2869   int i;
2870
2871   if (filename_music == NULL)
2872     return NULL;
2873
2874   /* ---------- try to replace file extension ---------- */
2875
2876   filename_prefix = getStringCopy(filename_music);
2877   if (strrchr(filename_prefix, '.') != NULL)
2878     *strrchr(filename_prefix, '.') = '\0';
2879   filename_info = getStringCat2(filename_prefix, ".txt");
2880
2881 #if 0
2882   printf("trying to load file '%s'...\n", filename_info);
2883 #endif
2884
2885   if (fileExists(filename_info))
2886     setup_file_hash = loadSetupFileHash(filename_info);
2887
2888   free(filename_prefix);
2889   free(filename_info);
2890
2891   if (setup_file_hash == NULL)
2892   {
2893     /* ---------- try to add file extension ---------- */
2894
2895     filename_prefix = getStringCopy(filename_music);
2896     filename_info = getStringCat2(filename_prefix, ".txt");
2897
2898 #if 0
2899     printf("trying to load file '%s'...\n", filename_info);
2900 #endif
2901
2902     if (fileExists(filename_info))
2903       setup_file_hash = loadSetupFileHash(filename_info);
2904
2905     free(filename_prefix);
2906     free(filename_info);
2907   }
2908
2909   if (setup_file_hash == NULL)
2910     return NULL;
2911
2912   /* ---------- music file info found ---------- */
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 = getStringCopy(value);  /* may be NULL */
2919   }
2920
2921   new_music_file_info = checked_calloc(sizeof(struct MusicFileInfo));
2922   *new_music_file_info = tmp_music_file_info;
2923
2924   return new_music_file_info;
2925 }
2926
2927 void LoadMusicInfo()
2928 {
2929   char *music_directory = getCustomMusicDirectory();
2930   int num_music = getMusicListSize();
2931   DIR *dir;
2932   struct dirent *dir_entry;
2933   struct FileInfo *music;
2934   struct MusicFileInfo *next, **new;
2935   int i;
2936
2937   while (music_file_info != NULL)
2938   {
2939     next = music_file_info->next;
2940
2941     if (music_file_info->context)
2942       free(music_file_info->context);
2943     if (music_file_info->title)
2944       free(music_file_info->title);
2945     if (music_file_info->artist)
2946       free(music_file_info->artist);
2947     if (music_file_info->album)
2948       free(music_file_info->album);
2949     if (music_file_info->year)
2950       free(music_file_info->year);
2951
2952     free(music_file_info);
2953
2954     music_file_info = next;
2955   }
2956
2957   new = &music_file_info;
2958
2959   for (i=0; i < num_music; i++)
2960   {
2961     music = getMusicListEntry(i);
2962
2963     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
2964       continue;
2965
2966 #if 0
2967     printf("::: -> '%s'\n", music->filename);
2968 #endif
2969
2970     *new = get_music_file_info(music->filename);
2971     if (*new != NULL)
2972       new = &(*new)->next;
2973   }
2974
2975   if ((dir = opendir(music_directory)) == NULL)
2976   {
2977     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
2978     return;
2979   }
2980
2981   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2982   {
2983     char *basename = dir_entry->d_name;
2984     boolean music_already_used = FALSE;
2985     int i;
2986
2987     for (i=0; i < num_music; i++)
2988     {
2989       music = getMusicListEntry(i);
2990
2991       if (strcmp(basename, music->filename) == 0)
2992       {
2993         music_already_used = TRUE;
2994         break;
2995       }
2996     }
2997
2998     if (music_already_used)
2999       continue;
3000
3001     if (!FileIsSound(basename) && !FileIsMusic(basename))
3002       continue;
3003
3004 #if 0
3005     printf("::: -> '%s'\n", basename);
3006 #endif
3007
3008     *new = get_music_file_info(basename);
3009     if (*new != NULL)
3010       new = &(*new)->next;
3011   }
3012
3013   closedir(dir);
3014
3015 #if 0
3016   /* TEST-ONLY */
3017   for (next = music_file_info; next != NULL; next = next->next)
3018     printf("::: title == '%s'\n", next->title);
3019 #endif
3020 }
3021
3022 void add_demo_anim(int element, int action, int direction, int delay,
3023                    int *num_list_entries)
3024 {
3025   struct DemoAnimInfo *new_list_entry;
3026   (*num_list_entries)++;
3027
3028   demo_anim_info =
3029     checked_realloc(demo_anim_info,
3030                     *num_list_entries * sizeof(struct DemoAnimInfo));
3031   new_list_entry = &demo_anim_info[*num_list_entries - 1];
3032
3033   new_list_entry->element = element;
3034   new_list_entry->action = action;
3035   new_list_entry->direction = direction;
3036   new_list_entry->delay = delay;
3037 }
3038
3039 void print_unknown_token(char *filename, char *token, int token_nr)
3040 {
3041   if (token_nr == 0)
3042   {
3043     Error(ERR_RETURN_LINE, "-");
3044     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3045     Error(ERR_RETURN, "- config file: '%s'", filename);
3046   }
3047
3048   Error(ERR_RETURN, "- token: '%s'", token);
3049 }
3050
3051 void print_unknown_token_end(int token_nr)
3052 {
3053   if (token_nr > 0)
3054     Error(ERR_RETURN_LINE, "-");
3055 }
3056
3057 void LoadDemoAnimInfo()
3058 {
3059   char *filename = getDemoAnimInfoFilename();
3060   SetupFileList *setup_file_list, *list;
3061   SetupFileHash *element_hash, *action_hash, *direction_hash;
3062   int num_list_entries = 0;
3063   int num_unknown_tokens = 0;
3064   int i;
3065
3066   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3067   {
3068     /* use reliable default values from static configuration */
3069     SetupFileList *insert_ptr;
3070
3071     insert_ptr = setup_file_list =
3072       newSetupFileList(demo_anim_info_config[0].token,
3073                        demo_anim_info_config[0].value);
3074
3075     for (i=1; demo_anim_info_config[i].token; i++)
3076       insert_ptr = addListEntry(insert_ptr,
3077                                 demo_anim_info_config[i].token,
3078                                 demo_anim_info_config[i].value);
3079   }
3080
3081   element_hash   = newSetupFileHash();
3082   action_hash    = newSetupFileHash();
3083   direction_hash = newSetupFileHash();
3084
3085   for (i=0; i < MAX_NUM_ELEMENTS; i++)
3086     setHashEntry(element_hash, element_info[i].token_name, itoa(i));
3087
3088   for (i=0; i < NUM_ACTIONS; i++)
3089     setHashEntry(action_hash, element_action_info[i].suffix,
3090                  itoa(element_action_info[i].value));
3091
3092   /* do not store direction index (bit) here, but direction value! */
3093   for (i=0; i < NUM_DIRECTIONS; i++)
3094     setHashEntry(direction_hash, element_direction_info[i].suffix,
3095                  itoa(1 << element_direction_info[i].value));
3096
3097   for (list = setup_file_list; list != NULL; list = list->next)
3098   {
3099     char *element_token, *action_token, *direction_token;
3100     char *element_value, *action_value, *direction_value;
3101     int delay = atoi(list->value);
3102
3103     if (strcmp(list->token, "end") == 0)
3104     {
3105       add_demo_anim(-1, -1, -1, -1, &num_list_entries);
3106
3107       continue;
3108     }
3109
3110     element_token = list->token;
3111     element_value = getHashEntry(element_hash, element_token);
3112
3113     if (element_value != NULL)
3114     {
3115       /* element found */
3116       add_demo_anim(atoi(element_value), -1, -1, delay, &num_list_entries);
3117
3118       continue;
3119     }
3120
3121     if (strchr(element_token, '.') == NULL)
3122     {
3123       /* no further suffixes found -- this is not an element */
3124       print_unknown_token(filename, list->token, num_unknown_tokens++);
3125
3126       continue;
3127     }
3128
3129     action_token = strchr(element_token, '.');
3130     element_token = getStringCopy(element_token);
3131     *strchr(element_token, '.') = '\0';
3132
3133     element_value = getHashEntry(element_hash, element_token);
3134
3135     if (element_value == NULL)
3136     {
3137       /* this is not an element */
3138       print_unknown_token(filename, list->token, num_unknown_tokens++);
3139       free(element_token);
3140
3141       continue;
3142     }
3143
3144     action_value = getHashEntry(action_hash, action_token);
3145
3146     if (action_value != NULL)
3147     {
3148       /* action found */
3149       add_demo_anim(atoi(element_value), atoi(action_value), -1, delay,
3150                     &num_list_entries);
3151       free(element_token);
3152
3153       continue;
3154     }
3155
3156     direction_token = action_token;
3157     direction_value = getHashEntry(direction_hash, direction_token);
3158
3159     if (direction_value != NULL)
3160     {
3161       /* direction found */
3162       add_demo_anim(atoi(element_value), -1, atoi(direction_value), delay,
3163                     &num_list_entries);
3164       free(element_token);
3165
3166       continue;
3167     }
3168
3169     if (strchr(action_token + 1, '.') == NULL)
3170     {
3171       /* no further suffixes found -- this is not an action or direction */
3172       print_unknown_token(filename, list->token, num_unknown_tokens++);
3173       free(element_token);
3174
3175       continue;
3176     }
3177
3178     direction_token = strchr(action_token + 1, '.');
3179     action_token = getStringCopy(action_token);
3180     *strchr(action_token + 1, '.') = '\0';
3181
3182     action_value = getHashEntry(action_hash, action_token);
3183
3184     if (action_value == NULL)
3185     {
3186       /* this is not an action */
3187       print_unknown_token(filename, list->token, num_unknown_tokens++);
3188       free(element_token);
3189       free(action_token);
3190
3191       continue;
3192     }
3193
3194     direction_value = getHashEntry(direction_hash, direction_token);
3195
3196     if (direction_value != NULL)
3197     {
3198       /* direction found */
3199       add_demo_anim(atoi(element_value), atoi(action_value),
3200                     atoi(direction_value), delay, &num_list_entries);
3201       free(element_token);
3202       free(action_token);
3203
3204       continue;
3205     }
3206
3207     print_unknown_token(filename, list->token, num_unknown_tokens++);
3208
3209     free(element_token);
3210     free(action_token);
3211   }
3212
3213   print_unknown_token_end(num_unknown_tokens);
3214
3215   add_demo_anim(-999, -999, -999, -999, &num_list_entries);
3216
3217   freeSetupFileList(setup_file_list);
3218   freeSetupFileHash(element_hash);
3219   freeSetupFileHash(action_hash);
3220   freeSetupFileHash(direction_hash);
3221
3222 #if 0
3223   /* TEST ONLY */
3224   for (i=0; i < num_list_entries; i++)
3225     printf("::: %d, %d, %d => %d\n",
3226            demo_anim_info[i].element,
3227            demo_anim_info[i].action,
3228            demo_anim_info[i].direction,
3229            demo_anim_info[i].delay);
3230 #endif
3231 }
3232
3233 void LoadDemoAnimText()
3234 {
3235   char *filename = getDemoAnimTextFilename();
3236   int i;
3237
3238   if (demo_anim_text != NULL)
3239     freeSetupFileList(demo_anim_text);
3240
3241   if ((demo_anim_text = loadSetupFileList(filename)) == NULL)
3242   {
3243     /* use reliable default values from static configuration */
3244     SetupFileList *insert_ptr;
3245
3246     insert_ptr = demo_anim_text =
3247       newSetupFileList(demo_anim_text_config[0].token,
3248                        demo_anim_text_config[0].value);
3249
3250     for (i=1; demo_anim_text_config[i].token; i++)
3251       insert_ptr = addListEntry(insert_ptr,
3252                                 demo_anim_text_config[i].token,
3253                                 demo_anim_text_config[i].value);
3254   }
3255
3256 #if 0
3257   /* TEST ONLY */
3258   {
3259     SetupFileList *list;
3260
3261     for (list = demo_anim_text; list != NULL; list = list->next)
3262       printf("::: '%s' => '%s'\n", list->token, list->value);
3263   }
3264 #endif
3265 }