8233ac5141d754d86f69495d33444cac798ee880
[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 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
2766 {
2767   char *filename = getEditorSetupFilename();
2768   SetupFileList *setup_file_list, *list;
2769   SetupFileHash *element_hash;
2770   int num_unknown_tokens = 0;
2771   int i;
2772
2773   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
2774     return;
2775
2776   element_hash = newSetupFileHash();
2777
2778   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
2779     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
2780
2781   /* determined size may be larger than needed (due to unknown elements) */
2782   *num_elements = 0;
2783   for (list = setup_file_list; list != NULL; list = list->next)
2784     (*num_elements)++;
2785
2786   /* add space for up to 3 more elements for padding that may be needed */
2787   *num_elements += 3;
2788
2789   *elements = checked_malloc(*num_elements * sizeof(int));
2790
2791   *num_elements = 0;
2792   for (list = setup_file_list; list != NULL; list = list->next)
2793   {
2794     char *value = getHashEntry(element_hash, list->token);
2795
2796     if (value)
2797     {
2798       (*elements)[(*num_elements)++] = atoi(value);
2799     }
2800     else
2801     {
2802       if (num_unknown_tokens == 0)
2803       {
2804         Error(ERR_RETURN_LINE, "-");
2805         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
2806         Error(ERR_RETURN, "- config file: '%s'", filename);
2807
2808         num_unknown_tokens++;
2809       }
2810
2811       Error(ERR_RETURN, "- token: '%s'", list->token);
2812     }
2813   }
2814
2815   if (num_unknown_tokens > 0)
2816     Error(ERR_RETURN_LINE, "-");
2817
2818   while (*num_elements % 4)     /* pad with empty elements, if needed */
2819     (*elements)[(*num_elements)++] = EL_EMPTY;
2820
2821   freeSetupFileList(setup_file_list);
2822   freeSetupFileHash(element_hash);
2823
2824 #if 0
2825   /* TEST-ONLY */
2826   for (i = 0; i < *num_elements; i++)
2827     printf("editor: element '%s' [%d]\n",
2828            element_info[(*elements)[i]].token_name, (*elements)[i]);
2829 #endif
2830 }
2831
2832 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
2833 {
2834   SetupFileHash *setup_file_hash = NULL;
2835   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
2836   char *filename_music = getCustomMusicFilename(basename);
2837   char *filename_prefix, *filename_info;
2838   struct
2839   {
2840     char *token;
2841     char **value_ptr;
2842   }
2843   token_to_value_ptr[] =
2844   {
2845     { "context",        &tmp_music_file_info.context    },
2846     { "title",          &tmp_music_file_info.title      },
2847     { "artist",         &tmp_music_file_info.artist     },
2848     { "album",          &tmp_music_file_info.album      },
2849     { "year",           &tmp_music_file_info.year       },
2850     { NULL,             NULL                            },
2851   };
2852   int i;
2853
2854   if (filename_music == NULL)
2855     return NULL;
2856
2857   /* ---------- try to replace file extension ---------- */
2858
2859   filename_prefix = getStringCopy(filename_music);
2860   if (strrchr(filename_prefix, '.') != NULL)
2861     *strrchr(filename_prefix, '.') = '\0';
2862   filename_info = getStringCat2(filename_prefix, ".txt");
2863
2864 #if 0
2865   printf("trying to load file '%s'...\n", filename_info);
2866 #endif
2867
2868   if (fileExists(filename_info))
2869     setup_file_hash = loadSetupFileHash(filename_info);
2870
2871   free(filename_prefix);
2872   free(filename_info);
2873
2874   if (setup_file_hash == NULL)
2875   {
2876     /* ---------- try to add file extension ---------- */
2877
2878     filename_prefix = getStringCopy(filename_music);
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
2892   if (setup_file_hash == NULL)
2893     return NULL;
2894
2895   /* ---------- music file info found ---------- */
2896
2897   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
2898
2899   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
2900   {
2901     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
2902
2903     *token_to_value_ptr[i].value_ptr =
2904       getStringCopy(value != NULL ? value : UNKNOWN_NAME);
2905   }
2906
2907   tmp_music_file_info.music = music;
2908
2909   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
2910   *new_music_file_info = tmp_music_file_info;
2911
2912   return new_music_file_info;
2913 }
2914
2915 void LoadMusicInfo()
2916 {
2917   char *music_directory = getCustomMusicDirectory();
2918   int num_music = getMusicListSize();
2919   int num_music_noconf = 0;
2920   DIR *dir;
2921   struct dirent *dir_entry;
2922   struct FileInfo *music;
2923   struct MusicFileInfo *next, **new;
2924   int i;
2925
2926   while (music_file_info != NULL)
2927   {
2928     next = music_file_info->next;
2929
2930     if (music_file_info->context)
2931       free(music_file_info->context);
2932     if (music_file_info->title)
2933       free(music_file_info->title);
2934     if (music_file_info->artist)
2935       free(music_file_info->artist);
2936     if (music_file_info->album)
2937       free(music_file_info->album);
2938     if (music_file_info->year)
2939       free(music_file_info->year);
2940
2941     free(music_file_info);
2942
2943     music_file_info = next;
2944   }
2945
2946   new = &music_file_info;
2947
2948   for (i = 0; i < num_music; i++)
2949   {
2950     music = getMusicListEntry(i);
2951
2952     if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
2953       continue;
2954
2955     /* a configured file may be not recognized as music */
2956     if (!FileIsMusic(music->filename))
2957       continue;
2958
2959 #if 0
2960     printf("::: -> '%s' (configured)\n", music->filename);
2961 #endif
2962
2963     *new = get_music_file_info(music->filename, i);
2964     if (*new != NULL)
2965       new = &(*new)->next;
2966   }
2967
2968   if ((dir = opendir(music_directory)) == NULL)
2969   {
2970     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
2971     return;
2972   }
2973
2974   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2975   {
2976     char *basename = dir_entry->d_name;
2977     boolean music_already_used = FALSE;
2978     int i;
2979
2980     for (i = 0; i < num_music; i++)
2981     {
2982       music = getMusicListEntry(i);
2983
2984       if (strcmp(basename, music->filename) == 0)
2985       {
2986         music_already_used = TRUE;
2987         break;
2988       }
2989     }
2990
2991     if (music_already_used)
2992       continue;
2993
2994     if (!FileIsMusic(basename))
2995       continue;
2996
2997 #if 0
2998     printf("::: -> '%s' (found in directory)\n", basename);
2999 #endif
3000
3001     *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3002     if (*new != NULL)
3003       new = &(*new)->next;
3004
3005     num_music_noconf++;
3006   }
3007
3008   closedir(dir);
3009
3010 #if 0
3011   /* TEST-ONLY */
3012   for (next = music_file_info; next != NULL; next = next->next)
3013     printf("::: title == '%s'\n", next->title);
3014 #endif
3015 }
3016
3017 void add_helpanim_entry(int element, int action, int direction, int delay,
3018                         int *num_list_entries)
3019 {
3020   struct HelpAnimInfo *new_list_entry;
3021   (*num_list_entries)++;
3022
3023   helpanim_info =
3024     checked_realloc(helpanim_info,
3025                     *num_list_entries * sizeof(struct HelpAnimInfo));
3026   new_list_entry = &helpanim_info[*num_list_entries - 1];
3027
3028   new_list_entry->element = element;
3029   new_list_entry->action = action;
3030   new_list_entry->direction = direction;
3031   new_list_entry->delay = delay;
3032 }
3033
3034 void print_unknown_token(char *filename, char *token, int token_nr)
3035 {
3036   if (token_nr == 0)
3037   {
3038     Error(ERR_RETURN_LINE, "-");
3039     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3040     Error(ERR_RETURN, "- config file: '%s'", filename);
3041   }
3042
3043   Error(ERR_RETURN, "- token: '%s'", token);
3044 }
3045
3046 void print_unknown_token_end(int token_nr)
3047 {
3048   if (token_nr > 0)
3049     Error(ERR_RETURN_LINE, "-");
3050 }
3051
3052 void LoadHelpAnimInfo()
3053 {
3054   char *filename = getHelpAnimFilename();
3055   SetupFileList *setup_file_list = NULL, *list;
3056   SetupFileHash *element_hash, *action_hash, *direction_hash;
3057   int num_list_entries = 0;
3058   int num_unknown_tokens = 0;
3059   int i;
3060
3061   if (fileExists(filename))
3062     setup_file_list = loadSetupFileList(filename);
3063
3064   if (setup_file_list == NULL)
3065   {
3066     /* use reliable default values from static configuration */
3067     SetupFileList *insert_ptr;
3068
3069     insert_ptr = setup_file_list =
3070       newSetupFileList(helpanim_config[0].token,
3071                        helpanim_config[0].value);
3072
3073     for (i = 1; helpanim_config[i].token; i++)
3074       insert_ptr = addListEntry(insert_ptr,
3075                                 helpanim_config[i].token,
3076                                 helpanim_config[i].value);
3077   }
3078
3079   element_hash   = newSetupFileHash();
3080   action_hash    = newSetupFileHash();
3081   direction_hash = newSetupFileHash();
3082
3083   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3084     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3085
3086   for (i = 0; i < NUM_ACTIONS; i++)
3087     setHashEntry(action_hash, element_action_info[i].suffix,
3088                  i_to_a(element_action_info[i].value));
3089
3090   /* do not store direction index (bit) here, but direction value! */
3091   for (i = 0; i < NUM_DIRECTIONS; i++)
3092     setHashEntry(direction_hash, element_direction_info[i].suffix,
3093                  i_to_a(1 << element_direction_info[i].value));
3094
3095   for (list = setup_file_list; list != NULL; list = list->next)
3096   {
3097     char *element_token, *action_token, *direction_token;
3098     char *element_value, *action_value, *direction_value;
3099     int delay = atoi(list->value);
3100
3101     if (strcmp(list->token, "end") == 0)
3102     {
3103       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3104
3105       continue;
3106     }
3107
3108     /* first try to break element into element/action/direction parts;
3109        if this does not work, also accept combined "element[.act][.dir]"
3110        elements (like "dynamite.active"), which are unique elements */
3111
3112     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
3113     {
3114       element_value = getHashEntry(element_hash, list->token);
3115       if (element_value != NULL)        /* element found */
3116         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3117                            &num_list_entries);
3118       else
3119       {
3120         /* no further suffixes found -- this is not an element */
3121         print_unknown_token(filename, list->token, num_unknown_tokens++);
3122       }
3123
3124       continue;
3125     }
3126
3127     /* token has format "<prefix>.<something>" */
3128
3129     action_token = strchr(list->token, '.');    /* suffix may be action ... */
3130     direction_token = action_token;             /* ... or direction */
3131
3132     element_token = getStringCopy(list->token);
3133     *strchr(element_token, '.') = '\0';
3134
3135     element_value = getHashEntry(element_hash, element_token);
3136
3137     if (element_value == NULL)          /* this is no element */
3138     {
3139       element_value = getHashEntry(element_hash, list->token);
3140       if (element_value != NULL)        /* combined element found */
3141         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3142                            &num_list_entries);
3143       else
3144         print_unknown_token(filename, list->token, num_unknown_tokens++);
3145
3146       free(element_token);
3147
3148       continue;
3149     }
3150
3151     action_value = getHashEntry(action_hash, action_token);
3152
3153     if (action_value != NULL)           /* action found */
3154     {
3155       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3156                     &num_list_entries);
3157
3158       free(element_token);
3159
3160       continue;
3161     }
3162
3163     direction_value = getHashEntry(direction_hash, direction_token);
3164
3165     if (direction_value != NULL)        /* direction found */
3166     {
3167       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3168                          &num_list_entries);
3169
3170       free(element_token);
3171
3172       continue;
3173     }
3174
3175     if (strchr(action_token + 1, '.') == NULL)
3176     {
3177       /* no further suffixes found -- this is not an action nor direction */
3178
3179       element_value = getHashEntry(element_hash, list->token);
3180       if (element_value != NULL)        /* combined element found */
3181         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3182                            &num_list_entries);
3183       else
3184         print_unknown_token(filename, list->token, num_unknown_tokens++);
3185
3186       free(element_token);
3187
3188       continue;
3189     }
3190
3191     /* token has format "<prefix>.<suffix>.<something>" */
3192
3193     direction_token = strchr(action_token + 1, '.');
3194
3195     action_token = getStringCopy(action_token);
3196     *strchr(action_token + 1, '.') = '\0';
3197
3198     action_value = getHashEntry(action_hash, action_token);
3199
3200     if (action_value == NULL)           /* this is no action */
3201     {
3202       element_value = getHashEntry(element_hash, list->token);
3203       if (element_value != NULL)        /* combined element found */
3204         add_helpanim_entry(atoi(element_value), -1, -1, delay,
3205                            &num_list_entries);
3206       else
3207         print_unknown_token(filename, list->token, num_unknown_tokens++);
3208
3209       free(element_token);
3210       free(action_token);
3211
3212       continue;
3213     }
3214
3215     direction_value = getHashEntry(direction_hash, direction_token);
3216
3217     if (direction_value != NULL)        /* direction found */
3218     {
3219       add_helpanim_entry(atoi(element_value), atoi(action_value),
3220                          atoi(direction_value), delay, &num_list_entries);
3221
3222       free(element_token);
3223       free(action_token);
3224
3225       continue;
3226     }
3227
3228     /* this is no direction */
3229
3230     element_value = getHashEntry(element_hash, list->token);
3231     if (element_value != NULL)          /* combined element found */
3232       add_helpanim_entry(atoi(element_value), -1, -1, delay,
3233                          &num_list_entries);
3234     else
3235       print_unknown_token(filename, list->token, num_unknown_tokens++);
3236
3237     free(element_token);
3238     free(action_token);
3239   }
3240
3241   print_unknown_token_end(num_unknown_tokens);
3242
3243   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3244   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
3245
3246   freeSetupFileList(setup_file_list);
3247   freeSetupFileHash(element_hash);
3248   freeSetupFileHash(action_hash);
3249   freeSetupFileHash(direction_hash);
3250
3251 #if 0
3252   /* TEST ONLY */
3253   for (i = 0; i < num_list_entries; i++)
3254     printf("::: %d, %d, %d => %d\n",
3255            helpanim_info[i].element,
3256            helpanim_info[i].action,
3257            helpanim_info[i].direction,
3258            helpanim_info[i].delay);
3259 #endif
3260 }
3261
3262 void LoadHelpTextInfo()
3263 {
3264   char *filename = getHelpTextFilename();
3265   int i;
3266
3267   if (helptext_info != NULL)
3268   {
3269     freeSetupFileHash(helptext_info);
3270     helptext_info = NULL;
3271   }
3272
3273   if (fileExists(filename))
3274     helptext_info = loadSetupFileHash(filename);
3275
3276   if (helptext_info == NULL)
3277   {
3278     /* use reliable default values from static configuration */
3279     helptext_info = newSetupFileHash();
3280
3281     for (i = 0; helptext_config[i].token; i++)
3282       setHashEntry(helptext_info,
3283                    helptext_config[i].token,
3284                    helptext_config[i].value);
3285   }
3286
3287 #if 0
3288   /* TEST ONLY */
3289   BEGIN_HASH_ITERATION(helptext_info, itr)
3290   {
3291     printf("::: '%s' => '%s'\n",
3292            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3293   }
3294   END_HASH_ITERATION(hash, itr)
3295 #endif
3296 }