rnd-20020323-2-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 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
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "tools.h"
21 #include "tape.h"
22
23
24 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
25 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
26 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
27 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
29 #define LEVEL_HEADER_UNUSED     14      /* unused level header bytes  */
30 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
32 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
33 #define TAPE_HEADER_UNUSED      7       /* unused tape header bytes   */
34
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39
40
41 static void setLevelInfoToDefaults()
42 {
43   int i, x, y;
44
45   level.file_version = FILE_VERSION_ACTUAL;
46   level.game_version = GAME_VERSION_ACTUAL;
47
48   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
49   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
50   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
51
52   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
53   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
54
55   for(x=0; x<MAX_LEV_FIELDX; x++)
56     for(y=0; y<MAX_LEV_FIELDY; y++)
57       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
58
59   level.time = 100;
60   level.gems_needed = 0;
61   level.amoeba_speed = 10;
62   level.time_magic_wall = 10;
63   level.time_wheel = 10;
64   level.time_light = 10;
65   level.time_timegate = 10;
66   level.amoeba_content = EL_DIAMANT;
67   level.double_speed = FALSE;
68   level.gravity = FALSE;
69   level.em_slippery_gems = FALSE;
70
71   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
72     level.name[i] = '\0';
73   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
74     level.author[i] = '\0';
75
76   strcpy(level.name, NAMELESS_LEVEL_NAME);
77   strcpy(level.author, ANONYMOUS_NAME);
78
79   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
80     level.score[i] = 10;
81
82   level.num_yam_contents = STD_ELEMENT_CONTENTS;
83   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
84     for(x=0; x<3; x++)
85       for(y=0; y<3; y++)
86         level.yam_content[i][x][y] =
87           (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
88
89   Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
90   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
91     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
92
93   BorderElement = EL_BETON;
94
95   /* try to determine better author name than 'anonymous' */
96   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
97   {
98     strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
99     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
100   }
101   else
102   {
103     switch (LEVELCLASS(leveldir_current))
104     {
105       case LEVELCLASS_TUTORIAL:
106         strcpy(level.author, PROGRAM_AUTHOR_STRING);
107         break;
108
109       case LEVELCLASS_CONTRIBUTION:
110         strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
111         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
112         break;
113
114       case LEVELCLASS_USER:
115         strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
116         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
117         break;
118
119       default:
120         /* keep default value */
121         break;
122     }
123   }
124 }
125
126 static int checkLevelElement(int element)
127 {
128   if (element >= EL_FIRST_RUNTIME_EL)
129   {
130     Error(ERR_WARN, "invalid level element %d", element);
131     element = EL_CHAR_FRAGE;
132   }
133
134   return element;
135 }
136
137 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
138 {
139   ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
140
141   return chunk_size;
142 }
143
144 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
145 {
146   int i, x, y;
147
148   lev_fieldx = level->fieldx = fgetc(file);
149   lev_fieldy = level->fieldy = fgetc(file);
150
151   level->time           = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
152   level->gems_needed    = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
153
154   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
155     level->name[i] = fgetc(file);
156   level->name[MAX_LEVEL_NAME_LEN] = 0;
157
158   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
159     level->score[i] = fgetc(file);
160
161   level->num_yam_contents = STD_ELEMENT_CONTENTS;
162   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
163     for(y=0; y<3; y++)
164       for(x=0; x<3; x++)
165         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
166
167   level->amoeba_speed           = fgetc(file);
168   level->time_magic_wall        = fgetc(file);
169   level->time_wheel             = fgetc(file);
170   level->amoeba_content         = checkLevelElement(fgetc(file));
171   level->double_speed           = (fgetc(file) == 1 ? TRUE : FALSE);
172   level->gravity                = (fgetc(file) == 1 ? TRUE : FALSE);
173   level->encoding_16bit_field   = (fgetc(file) == 1 ? TRUE : FALSE);
174   level->em_slippery_gems       = (fgetc(file) == 1 ? TRUE : FALSE);
175
176   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
177
178   return chunk_size;
179 }
180
181 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
182 {
183   int i;
184
185   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
186     level->author[i] = fgetc(file);
187   level->author[MAX_LEVEL_NAME_LEN] = 0;
188
189   return chunk_size;
190 }
191
192 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
193 {
194   int i, x, y;
195   int header_size = 4;
196   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
197   int chunk_size_expected = header_size + content_size;
198
199   /* Note: "chunk_size" was wrong before version 2.0 when elements are
200      stored with 16-bit encoding (and should be twice as big then).
201      Even worse, playfield data was stored 16-bit when only yamyam content
202      contained 16-bit elements and vice versa. */
203
204   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
205     chunk_size_expected += content_size;
206
207   if (chunk_size_expected != chunk_size)
208   {
209     ReadUnusedBytesFromFile(file, chunk_size);
210     return chunk_size_expected;
211   }
212
213   fgetc(file);
214   level->num_yam_contents = fgetc(file);
215   fgetc(file);
216   fgetc(file);
217
218   /* correct invalid number of content fields -- should never happen */
219   if (level->num_yam_contents < 1 ||
220       level->num_yam_contents > MAX_ELEMENT_CONTENTS)
221     level->num_yam_contents = STD_ELEMENT_CONTENTS;
222
223   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
224     for(y=0; y<3; y++)
225       for(x=0; x<3; x++)
226         level->yam_content[i][x][y] =
227           checkLevelElement(level->encoding_16bit_field ?
228                             getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
229                             fgetc(file));
230   return chunk_size;
231 }
232
233 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
234 {
235   int x, y;
236   int chunk_size_expected = level->fieldx * level->fieldy;
237
238   /* Note: "chunk_size" was wrong before version 2.0 when elements are
239      stored with 16-bit encoding (and should be twice as big then).
240      Even worse, playfield data was stored 16-bit when only yamyam content
241      contained 16-bit elements and vice versa. */
242
243   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
244     chunk_size_expected *= 2;
245
246   if (chunk_size_expected != chunk_size)
247   {
248     ReadUnusedBytesFromFile(file, chunk_size);
249     return chunk_size_expected;
250   }
251
252   for(y=0; y<level->fieldy; y++)
253     for(x=0; x<level->fieldx; x++)
254       Feld[x][y] = Ur[x][y] =
255         checkLevelElement(level->encoding_16bit_field ?
256                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
257                           fgetc(file));
258   return chunk_size;
259 }
260
261 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
262 {
263   int i, x, y;
264   int element;
265   int num_contents, content_xsize, content_ysize;
266   int content_array[MAX_ELEMENT_CONTENTS][3][3];
267
268   element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
269   num_contents = fgetc(file);
270   content_xsize = fgetc(file);
271   content_ysize = fgetc(file);
272   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
273
274   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
275     for(y=0; y<3; y++)
276       for(x=0; x<3; x++)
277         content_array[i][x][y] =
278           checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
279
280   /* correct invalid number of content fields -- should never happen */
281   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
282     num_contents = STD_ELEMENT_CONTENTS;
283
284   if (element == EL_MAMPFER)
285   {
286     level->num_yam_contents = num_contents;
287
288     for(i=0; i<num_contents; i++)
289       for(y=0; y<3; y++)
290         for(x=0; x<3; x++)
291           level->yam_content[i][x][y] = content_array[i][x][y];
292   }
293   else if (element == EL_AMOEBE_BD)
294   {
295     level->amoeba_content = content_array[0][0][0];
296   }
297   else
298   {
299     Error(ERR_WARN, "cannot load content for element '%d'", element);
300   }
301
302   return chunk_size;
303 }
304
305 void LoadLevel(int level_nr)
306 {
307   char *filename = getLevelFilename(level_nr);
308   char cookie[MAX_LINE_LEN];
309   char chunk_name[CHUNK_ID_LEN + 1];
310   int chunk_size;
311   FILE *file;
312
313   /* always start with reliable default values */
314   setLevelInfoToDefaults();
315
316   if (!(file = fopen(filename, MODE_READ)))
317   {
318     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
319     return;
320   }
321
322   getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
323   if (strcmp(chunk_name, "RND1") == 0)
324   {
325     getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);   /* not used */
326
327     getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
328     if (strcmp(chunk_name, "CAVE") != 0)
329     {
330       Error(ERR_WARN, "unknown format of level file '%s'", filename);
331       fclose(file);
332       return;
333     }
334   }
335   else  /* check for pre-2.0 file format with cookie string */
336   {
337     strcpy(cookie, chunk_name);
338     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
339     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
340       cookie[strlen(cookie) - 1] = '\0';
341
342     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
343     {
344       Error(ERR_WARN, "unknown format of level file '%s'", filename);
345       fclose(file);
346       return;
347     }
348
349     if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
350     {
351       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
352       fclose(file);
353       return;
354     }
355
356     /* pre-2.0 level files have no game version, so use file version here */
357     level.game_version = level.file_version;
358   }
359
360   if (level.file_version < FILE_VERSION_1_2)
361   {
362     /* level files from versions before 1.2.0 without chunk structure */
363     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,           &level);
364     LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
365   }
366   else
367   {
368     static struct
369     {
370       char *name;
371       int size;
372       int (*loader)(FILE *, int, struct LevelInfo *);
373     }
374     chunk_info[] =
375     {
376       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
377       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
378       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
379       { "CONT", -1,                     LoadLevel_CONT },
380       { "BODY", -1,                     LoadLevel_BODY },
381       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
382       {  NULL,  0,                      NULL }
383     };
384
385     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
386     {
387       int i = 0;
388
389       while (chunk_info[i].name != NULL &&
390              strcmp(chunk_name, chunk_info[i].name) != 0)
391         i++;
392
393       if (chunk_info[i].name == NULL)
394       {
395         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
396               chunk_name, filename);
397         ReadUnusedBytesFromFile(file, chunk_size);
398       }
399       else if (chunk_info[i].size != -1 &&
400                chunk_info[i].size != chunk_size)
401       {
402         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
403               chunk_size, chunk_name, filename);
404         ReadUnusedBytesFromFile(file, chunk_size);
405       }
406       else
407       {
408         /* call function to load this level chunk */
409         int chunk_size_expected =
410           (chunk_info[i].loader)(file, chunk_size, &level);
411
412         /* the size of some chunks cannot be checked before reading other
413            chunks first (like "HEAD" and "BODY") that contain some header
414            information, so check them here */
415         if (chunk_size_expected != chunk_size)
416         {
417           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
418                 chunk_size, chunk_name, filename);
419         }
420       }
421     }
422   }
423
424   fclose(file);
425
426   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
427       IS_LEVELCLASS_USER(leveldir_current))
428   {
429     /* For user contributed and private levels, use the version of
430        the game engine the levels were created for.
431        Since 2.0.1, the game engine version is now directly stored
432        in the level file (chunk "VERS"), so there is no need anymore
433        to set the game version from the file version (except for old,
434        pre-2.0 levels, where the game version is still taken from the
435        file format version used to store the level -- see above). */
436
437     /* do some special adjustments to support older level versions */
438     if (level.file_version == FILE_VERSION_1_0)
439     {
440       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
441       Error(ERR_WARN, "using high speed movement for player");
442
443       /* player was faster than monsters in (pre-)1.0 levels */
444       level.double_speed = TRUE;
445     }
446   }
447   else
448   {
449     /* Always use the latest version of the game engine for all but
450        user contributed and private levels; this allows for actual
451        corrections in the game engine to take effect for existing,
452        converted levels (from "classic" or other existing games) to
453        make the game emulation more accurate, while (hopefully) not
454        breaking existing levels created from other players. */
455
456     level.game_version = GAME_VERSION_ACTUAL;
457
458     /* Set special EM style gems behaviour: EM style gems slip down from
459        normal, steel and growing wall. As this is a more fundamental change,
460        it seems better to set the default behaviour to "off" (as it is more
461        natural) and make it configurable in the level editor (as a property
462        of gem style elements). Already existing converted levels (neither
463        private nor contributed levels) are changed to the new behaviour. */
464
465     if (level.file_version < FILE_VERSION_2_0)
466       level.em_slippery_gems = TRUE;
467   }
468
469   /* determine border element for this level */
470   SetBorderElement();
471 }
472
473 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
474 {
475   int i, x, y;
476
477   fputc(level->fieldx, file);
478   fputc(level->fieldy, file);
479
480   putFile16BitInteger(file, level->time,        BYTE_ORDER_BIG_ENDIAN);
481   putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
482
483   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
484     fputc(level->name[i], file);
485
486   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
487     fputc(level->score[i], file);
488
489   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
490     for(y=0; y<3; y++)
491       for(x=0; x<3; x++)
492         fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
493                level->yam_content[i][x][y]),
494               file);
495   fputc(level->amoeba_speed, file);
496   fputc(level->time_magic_wall, file);
497   fputc(level->time_wheel, file);
498   fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
499         file);
500   fputc((level->double_speed ? 1 : 0), file);
501   fputc((level->gravity ? 1 : 0), file);
502   fputc((level->encoding_16bit_field ? 1 : 0), file);
503   fputc((level->em_slippery_gems ? 1 : 0), file);
504
505   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
506 }
507
508 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
509 {
510   int i;
511
512   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
513     fputc(level->author[i], file);
514 }
515
516 #if 0
517 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
518 {
519   int i, x, y;
520
521   fputc(EL_MAMPFER, file);
522   fputc(level->num_yam_contents, file);
523   fputc(0, file);
524   fputc(0, file);
525
526   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
527     for(y=0; y<3; y++)
528       for(x=0; x<3; x++)
529         if (level->encoding_16bit_field)
530           putFile16BitInteger(file, level->yam_content[i][x][y],
531                               BYTE_ORDER_BIG_ENDIAN);
532         else
533           fputc(level->yam_content[i][x][y], file);
534 }
535 #endif
536
537 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
538 {
539   int x, y;
540
541   for(y=0; y<level->fieldy; y++) 
542     for(x=0; x<level->fieldx; x++) 
543       if (level->encoding_16bit_field)
544         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
545       else
546         fputc(Ur[x][y], file);
547 }
548
549 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
550 {
551   int i, x, y;
552   int num_contents, content_xsize, content_ysize;
553   int content_array[MAX_ELEMENT_CONTENTS][3][3];
554
555   if (element == EL_MAMPFER)
556   {
557     num_contents = level->num_yam_contents;
558     content_xsize = 3;
559     content_ysize = 3;
560
561     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
562       for(y=0; y<3; y++)
563         for(x=0; x<3; x++)
564           content_array[i][x][y] = level->yam_content[i][x][y];
565   }
566   else if (element == EL_AMOEBE_BD)
567   {
568     num_contents = 1;
569     content_xsize = 1;
570     content_ysize = 1;
571
572     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
573       for(y=0; y<3; y++)
574         for(x=0; x<3; x++)
575           content_array[i][x][y] = EL_LEERRAUM;
576     content_array[0][0][0] = level->amoeba_content;
577   }
578   else
579   {
580     /* chunk header already written -- write empty chunk data */
581     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
582
583     Error(ERR_WARN, "cannot save content for element '%d'", element);
584     return;
585   }
586
587   putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
588   fputc(num_contents, file);
589   fputc(content_xsize, file);
590   fputc(content_ysize, file);
591
592   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
593
594   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
595     for(y=0; y<3; y++)
596       for(x=0; x<3; x++)
597         putFile16BitInteger(file, content_array[i][x][y],
598                             BYTE_ORDER_BIG_ENDIAN);
599 }
600
601 void SaveLevel(int level_nr)
602 {
603   int i, x, y;
604   char *filename = getLevelFilename(level_nr);
605   int body_chunk_size;
606   FILE *file;
607
608   if (!(file = fopen(filename, MODE_WRITE)))
609   {
610     Error(ERR_WARN, "cannot save level file '%s'", filename);
611     return;
612   }
613
614
615   /* check level field for 16-bit elements */
616   level.encoding_16bit_field = FALSE;
617   for(y=0; y<level.fieldy; y++) 
618     for(x=0; x<level.fieldx; x++) 
619       if (Ur[x][y] > 255)
620         level.encoding_16bit_field = TRUE;
621
622   /* check yamyam content for 16-bit elements */
623   level.encoding_16bit_yamyam = FALSE;
624   for(i=0; i<level.num_yam_contents; i++)
625     for(y=0; y<3; y++)
626       for(x=0; x<3; x++)
627         if (level.yam_content[i][x][y] > 255)
628           level.encoding_16bit_yamyam = TRUE;
629
630   /* check amoeba content for 16-bit elements */
631   level.encoding_16bit_amoeba = FALSE;
632   if (level.amoeba_content > 255)
633     level.encoding_16bit_amoeba = TRUE;
634
635   body_chunk_size =
636     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
637
638   putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
639   putFileChunk(file, "CAVE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
640
641   putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
642   WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
643
644   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
645   SaveLevel_HEAD(file, &level);
646
647   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
648   SaveLevel_AUTH(file, &level);
649
650   putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
651   SaveLevel_BODY(file, &level);
652
653   if (level.encoding_16bit_yamyam ||
654       level.num_yam_contents != STD_ELEMENT_CONTENTS)
655   {
656     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
657     SaveLevel_CNT2(file, &level, EL_MAMPFER);
658   }
659
660   if (level.encoding_16bit_amoeba)
661   {
662     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
663     SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
664   }
665
666   fclose(file);
667
668   SetFilePermissions(filename, PERMS_PRIVATE);
669 }
670
671 static void setTapeInfoToDefaults()
672 {
673   int i;
674
675   /* always start with reliable default values (empty tape) */
676   tape.file_version = FILE_VERSION_ACTUAL;
677   tape.game_version = GAME_VERSION_ACTUAL;
678   TapeErase();
679
680   /* default values (also for pre-1.2 tapes) with only the first player */
681   tape.player_participates[0] = TRUE;
682   for(i=1; i<MAX_PLAYERS; i++)
683     tape.player_participates[i] = FALSE;
684
685   /* at least one (default: the first) player participates in every tape */
686   tape.num_participating_players = 1;
687
688   tape.level_nr = level_nr;
689   tape.counter = 0;
690   tape.changed = FALSE;
691
692   tape.recording = FALSE;
693   tape.playing = FALSE;
694   tape.pausing = FALSE;
695 }
696
697 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
698 {
699   ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
700
701   return chunk_size;
702 }
703
704 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
705 {
706   int i;
707
708   tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
709   tape->date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
710   tape->length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
711
712   /* read header fields that are new since version 1.2 */
713   if (tape->file_version >= FILE_VERSION_1_2)
714   {
715     byte store_participating_players = fgetc(file);
716
717     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
718
719     /* since version 1.2, tapes store which players participate in the tape */
720     tape->num_participating_players = 0;
721     for(i=0; i<MAX_PLAYERS; i++)
722     {
723       tape->player_participates[i] = FALSE;
724
725       if (store_participating_players & (1 << i))
726       {
727         tape->player_participates[i] = TRUE;
728         tape->num_participating_players++;
729       }
730     }
731   }
732
733   return chunk_size;
734 }
735
736 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
737 {
738   int i, j;
739   int chunk_size_expected =
740     (tape->num_participating_players + 1) * tape->length;
741
742   if (chunk_size_expected != chunk_size)
743   {
744     ReadUnusedBytesFromFile(file, chunk_size);
745     return chunk_size_expected;
746   }
747
748   for(i=0; i<tape->length; i++)
749   {
750     if (i >= MAX_TAPELEN)
751       break;
752
753     for(j=0; j<MAX_PLAYERS; j++)
754     {
755       tape->pos[i].action[j] = MV_NO_MOVING;
756
757       if (tape->player_participates[j])
758         tape->pos[i].action[j] = fgetc(file);
759     }
760
761     tape->pos[i].delay = fgetc(file);
762
763     if (tape->file_version == FILE_VERSION_1_0)
764     {
765       /* eliminate possible diagonal moves in old tapes */
766       /* this is only for backward compatibility */
767
768       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
769       byte action = tape->pos[i].action[0];
770       int k, num_moves = 0;
771
772       for (k=0; k<4; k++)
773       {
774         if (action & joy_dir[k])
775         {
776           tape->pos[i + num_moves].action[0] = joy_dir[k];
777           if (num_moves > 0)
778             tape->pos[i + num_moves].delay = 0;
779           num_moves++;
780         }
781       }
782
783       if (num_moves > 1)
784       {
785         num_moves--;
786         i += num_moves;
787         tape->length += num_moves;
788       }
789     }
790     else if (tape->file_version < FILE_VERSION_2_0)
791     {
792       if (tape->pos[i].delay > 1)
793       {
794         /* action part */
795         tape->pos[i + 1] = tape->pos[i];
796         tape->pos[i + 1].delay = 1;
797
798         /* delay part */
799         for(j=0; j<MAX_PLAYERS; j++)
800           tape->pos[i].action[j] = MV_NO_MOVING;
801         tape->pos[i].delay--;
802
803         i++;
804         tape->length++;
805       }
806     }
807
808     if (feof(file))
809       break;
810   }
811
812   if (i != tape->length)
813     chunk_size = (tape->num_participating_players + 1) * i;
814
815   return chunk_size;
816 }
817
818 void LoadTape(int level_nr)
819 {
820   char *filename = getTapeFilename(level_nr);
821   char cookie[MAX_LINE_LEN];
822   char chunk_name[CHUNK_ID_LEN + 1];
823   FILE *file;
824   int chunk_size;
825
826   /* always start with reliable default values */
827   setTapeInfoToDefaults();
828
829   if (!(file = fopen(filename, MODE_READ)))
830     return;
831
832   getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
833   if (strcmp(chunk_name, "RND1") == 0)
834   {
835     getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);   /* not used */
836
837     getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
838     if (strcmp(chunk_name, "TAPE") != 0)
839     {
840       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
841       fclose(file);
842       return;
843     }
844   }
845   else  /* check for pre-2.0 file format with cookie string */
846   {
847     strcpy(cookie, chunk_name);
848     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
849     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
850       cookie[strlen(cookie) - 1] = '\0';
851
852     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
853     {
854       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
855       fclose(file);
856       return;
857     }
858
859     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
860     {
861       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
862       fclose(file);
863       return;
864     }
865
866     /* pre-2.0 tape files have no game version, so use file version here */
867     tape.game_version = tape.file_version;
868   }
869
870   if (tape.file_version < FILE_VERSION_1_2)
871   {
872     /* tape files from versions before 1.2.0 without chunk structure */
873     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
874     LoadTape_BODY(file, 2 * tape.length,  &tape);
875   }
876   else
877   {
878     static struct
879     {
880       char *name;
881       int size;
882       int (*loader)(FILE *, int, struct TapeInfo *);
883     }
884     chunk_info[] =
885     {
886       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
887       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
888       { "BODY", -1,                     LoadTape_BODY },
889       {  NULL,  0,                      NULL }
890     };
891
892     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
893     {
894       int i = 0;
895
896       while (chunk_info[i].name != NULL &&
897              strcmp(chunk_name, chunk_info[i].name) != 0)
898         i++;
899
900       if (chunk_info[i].name == NULL)
901       {
902         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
903               chunk_name, filename);
904         ReadUnusedBytesFromFile(file, chunk_size);
905       }
906       else if (chunk_info[i].size != -1 &&
907                chunk_info[i].size != chunk_size)
908       {
909         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
910               chunk_size, chunk_name, filename);
911         ReadUnusedBytesFromFile(file, chunk_size);
912       }
913       else
914       {
915         /* call function to load this tape chunk */
916         int chunk_size_expected =
917           (chunk_info[i].loader)(file, chunk_size, &tape);
918
919         /* the size of some chunks cannot be checked before reading other
920            chunks first (like "HEAD" and "BODY") that contain some header
921            information, so check them here */
922         if (chunk_size_expected != chunk_size)
923         {
924           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
925                 chunk_size, chunk_name, filename);
926         }
927       }
928     }
929   }
930
931   fclose(file);
932
933   tape.length_seconds = GetTapeLength();
934 }
935
936 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
937 {
938   int i;
939   byte store_participating_players = 0;
940
941   /* set bits for participating players for compact storage */
942   for(i=0; i<MAX_PLAYERS; i++)
943     if (tape->player_participates[i])
944       store_participating_players |= (1 << i);
945
946   putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
947   putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
948   putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
949
950   fputc(store_participating_players, file);
951
952   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
953 }
954
955 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
956 {
957   int i, j;
958
959   for(i=0; i<tape->length; i++)
960   {
961     for(j=0; j<MAX_PLAYERS; j++)
962       if (tape->player_participates[j])
963         fputc(tape->pos[i].action[j], file);
964
965     fputc(tape->pos[i].delay, file);
966   }
967 }
968
969 void SaveTape(int level_nr)
970 {
971   int i;
972   char *filename = getTapeFilename(level_nr);
973   FILE *file;
974   boolean new_tape = TRUE;
975   int num_participating_players = 0;
976   int body_chunk_size;
977
978   InitTapeDirectory(leveldir_current->filename);
979
980   /* if a tape still exists, ask to overwrite it */
981   if (access(filename, F_OK) == 0)
982   {
983     new_tape = FALSE;
984     if (!Request("Replace old tape ?", REQ_ASK))
985       return;
986   }
987
988   if (!(file = fopen(filename, MODE_WRITE)))
989   {
990     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
991     return;
992   }
993
994   /* count number of participating players  */
995   for(i=0; i<MAX_PLAYERS; i++)
996     if (tape.player_participates[i])
997       num_participating_players++;
998
999   body_chunk_size = (num_participating_players + 1) * tape.length;
1000
1001   putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1002   putFileChunk(file, "TAPE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
1003
1004   putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1005   WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1006
1007   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1008   SaveTape_HEAD(file, &tape);
1009
1010   putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1011   SaveTape_BODY(file, &tape);
1012
1013   fclose(file);
1014
1015   SetFilePermissions(filename, PERMS_PRIVATE);
1016
1017   tape.changed = FALSE;
1018
1019   if (new_tape)
1020     Request("tape saved !", REQ_CONFIRM);
1021 }
1022
1023 void DumpTape(struct TapeInfo *tape)
1024 {
1025   int i, j;
1026
1027   if (TAPE_IS_EMPTY(*tape))
1028   {
1029     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1030     return;
1031   }
1032
1033   printf("\n");
1034   printf("-------------------------------------------------------------------------------\n");
1035   printf("Tape of Level %d (file version %06d, game version %06d\n",
1036          tape->level_nr, tape->file_version, tape->game_version);
1037   printf("-------------------------------------------------------------------------------\n");
1038
1039   for(i=0; i<tape->length; i++)
1040   {
1041     if (i >= MAX_TAPELEN)
1042       break;
1043
1044     for(j=0; j<MAX_PLAYERS; j++)
1045     {
1046       if (tape->player_participates[j])
1047       {
1048         int action = tape->pos[i].action[j];
1049
1050         printf("%d:%02x ", j, action);
1051         printf("[%c%c%c%c|%c%c] - ",
1052                (action & JOY_LEFT ? '<' : ' '),
1053                (action & JOY_RIGHT ? '>' : ' '),
1054                (action & JOY_UP ? '^' : ' '),
1055                (action & JOY_DOWN ? 'v' : ' '),
1056                (action & JOY_BUTTON_1 ? '1' : ' '),
1057                (action & JOY_BUTTON_2 ? '2' : ' '));
1058       }
1059     }
1060
1061     printf("(%03d)\n", tape->pos[i].delay);
1062   }
1063
1064   printf("-------------------------------------------------------------------------------\n");
1065 }
1066
1067 void LoadScore(int level_nr)
1068 {
1069   int i;
1070   char *filename = getScoreFilename(level_nr);
1071   char cookie[MAX_LINE_LEN];
1072   char line[MAX_LINE_LEN];
1073   char *line_ptr;
1074   FILE *file;
1075
1076   /* always start with reliable default values */
1077   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1078   {
1079     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1080     highscore[i].Score = 0;
1081   }
1082
1083   if (!(file = fopen(filename, MODE_READ)))
1084     return;
1085
1086   /* check file identifier */
1087   fgets(cookie, MAX_LINE_LEN, file);
1088   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1089     cookie[strlen(cookie) - 1] = '\0';
1090
1091   if (!checkCookieString(cookie, SCORE_COOKIE))
1092   {
1093     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1094     fclose(file);
1095     return;
1096   }
1097
1098   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1099   {
1100     fscanf(file, "%d", &highscore[i].Score);
1101     fgets(line, MAX_LINE_LEN, file);
1102
1103     if (line[strlen(line) - 1] == '\n')
1104       line[strlen(line) - 1] = '\0';
1105
1106     for (line_ptr = line; *line_ptr; line_ptr++)
1107     {
1108       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1109       {
1110         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1111         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1112         break;
1113       }
1114     }
1115   }
1116
1117   fclose(file);
1118 }
1119
1120 void SaveScore(int level_nr)
1121 {
1122   int i;
1123   char *filename = getScoreFilename(level_nr);
1124   FILE *file;
1125
1126   InitScoreDirectory(leveldir_current->filename);
1127
1128   if (!(file = fopen(filename, MODE_WRITE)))
1129   {
1130     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1131     return;
1132   }
1133
1134   fprintf(file, "%s\n\n", SCORE_COOKIE);
1135
1136   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1137     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1138
1139   fclose(file);
1140
1141   SetFilePermissions(filename, PERMS_PUBLIC);
1142 }