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