3f50b5e9da42d312637589df1fd4dd84a5f8065c
[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 <dirent.h>
16 #include <sys/stat.h>
17
18 #include "libgame/libgame.h"
19
20 #include "files.h"
21 #include "tools.h"
22 #include "tape.h"
23 #include "joystick.h"
24
25 #define MAX_FILENAME_LEN        256     /* maximal filename length   */
26 #define MAX_LINE_LEN            1000    /* maximal input line length */
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header */
29 #define LEVEL_HEADER_UNUSED     15      /* 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            "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
37 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE             "ROCKSNDIAMONDS_TAPE_FILE_VERSION_2.0"
39 #define SETUP_COOKIE            "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE       "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE        "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define LEVEL_COOKIE_14         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
46 #define TAPE_COOKIE_10          "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47 #define TAPE_COOKIE_12          "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
48
49 /* file names and filename extensions */
50 #if !defined(PLATFORM_MSDOS)
51 #define LEVELSETUP_DIRECTORY    "levelsetup"
52 #define SETUP_FILENAME          "setup.conf"
53 #define LEVELSETUP_FILENAME     "levelsetup.conf"
54 #define LEVELINFO_FILENAME      "levelinfo.conf"
55 #define LEVELFILE_EXTENSION     "level"
56 #define TAPEFILE_EXTENSION      "tape"
57 #define SCOREFILE_EXTENSION     "score"
58 #else
59 #define LEVELSETUP_DIRECTORY    "lvlsetup"
60 #define SETUP_FILENAME          "setup.cnf"
61 #define LEVELSETUP_FILENAME     "lvlsetup.cnf"
62 #define LEVELINFO_FILENAME      "lvlinfo.cnf"
63 #define LEVELFILE_EXTENSION     "lvl"
64 #define TAPEFILE_EXTENSION      "tap"
65 #define SCOREFILE_EXTENSION     "sco"
66 #endif
67
68 #if defined(PLATFORM_WIN32)
69 #ifndef S_IRGRP
70 #define S_IRGRP S_IRUSR
71 #endif
72 #ifndef S_IROTH
73 #define S_IROTH S_IRUSR
74 #endif
75 #ifndef S_IWGRP
76 #define S_IWGRP S_IWUSR
77 #endif
78 #ifndef S_IWOTH
79 #define S_IWOTH S_IWUSR
80 #endif
81 #ifndef S_IXGRP
82 #define S_IXGRP S_IXUSR
83 #endif
84 #ifndef S_IXOTH
85 #define S_IXOTH S_IXUSR
86 #endif
87 #endif  /* PLATFORM_WIN32 */
88
89 /* file permissions for newly written files */
90 #define MODE_R_ALL              (S_IRUSR | S_IRGRP | S_IROTH)
91 #define MODE_W_ALL              (S_IWUSR | S_IWGRP | S_IWOTH)
92 #define MODE_X_ALL              (S_IXUSR | S_IXGRP | S_IXOTH)
93 #define LEVEL_PERMS             (MODE_R_ALL | MODE_W_ALL)
94 #define SCORE_PERMS             LEVEL_PERMS
95 #define TAPE_PERMS              LEVEL_PERMS
96 #define SETUP_PERMS             LEVEL_PERMS
97
98 /* sort priorities of level series (also used as level series classes) */
99 #define LEVELCLASS_TUTORIAL_START       10
100 #define LEVELCLASS_TUTORIAL_END         99
101 #define LEVELCLASS_CLASSICS_START       100
102 #define LEVELCLASS_CLASSICS_END         199
103 #define LEVELCLASS_CONTRIBUTION_START   200
104 #define LEVELCLASS_CONTRIBUTION_END     299
105 #define LEVELCLASS_USER_START           300
106 #define LEVELCLASS_USER_END             399
107 #define LEVELCLASS_BD_START             400
108 #define LEVELCLASS_BD_END               499
109 #define LEVELCLASS_EM_START             500
110 #define LEVELCLASS_EM_END               599
111 #define LEVELCLASS_SP_START             600
112 #define LEVELCLASS_SP_END               699
113 #define LEVELCLASS_DX_START             700
114 #define LEVELCLASS_DX_END               799
115
116 #define LEVELCLASS_TUTORIAL             LEVELCLASS_TUTORIAL_START
117 #define LEVELCLASS_CLASSICS             LEVELCLASS_CLASSICS_START
118 #define LEVELCLASS_CONTRIBUTION         LEVELCLASS_CONTRIBUTION_START
119 #define LEVELCLASS_USER                 LEVELCLASS_USER_START
120 #define LEVELCLASS_BD                   LEVELCLASS_BD_START
121 #define LEVELCLASS_EM                   LEVELCLASS_EM_START
122 #define LEVELCLASS_SP                   LEVELCLASS_SP_START
123 #define LEVELCLASS_DX                   LEVELCLASS_DX_START
124
125 #define LEVELCLASS_UNDEFINED            999
126
127 #define NUM_LEVELCLASS_DESC     8
128 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
129 {
130   "Tutorial Levels",
131   "Classic Originals",
132   "Contributions",
133   "Private Levels",
134   "Boulderdash",
135   "Emerald Mine",
136   "Supaplex",
137   "DX Boulderdash"
138 };
139
140 #define IS_LEVELCLASS_TUTORIAL(p) \
141         ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
142          (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
143 #define IS_LEVELCLASS_CLASSICS(p) \
144         ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
145          (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
146 #define IS_LEVELCLASS_CONTRIBUTION(p) \
147         ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
148          (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
149 #define IS_LEVELCLASS_USER(p) \
150         ((p)->sort_priority >= LEVELCLASS_USER_START && \
151          (p)->sort_priority <= LEVELCLASS_USER_END)
152 #define IS_LEVELCLASS_BD(p) \
153         ((p)->sort_priority >= LEVELCLASS_BD_START && \
154          (p)->sort_priority <= LEVELCLASS_BD_END)
155 #define IS_LEVELCLASS_EM(p) \
156         ((p)->sort_priority >= LEVELCLASS_EM_START && \
157          (p)->sort_priority <= LEVELCLASS_EM_END)
158 #define IS_LEVELCLASS_SP(p) \
159         ((p)->sort_priority >= LEVELCLASS_SP_START && \
160          (p)->sort_priority <= LEVELCLASS_SP_END)
161 #define IS_LEVELCLASS_DX(p) \
162         ((p)->sort_priority >= LEVELCLASS_DX_START && \
163          (p)->sort_priority <= LEVELCLASS_DX_END)
164
165 #define LEVELCLASS(n)   (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
166                          IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
167                          IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
168                          IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
169                          IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
170                          IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
171                          IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
172                          IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
173                          LEVELCLASS_UNDEFINED)
174
175 #define LEVELCOLOR(n)   (IS_LEVELCLASS_TUTORIAL(n) ?            FC_BLUE : \
176                          IS_LEVELCLASS_CLASSICS(n) ?            FC_RED : \
177                          IS_LEVELCLASS_BD(n) ?                  FC_GREEN : \
178                          IS_LEVELCLASS_EM(n) ?                  FC_YELLOW : \
179                          IS_LEVELCLASS_SP(n) ?                  FC_GREEN : \
180                          IS_LEVELCLASS_DX(n) ?                  FC_YELLOW : \
181                          IS_LEVELCLASS_CONTRIBUTION(n) ?        FC_GREEN : \
182                          IS_LEVELCLASS_USER(n) ?                FC_RED : \
183                          FC_BLUE)
184
185 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ?            0 : \
186                          IS_LEVELCLASS_CLASSICS(n) ?            1 : \
187                          IS_LEVELCLASS_BD(n) ?                  2 : \
188                          IS_LEVELCLASS_EM(n) ?                  3 : \
189                          IS_LEVELCLASS_SP(n) ?                  4 : \
190                          IS_LEVELCLASS_DX(n) ?                  5 : \
191                          IS_LEVELCLASS_CONTRIBUTION(n) ?        6 : \
192                          IS_LEVELCLASS_USER(n) ?                7 : \
193                          9)
194
195 static int getFileVersionFromCookieString(const char *cookie)
196 {
197   const char *ptr_cookie1, *ptr_cookie2;
198   const char *pattern1 = "_FILE_VERSION_";
199   const char *pattern2 = "?.?";
200   const int len_cookie = strlen(cookie);
201   const int len_pattern1 = strlen(pattern1);
202   const int len_pattern2 = strlen(pattern2);
203   const int len_pattern = len_pattern1 + len_pattern2;
204   int version_major, version_minor;
205
206   if (len_cookie <= len_pattern)
207     return -1;
208
209   ptr_cookie1 = &cookie[len_cookie - len_pattern];
210   ptr_cookie2 = &cookie[len_cookie - len_pattern2];
211
212   if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
213     return -1;
214
215   if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
216       ptr_cookie2[1] != '.' ||
217       ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
218     return -1;
219
220   version_major = ptr_cookie2[0] - '0';
221   version_minor = ptr_cookie2[2] - '0';
222
223   return (version_major * 10 + version_minor);
224 }
225
226 boolean checkCookieString(const char *cookie, const char *template)
227 {
228   const char *pattern = "_FILE_VERSION_?.?";
229   const int len_cookie = strlen(cookie);
230   const int len_template = strlen(template);
231   const int len_pattern = strlen(pattern);
232
233   if (len_cookie != len_template)
234     return FALSE;
235
236   if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
237     return FALSE;
238
239   return TRUE;
240 }
241
242 char *getLevelClassDescription(struct LevelDirInfo *ldi)
243 {
244   int position = ldi->sort_priority / 100;
245
246   if (position >= 0 && position < NUM_LEVELCLASS_DESC)
247     return levelclass_desc[position];
248   else
249     return "Unknown Level Class";
250 }
251
252 static void SaveUserLevelInfo();                /* for 'InitUserLevelDir()' */
253 static char *getSetupLine(char *, int);         /* for 'SaveUserLevelInfo()' */
254
255 static char *getSetupDir()
256 {
257   return getUserDataDir();
258 }
259
260 static char *getUserLevelDir(char *level_subdir)
261 {
262   static char *userlevel_dir = NULL;
263   char *data_dir = getUserDataDir();
264   char *userlevel_subdir = LEVELS_DIRECTORY;
265
266   if (userlevel_dir)
267     free(userlevel_dir);
268
269   if (strlen(level_subdir) > 0)
270     userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
271   else
272     userlevel_dir = getPath2(data_dir, userlevel_subdir);
273
274   return userlevel_dir;
275 }
276
277 static char *getTapeDir(char *level_subdir)
278 {
279   static char *tape_dir = NULL;
280   char *data_dir = getUserDataDir();
281   char *tape_subdir = TAPES_DIRECTORY;
282
283   if (tape_dir)
284     free(tape_dir);
285
286   if (strlen(level_subdir) > 0)
287     tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
288   else
289     tape_dir = getPath2(data_dir, tape_subdir);
290
291   return tape_dir;
292 }
293
294 static char *getScoreDir(char *level_subdir)
295 {
296   static char *score_dir = NULL;
297   char *data_dir = options.rw_base_directory;
298   char *score_subdir = SCORES_DIRECTORY;
299
300   if (score_dir)
301     free(score_dir);
302
303   if (strlen(level_subdir) > 0)
304     score_dir = getPath3(data_dir, score_subdir, level_subdir);
305   else
306     score_dir = getPath2(data_dir, score_subdir);
307
308   return score_dir;
309 }
310
311 static char *getLevelSetupDir(char *level_subdir)
312 {
313   static char *levelsetup_dir = NULL;
314   char *data_dir = getUserDataDir();
315   char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
316
317   if (levelsetup_dir)
318     free(levelsetup_dir);
319
320   if (strlen(level_subdir) > 0)
321     levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
322   else
323     levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
324
325   return levelsetup_dir;
326 }
327
328 static char *getLevelFilename(int nr)
329 {
330   static char *filename = NULL;
331   char basename[MAX_FILENAME_LEN];
332
333   if (filename != NULL)
334     free(filename);
335
336   sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
337   filename = getPath3((leveldir_current->user_defined ?
338                        getUserLevelDir("") :
339                        options.level_directory),
340                       leveldir_current->fullpath,
341                       basename);
342
343   return filename;
344 }
345
346 static char *getTapeFilename(int nr)
347 {
348   static char *filename = NULL;
349   char basename[MAX_FILENAME_LEN];
350
351   if (filename != NULL)
352     free(filename);
353
354   sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
355   filename = getPath2(getTapeDir(leveldir_current->filename), basename);
356
357   return filename;
358 }
359
360 static char *getScoreFilename(int nr)
361 {
362   static char *filename = NULL;
363   char basename[MAX_FILENAME_LEN];
364
365   if (filename != NULL)
366     free(filename);
367
368   sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
369   filename = getPath2(getScoreDir(leveldir_current->filename), basename);
370
371   return filename;
372 }
373
374 static void InitTapeDirectory(char *level_subdir)
375 {
376   createDirectory(getUserDataDir(), "user data");
377   createDirectory(getTapeDir(""), "main tape");
378   createDirectory(getTapeDir(level_subdir), "level tape");
379 }
380
381 static void InitScoreDirectory(char *level_subdir)
382 {
383   createDirectory(getScoreDir(""), "main score");
384   createDirectory(getScoreDir(level_subdir), "level score");
385 }
386
387 static void InitUserLevelDirectory(char *level_subdir)
388 {
389   if (access(getUserLevelDir(level_subdir), F_OK) != 0)
390   {
391     createDirectory(getUserDataDir(), "user data");
392     createDirectory(getUserLevelDir(""), "main user level");
393     createDirectory(getUserLevelDir(level_subdir), "user level");
394
395     SaveUserLevelInfo();
396   }
397 }
398
399 static void InitLevelSetupDirectory(char *level_subdir)
400 {
401   createDirectory(getUserDataDir(), "user data");
402   createDirectory(getLevelSetupDir(""), "main level setup");
403   createDirectory(getLevelSetupDir(level_subdir), "level setup");
404 }
405
406 static void setLevelInfoToDefaults()
407 {
408   int i, x, y;
409
410   level.file_version = FILE_VERSION_ACTUAL;
411   level.game_version = GAME_VERSION_ACTUAL;
412
413   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
414   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
415   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
416
417   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
418   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
419
420   for(x=0; x<MAX_LEV_FIELDX; x++)
421     for(y=0; y<MAX_LEV_FIELDY; y++)
422       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
423
424   level.time = 100;
425   level.gems_needed = 0;
426   level.amoeba_speed = 10;
427   level.time_magic_wall = 10;
428   level.time_wheel = 10;
429   level.time_light = 10;
430   level.time_timegate = 10;
431   level.amoeba_content = EL_DIAMANT;
432   level.double_speed = FALSE;
433   level.gravity = FALSE;
434
435   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
436     level.name[i] = '\0';
437   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
438     level.author[i] = '\0';
439
440   strcpy(level.name, NAMELESS_LEVEL_NAME);
441   strcpy(level.author, ANONYMOUS_NAME);
442
443   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
444     level.score[i] = 10;
445
446   level.num_yam_contents = STD_ELEMENT_CONTENTS;
447   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
448     for(x=0; x<3; x++)
449       for(y=0; y<3; y++)
450         level.yam_content[i][x][y] =
451           (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
452
453   Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
454   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
455     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
456
457   BorderElement = EL_BETON;
458
459   /* try to determine better author name than 'anonymous' */
460   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
461   {
462     strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
463     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
464   }
465   else
466   {
467     switch (LEVELCLASS(leveldir_current))
468     {
469       case LEVELCLASS_TUTORIAL:
470         strcpy(level.author, PROGRAM_AUTHOR_STRING);
471         break;
472
473       case LEVELCLASS_CONTRIBUTION:
474         strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
475         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
476         break;
477
478       case LEVELCLASS_USER:
479         strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
480         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
481         break;
482
483       default:
484         /* keep default value */
485         break;
486     }
487   }
488 }
489
490 static int checkLevelElement(int element)
491 {
492   if (element >= EL_FIRST_RUNTIME_EL)
493   {
494     Error(ERR_WARN, "invalid level element %d", element);
495     element = EL_CHAR_FRAGE;
496   }
497
498   return element;
499 }
500
501 void OLD_LoadLevel(int level_nr)
502 {
503   int i, x, y;
504   char *filename = getLevelFilename(level_nr);
505   char cookie[MAX_LINE_LEN];
506   char chunk_name[CHUNK_ID_LEN + 1];
507   boolean encoding_16bit = FALSE;       /* default: maximal 256 elements */
508   int file_version = FILE_VERSION_ACTUAL;
509   int chunk_size;
510   FILE *file;
511
512   /* always start with reliable default values */
513   setLevelInfoToDefaults();
514
515   if (!(file = fopen(filename, MODE_READ)))
516   {
517     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
518     return;
519   }
520
521   /* check file identifier */
522   fgets(cookie, MAX_LINE_LEN, file);
523   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
524     cookie[strlen(cookie) - 1] = '\0';
525
526 #if 0
527   if (strcmp(cookie, LEVEL_COOKIE_10) == 0)     /* old 1.0 level format */
528     file_version = FILE_VERSION_1_0;
529   else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
530     file_version = FILE_VERSION_1_2;
531   else if (strcmp(cookie, LEVEL_COOKIE) != 0)   /* unknown level format */
532   {
533     Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
534     fclose(file);
535     return;
536   }
537 #else
538   if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
539   {
540     Error(ERR_WARN, "unknown format of level file '%s'", filename);
541     fclose(file);
542     return;
543   }
544
545   file_version = getFileVersionFromCookieString(cookie);
546 #endif
547
548   level.file_version = file_version;
549
550   /* read chunk "HEAD" */
551   if (file_version >= FILE_VERSION_1_2)
552   {
553     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
554     if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
555     {
556       Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
557       fclose(file);
558       return;
559     }
560   }
561
562   lev_fieldx = level.fieldx = fgetc(file);
563   lev_fieldy = level.fieldy = fgetc(file);
564
565   level.time        = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
566   level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
567
568   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
569     level.name[i] = fgetc(file);
570   level.name[MAX_LEVEL_NAME_LEN] = 0;
571
572   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
573     level.score[i] = fgetc(file);
574
575   level.num_yam_contents = STD_ELEMENT_CONTENTS;
576   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
577   {
578     for(y=0; y<3; y++)
579     {
580       for(x=0; x<3; x++)
581       {
582         if (i < STD_ELEMENT_CONTENTS)
583           level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
584         else
585           level.yam_content[i][x][y] = EL_LEERRAUM;
586       }
587     }
588   }
589
590   level.amoeba_speed    = fgetc(file);
591   level.time_magic_wall = fgetc(file);
592   level.time_wheel      = fgetc(file);
593   level.amoeba_content  = checkLevelElement(fgetc(file));
594   level.double_speed    = (fgetc(file) == 1 ? TRUE : FALSE);
595   level.gravity         = (fgetc(file) == 1 ? TRUE : FALSE);
596
597   encoding_16bit        = (fgetc(file) == 1 ? TRUE : FALSE);
598
599   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* skip unused header bytes */
600     fgetc(file);
601
602   if (file_version >= FILE_VERSION_1_2)
603   {
604     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
605
606     /* look for optional author chunk */
607     if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
608     {
609       for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
610         level.author[i] = fgetc(file);
611       level.author[MAX_LEVEL_NAME_LEN] = 0;
612
613       getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
614     }
615
616     /* look for optional content chunk */
617     if (strcmp(chunk_name, "CONT") == 0 &&
618         chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
619     {
620       fgetc(file);
621       level.num_yam_contents = fgetc(file);
622       fgetc(file);
623       fgetc(file);
624
625       if (level.num_yam_contents < 1 ||
626           level.num_yam_contents > MAX_ELEMENT_CONTENTS)
627       {
628 #if DEBUG
629         printf("WARNING: num_yam_contents == %d (corrected)\n",
630                level.num_yam_contents);
631 #endif
632         level.num_yam_contents = STD_ELEMENT_CONTENTS;
633       }
634
635       for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
636         for(y=0; y<3; y++)
637           for(x=0; x<3; x++)
638             level.yam_content[i][x][y] =
639               checkLevelElement(encoding_16bit ?
640                                 getFile16BitInteger(file,
641                                                     BYTE_ORDER_BIG_ENDIAN) :
642                                 fgetc(file));
643
644       getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
645     }
646
647     /* next check body chunk identifier and chunk size */
648     if (strcmp(chunk_name, "BODY") != 0 ||
649         chunk_size != lev_fieldx * lev_fieldy)
650     {
651       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
652       fclose(file);
653       return;
654     }
655   }
656
657   /* clear all other level fields (needed if resized in level editor later) */
658   for(x=0; x<MAX_LEV_FIELDX; x++)
659     for(y=0; y<MAX_LEV_FIELDY; y++)
660       Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
661
662   /* now read in the valid level fields from level file */
663   for(y=0; y<lev_fieldy; y++)
664     for(x=0; x<lev_fieldx; x++)
665       Feld[x][y] = Ur[x][y] =
666         checkLevelElement(encoding_16bit ?
667                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
668                           fgetc(file));
669
670   fclose(file);
671
672   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
673       IS_LEVELCLASS_USER(leveldir_current))
674   {
675     /* for user contributed and private levels, use the version of
676        the game engine the levels were created for */
677     level.game_version = file_version;
678
679     /* player was faster than monsters in pre-1.0 levels */
680     if (file_version == FILE_VERSION_1_0)
681     {
682       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
683       Error(ERR_WARN, "using high speed movement for player");
684       level.double_speed = TRUE;
685     }
686   }
687   else
688   {
689     /* always use the latest version of the game engine for all but
690        user contributed and private levels */
691     level.game_version = GAME_VERSION_ACTUAL;
692   }
693
694   /* determine border element for this level */
695   SetBorderElement();
696 }
697
698 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
699 {
700   while (bytes--)
701     fgetc(file);
702 }
703
704 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
705 {
706   while (bytes--)
707     fputc(0, file);
708 }
709
710 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
711 {
712   int i, x, y;
713
714   lev_fieldx = level->fieldx = fgetc(file);
715   lev_fieldy = level->fieldy = fgetc(file);
716
717   level->time           = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
718   level->gems_needed    = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
719
720   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
721     level->name[i] = fgetc(file);
722   level->name[MAX_LEVEL_NAME_LEN] = 0;
723
724   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
725     level->score[i] = fgetc(file);
726
727   level->num_yam_contents = STD_ELEMENT_CONTENTS;
728   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
729     for(y=0; y<3; y++)
730       for(x=0; x<3; x++)
731         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
732
733   level->amoeba_speed           = fgetc(file);
734   level->time_magic_wall        = fgetc(file);
735   level->time_wheel             = fgetc(file);
736   level->amoeba_content         = checkLevelElement(fgetc(file));
737   level->double_speed           = (fgetc(file) == 1 ? TRUE : FALSE);
738   level->gravity                = (fgetc(file) == 1 ? TRUE : FALSE);
739
740   level->encoding_16bit_field   = (fgetc(file) == 1 ? TRUE : FALSE);
741
742   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
743
744   return chunk_size;
745 }
746
747 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
748 {
749   int i;
750
751   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
752     level->author[i] = fgetc(file);
753   level->author[MAX_LEVEL_NAME_LEN] = 0;
754
755   return chunk_size;
756 }
757
758 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
759 {
760   int i, x, y;
761   int header_size = 4;
762   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
763   int chunk_size_expected = header_size + content_size;
764
765   /* Note: "chunk_size" was wrong before version 2.0 when elements are
766      stored with 16-bit encoding (and should be twice as big then).
767      Even worse, playfield data was stored 16-bit when only yamyam content
768      contained 16-bit elements and vice versa. */
769
770   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771     chunk_size_expected += content_size;
772
773   if (chunk_size_expected != chunk_size)
774   {
775     ReadUnusedBytesFromFile(file, chunk_size);
776     return chunk_size_expected;
777   }
778
779   fgetc(file);
780   level->num_yam_contents = fgetc(file);
781   fgetc(file);
782   fgetc(file);
783
784   /* correct invalid number of content fields -- should never happen */
785   if (level->num_yam_contents < 1 ||
786       level->num_yam_contents > MAX_ELEMENT_CONTENTS)
787     level->num_yam_contents = STD_ELEMENT_CONTENTS;
788
789   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
790     for(y=0; y<3; y++)
791       for(x=0; x<3; x++)
792         level->yam_content[i][x][y] =
793           checkLevelElement(level->encoding_16bit_field ?
794                             getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
795                             fgetc(file));
796   return chunk_size;
797 }
798
799 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
800 {
801   int x, y;
802   int chunk_size_expected = level->fieldx * level->fieldy;
803
804   /* Note: "chunk_size" was wrong before version 2.0 when elements are
805      stored with 16-bit encoding (and should be twice as big then).
806      Even worse, playfield data was stored 16-bit when only yamyam content
807      contained 16-bit elements and vice versa. */
808
809   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
810     chunk_size_expected *= 2;
811
812   if (chunk_size_expected != chunk_size)
813   {
814     ReadUnusedBytesFromFile(file, chunk_size);
815     return chunk_size_expected;
816   }
817
818   for(y=0; y<level->fieldy; y++)
819     for(x=0; x<level->fieldx; x++)
820       Feld[x][y] = Ur[x][y] =
821         checkLevelElement(level->encoding_16bit_field ?
822                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
823                           fgetc(file));
824   return chunk_size;
825 }
826
827 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
828 {
829   int i, x, y;
830   int element;
831   int num_contents, content_xsize, content_ysize;
832   int content_array[MAX_ELEMENT_CONTENTS][3][3];
833
834   element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
835   num_contents = fgetc(file);
836   content_xsize = fgetc(file);
837   content_ysize = fgetc(file);
838   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
839
840   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
841     for(y=0; y<3; y++)
842       for(x=0; x<3; x++)
843         content_array[i][x][y] =
844           checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
845
846   /* correct invalid number of content fields -- should never happen */
847   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
848     num_contents = STD_ELEMENT_CONTENTS;
849
850   if (element == EL_MAMPFER)
851   {
852     level->num_yam_contents = num_contents;
853
854     for(i=0; i<num_contents; i++)
855       for(y=0; y<3; y++)
856         for(x=0; x<3; x++)
857           level->yam_content[i][x][y] = content_array[i][x][y];
858   }
859   else if (element == EL_AMOEBE_BD)
860   {
861     level->amoeba_content = content_array[0][0][0];
862   }
863   else
864   {
865     Error(ERR_WARN, "cannot load content for element '%d'", element);
866   }
867
868   return chunk_size;
869 }
870
871 void LoadLevel(int level_nr)
872 {
873   char *filename = getLevelFilename(level_nr);
874   char cookie[MAX_LINE_LEN];
875   char chunk_name[CHUNK_ID_LEN + 1];
876   int chunk_size;
877   FILE *file;
878
879   /* always start with reliable default values */
880   setLevelInfoToDefaults();
881
882   if (!(file = fopen(filename, MODE_READ)))
883   {
884     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
885     return;
886   }
887
888   /* check file identifier */
889   fgets(cookie, MAX_LINE_LEN, file);
890   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
891     cookie[strlen(cookie) - 1] = '\0';
892
893   if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
894   {
895     Error(ERR_WARN, "unknown format of level file '%s'", filename);
896     fclose(file);
897     return;
898   }
899
900   if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
901   {
902     Error(ERR_WARN, "unsupported version of level file '%s'", filename);
903     fclose(file);
904     return;
905   }
906
907   if (level.file_version < FILE_VERSION_1_2)
908   {
909     /* level files from versions before 1.2.0 without chunk structure */
910     LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
911     LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
912   }
913   else
914   {
915     static struct
916     {
917       char *name;
918       int size;
919       int (*loader)(struct LevelInfo *, FILE *, int);
920     }
921     chunk_info[] =
922     {
923       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
924       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
925       { "CONT", -1,                     LoadLevel_CONT },
926       { "BODY", -1,                     LoadLevel_BODY },
927       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
928       {  NULL,  0,                      NULL }
929     };
930
931     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
932     {
933       int i = 0;
934
935       while (chunk_info[i].name != NULL &&
936              strcmp(chunk_name, chunk_info[i].name) != 0)
937         i++;
938
939       if (chunk_info[i].name == NULL)
940       {
941         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
942               chunk_name, filename);
943         ReadUnusedBytesFromFile(file, chunk_size);
944       }
945       else if (chunk_info[i].size != -1 &&
946                chunk_info[i].size != chunk_size)
947       {
948         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
949               chunk_size, chunk_name, filename);
950         ReadUnusedBytesFromFile(file, chunk_size);
951       }
952       else
953       {
954         /* call function to load this level chunk */
955         int chunk_size_expected =
956           (chunk_info[i].loader)(&level, file, chunk_size);
957
958         /* the size of some chunks cannot be checked before reading other
959            chunks first (like "HEAD" and "BODY") that contain some header
960            information, so check them here */
961         if (chunk_size_expected != chunk_size)
962         {
963           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
964                 chunk_size, chunk_name, filename);
965         }
966       }
967     }
968   }
969
970   fclose(file);
971
972   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
973       IS_LEVELCLASS_USER(leveldir_current))
974   {
975     /* for user contributed and private levels, use the version of
976        the game engine the levels were created for */
977     level.game_version = level.file_version;
978
979     /* player was faster than monsters in pre-1.0 levels */
980     if (level.file_version == FILE_VERSION_1_0)
981     {
982       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
983       Error(ERR_WARN, "using high speed movement for player");
984       level.double_speed = TRUE;
985     }
986   }
987   else
988   {
989     /* always use the latest version of the game engine for all but
990        user contributed and private levels */
991     level.game_version = GAME_VERSION_ACTUAL;
992   }
993
994   /* determine border element for this level */
995   SetBorderElement();
996 }
997
998 void OLD_SaveLevel(int level_nr)
999 {
1000   int i, x, y;
1001   char *filename = getLevelFilename(level_nr);
1002 #if 0
1003   boolean encoding_16bit_amoeba = FALSE;
1004   boolean encoding_16bit_yamyam = FALSE;
1005 #endif
1006   boolean encoding_16bit = FALSE;       /* default: only 8-bit elements */
1007   char *oldest_possible_cookie;
1008   FILE *file;
1009
1010   if (!(file = fopen(filename, MODE_WRITE)))
1011   {
1012     Error(ERR_WARN, "cannot save level file '%s'", filename);
1013     return;
1014   }
1015
1016   /* check yam content for 16-bit elements */
1017   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1018     for(y=0; y<3; y++)
1019       for(x=0; x<3; x++)
1020         if (level.yam_content[i][x][y] > 255)
1021           encoding_16bit = TRUE;
1022
1023   /* check level field for 16-bit elements */
1024   for(y=0; y<lev_fieldy; y++) 
1025     for(x=0; x<lev_fieldx; x++) 
1026       if (Ur[x][y] > 255)
1027         encoding_16bit = TRUE;
1028
1029   oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1030
1031   fputs(oldest_possible_cookie, file);          /* file identifier */
1032   fputc('\n', file);
1033
1034   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1035
1036   fputc(level.fieldx, file);
1037   fputc(level.fieldy, file);
1038
1039   putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1040   putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1041
1042   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1043     fputc(level.name[i], file);
1044   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1045     fputc(level.score[i], file);
1046   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1047     for(y=0; y<3; y++)
1048       for(x=0; x<3; x++)
1049         fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1050   fputc(level.amoeba_speed, file);
1051   fputc(level.time_magic_wall, file);
1052   fputc(level.time_wheel, file);
1053   fputc(level.amoeba_content, file);
1054   fputc((level.double_speed ? 1 : 0), file);
1055   fputc((level.gravity ? 1 : 0), file);
1056
1057   fputc((encoding_16bit ? 1 : 0), file);
1058
1059   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* set unused header bytes to zero */
1060     fputc(0, file);
1061
1062   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1063
1064   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1065     fputc(level.author[i], file);
1066
1067   putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1068                BYTE_ORDER_BIG_ENDIAN);
1069
1070   fputc(EL_MAMPFER, file);
1071   fputc(level.num_yam_contents, file);
1072   fputc(0, file);
1073   fputc(0, file);
1074
1075   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1076     for(y=0; y<3; y++)
1077       for(x=0; x<3; x++)
1078         if (encoding_16bit)
1079           putFile16BitInteger(file, level.yam_content[i][x][y],
1080                               BYTE_ORDER_BIG_ENDIAN);
1081         else
1082           fputc(level.yam_content[i][x][y], file);
1083
1084   putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1085
1086   for(y=0; y<lev_fieldy; y++) 
1087     for(x=0; x<lev_fieldx; x++) 
1088       if (encoding_16bit)
1089         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1090       else
1091         fputc(Ur[x][y], file);
1092
1093   fclose(file);
1094
1095   chmod(filename, LEVEL_PERMS);
1096 }
1097
1098 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1099 {
1100   int i, x, y;
1101
1102   fputc(level->fieldx, file);
1103   fputc(level->fieldy, file);
1104
1105   putFile16BitInteger(file, level->time,        BYTE_ORDER_BIG_ENDIAN);
1106   putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1107
1108   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1109     fputc(level->name[i], file);
1110
1111   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1112     fputc(level->score[i], file);
1113
1114   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1115     for(y=0; y<3; y++)
1116       for(x=0; x<3; x++)
1117         fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1118                level->yam_content[i][x][y]),
1119               file);
1120   fputc(level->amoeba_speed, file);
1121   fputc(level->time_magic_wall, file);
1122   fputc(level->time_wheel, file);
1123   fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1124         file);
1125   fputc((level->double_speed ? 1 : 0), file);
1126   fputc((level->gravity ? 1 : 0), file);
1127
1128   fputc((level->encoding_16bit_field ? 1 : 0), file);
1129
1130   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1131 }
1132
1133 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1134 {
1135   int i;
1136
1137   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1138     fputc(level->author[i], file);
1139 }
1140
1141 #if 0
1142 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1143 {
1144   int i, x, y;
1145
1146   fputc(EL_MAMPFER, file);
1147   fputc(level->num_yam_contents, file);
1148   fputc(0, file);
1149   fputc(0, file);
1150
1151   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1152     for(y=0; y<3; y++)
1153       for(x=0; x<3; x++)
1154         if (level->encoding_16bit_field)
1155           putFile16BitInteger(file, level->yam_content[i][x][y],
1156                               BYTE_ORDER_BIG_ENDIAN);
1157         else
1158           fputc(level->yam_content[i][x][y], file);
1159 }
1160 #endif
1161
1162 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1163 {
1164   int x, y;
1165
1166   for(y=0; y<lev_fieldy; y++) 
1167     for(x=0; x<lev_fieldx; x++) 
1168       if (level->encoding_16bit_field)
1169         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1170       else
1171         fputc(Ur[x][y], file);
1172 }
1173
1174 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1175 {
1176   int i, x, y;
1177   int num_contents, content_xsize, content_ysize;
1178   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1179
1180   if (element == EL_MAMPFER)
1181   {
1182     num_contents = level->num_yam_contents;
1183     content_xsize = 3;
1184     content_ysize = 3;
1185
1186     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1187       for(y=0; y<3; y++)
1188         for(x=0; x<3; x++)
1189           content_array[i][x][y] = level->yam_content[i][x][y];
1190   }
1191   else if (element == EL_AMOEBE_BD)
1192   {
1193     num_contents = 1;
1194     content_xsize = 1;
1195     content_ysize = 1;
1196
1197     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1198       for(y=0; y<3; y++)
1199         for(x=0; x<3; x++)
1200           content_array[i][x][y] = EL_LEERRAUM;
1201     content_array[0][0][0] = level->amoeba_content;
1202   }
1203   else
1204   {
1205     /* chunk header already written -- write empty chunk data */
1206     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1207
1208     Error(ERR_WARN, "cannot save content for element '%d'", element);
1209     return;
1210   }
1211
1212   putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1213   fputc(num_contents, file);
1214   fputc(content_xsize, file);
1215   fputc(content_ysize, file);
1216
1217   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1218
1219   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1220     for(y=0; y<3; y++)
1221       for(x=0; x<3; x++)
1222         putFile16BitInteger(file, content_array[i][x][y],
1223                             BYTE_ORDER_BIG_ENDIAN);
1224 }
1225
1226 void SaveLevel(int level_nr)
1227 {
1228   int i, x, y;
1229   char *filename = getLevelFilename(level_nr);
1230   int body_chunk_size;
1231   FILE *file;
1232
1233   if (!(file = fopen(filename, MODE_WRITE)))
1234   {
1235     Error(ERR_WARN, "cannot save level file '%s'", filename);
1236     return;
1237   }
1238
1239   /* check level field for 16-bit elements */
1240   for(y=0; y<level.fieldy; y++) 
1241     for(x=0; x<level.fieldx; x++) 
1242       if (Ur[x][y] > 255)
1243         level.encoding_16bit_field = TRUE;
1244
1245   /* check yamyam content for 16-bit elements */
1246   for(i=0; i<level.num_yam_contents; i++)
1247     for(y=0; y<3; y++)
1248       for(x=0; x<3; x++)
1249         if (level.yam_content[i][x][y] > 255)
1250           level.encoding_16bit_yamyam = TRUE;
1251
1252   /* check amoeba content for 16-bit elements */
1253   if (level.amoeba_content > 255)
1254     level.encoding_16bit_amoeba = TRUE;
1255
1256   body_chunk_size =
1257     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1258
1259   fputs(LEVEL_COOKIE, file);            /* file identifier */
1260   fputc('\n', file);
1261
1262   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1263   SaveLevel_HEAD(&level, file);
1264
1265   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1266   SaveLevel_AUTH(&level, file);
1267
1268 #if 0
1269   if (level.encoding_16bit_field)       /* obsolete since new "CNT2" chunk */
1270   {
1271     chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1272
1273     putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1274     SaveLevel_CONT(&level, file);
1275   }
1276 #endif
1277
1278   putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1279   SaveLevel_BODY(&level, file);
1280
1281   if (level.encoding_16bit_yamyam ||
1282       level.num_yam_contents != STD_ELEMENT_CONTENTS)
1283   {
1284     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1285     SaveLevel_CNT2(&level, file, EL_MAMPFER);
1286   }
1287
1288   if (level.encoding_16bit_amoeba)
1289   {
1290     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1291     SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1292   }
1293
1294   fclose(file);
1295
1296   chmod(filename, LEVEL_PERMS);
1297 }
1298
1299 static void setTapeInfoToDefaults()
1300 {
1301   int i;
1302
1303   /* always start with reliable default values (empty tape) */
1304   tape.file_version = FILE_VERSION_ACTUAL;
1305   tape.game_version = GAME_VERSION_ACTUAL;
1306   TapeErase();
1307
1308   /* default values (also for pre-1.2 tapes) with only the first player */
1309   tape.player_participates[0] = TRUE;
1310   for(i=1; i<MAX_PLAYERS; i++)
1311     tape.player_participates[i] = FALSE;
1312
1313   /* at least one (default: the first) player participates in every tape */
1314   tape.num_participating_players = 1;
1315
1316   tape.level_nr = level_nr;
1317   tape.counter = 0;
1318   tape.changed = FALSE;
1319
1320   tape.recording = FALSE;
1321   tape.playing = FALSE;
1322   tape.pausing = FALSE;
1323 }
1324
1325 void OLD_LoadTape(int level_nr)
1326 {
1327   int i, j;
1328   char *filename = getTapeFilename(level_nr);
1329   char cookie[MAX_LINE_LEN];
1330   char chunk_name[CHUNK_ID_LEN + 1];
1331   FILE *file;
1332   int num_participating_players;
1333   int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1334   int chunk_size;
1335
1336   /* always start with reliable default values (empty tape) */
1337   tape.file_version = FILE_VERSION_ACTUAL;
1338   tape.game_version = GAME_VERSION_ACTUAL;
1339   TapeErase();
1340
1341   /* default values (also for pre-1.2 tapes) with only the first player */
1342   tape.player_participates[0] = TRUE;
1343   for(i=1; i<MAX_PLAYERS; i++)
1344     tape.player_participates[i] = FALSE;
1345
1346   /* at least one (default: the first) player participates in every tape */
1347   num_participating_players = 1;
1348
1349   if (!(file = fopen(filename, MODE_READ)))
1350     return;
1351
1352   /* check file identifier */
1353   fgets(cookie, MAX_LINE_LEN, file);
1354   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1355     cookie[strlen(cookie) - 1] = '\0';
1356
1357 #if 0
1358   if (strcmp(cookie, TAPE_COOKIE_10) == 0)      /* old 1.0 tape format */
1359     file_version = FILE_VERSION_1_0;
1360   else if (strcmp(cookie, TAPE_COOKIE) != 0)    /* unknown tape format */
1361   {
1362     Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1363     fclose(file);
1364     return;
1365   }
1366 #else
1367   if (!checkCookieString(cookie, TAPE_COOKIE))  /* unknown file format */
1368   {
1369     Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1370     fclose(file);
1371     return;
1372   }
1373
1374   file_version = getFileVersionFromCookieString(cookie);
1375 #endif
1376
1377   tape.file_version = file_version;
1378   tape.game_version = file_version;
1379
1380   /* read chunk "HEAD" */
1381   if (file_version >= FILE_VERSION_1_2)
1382   {
1383     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1384     if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1385     {
1386       Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1387       fclose(file);
1388       return;
1389     }
1390   }
1391
1392   tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1393   tape.date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1394   tape.length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1395
1396   /* read header fields that are new since version 1.2 */
1397   if (file_version >= FILE_VERSION_1_2)
1398   {
1399     byte store_participating_players = fgetc(file);
1400
1401     for(i=0; i<TAPE_HEADER_UNUSED; i++)         /* skip unused header bytes */
1402       fgetc(file);
1403
1404     /* since version 1.2, tapes store which players participate in the tape */
1405     num_participating_players = 0;
1406     for(i=0; i<MAX_PLAYERS; i++)
1407     {
1408       tape.player_participates[i] = FALSE;
1409
1410       if (store_participating_players & (1 << i))
1411       {
1412         tape.player_participates[i] = TRUE;
1413         num_participating_players++;
1414       }
1415     }
1416   }
1417
1418   tape.level_nr = level_nr;
1419   tape.counter = 0;
1420   tape.changed = FALSE;
1421
1422   tape.recording = FALSE;
1423   tape.playing = FALSE;
1424   tape.pausing = FALSE;
1425
1426   /* read chunk "BODY" */
1427   if (file_version >= FILE_VERSION_1_2)
1428   {
1429     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1430     if (strcmp(chunk_name, "BODY") ||
1431         chunk_size != (num_participating_players + 1) * tape.length)
1432     {
1433       Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1434       fclose(file);
1435       return;
1436     }
1437   }
1438
1439 #if DEBUG
1440   printf("\nTAPE OF LEVEL %d\n", level_nr);
1441 #endif
1442
1443   for(i=0; i<tape.length; i++)
1444   {
1445     if (i >= MAX_TAPELEN)
1446       break;
1447
1448     for(j=0; j<MAX_PLAYERS; j++)
1449     {
1450       tape.pos[i].action[j] = MV_NO_MOVING;
1451
1452       if (tape.player_participates[j])
1453         tape.pos[i].action[j] = fgetc(file);
1454
1455 #if DEBUG
1456       {
1457         int x = tape.pos[i].action[j];
1458
1459         printf("%d:%02x ", j, x);
1460         printf("[%c%c%c%c|%c%c] - ",
1461                (x & JOY_LEFT ? '<' : ' '),
1462                (x & JOY_RIGHT ? '>' : ' '),
1463                (x & JOY_UP ? '^' : ' '),
1464                (x & JOY_DOWN ? 'v' : ' '),
1465                (x & JOY_BUTTON_1 ? '1' : ' '),
1466                (x & JOY_BUTTON_2 ? '2' : ' '));
1467       }
1468 #endif
1469
1470     }
1471
1472     tape.pos[i].delay = fgetc(file);
1473
1474 #if DEBUG
1475     printf("[%03d]\n", tape.pos[i].delay);
1476 #endif
1477
1478     if (file_version == FILE_VERSION_1_0)
1479     {
1480       /* eliminate possible diagonal moves in old tapes */
1481       /* this is only for backward compatibility */
1482
1483       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1484       byte action = tape.pos[i].action[0];
1485       int k, num_moves = 0;
1486
1487       for (k=0; k<4; k++)
1488       {
1489         if (action & joy_dir[k])
1490         {
1491           tape.pos[i + num_moves].action[0] = joy_dir[k];
1492           if (num_moves > 0)
1493             tape.pos[i + num_moves].delay = 0;
1494           num_moves++;
1495         }
1496       }
1497
1498       if (num_moves > 1)
1499       {
1500         num_moves--;
1501         i += num_moves;
1502         tape.length += num_moves;
1503       }
1504     }
1505     else if (file_version < FILE_VERSION_2_0)
1506     {
1507       if (tape.pos[i].delay > 1)
1508       {
1509         /* action part */
1510         tape.pos[i + 1] = tape.pos[i];
1511         tape.pos[i + 1].delay = 1;
1512
1513         /* delay part */
1514         for(j=0; j<MAX_PLAYERS; j++)
1515           tape.pos[i].action[j] = MV_NO_MOVING;
1516         tape.pos[i].delay--;
1517
1518         i++;
1519         tape.length++;
1520       }
1521     }
1522
1523     if (feof(file))
1524       break;
1525   }
1526
1527   fclose(file);
1528
1529   if (i != tape.length)
1530     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1531
1532   tape.length_seconds = GetTapeLength();
1533 }
1534
1535 static int LoadTape_HEAD(struct TapeInfo *tape, FILE *file, int chunk_size)
1536 {
1537   int i;
1538
1539   tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1540   tape->date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1541   tape->length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1542
1543   /* read header fields that are new since version 1.2 */
1544   if (tape->file_version >= FILE_VERSION_1_2)
1545   {
1546     byte store_participating_players = fgetc(file);
1547
1548     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1549
1550     /* since version 1.2, tapes store which players participate in the tape */
1551     tape->num_participating_players = 0;
1552     for(i=0; i<MAX_PLAYERS; i++)
1553     {
1554       tape->player_participates[i] = FALSE;
1555
1556       if (store_participating_players & (1 << i))
1557       {
1558         tape->player_participates[i] = TRUE;
1559         tape->num_participating_players++;
1560       }
1561     }
1562   }
1563
1564   return chunk_size;
1565 }
1566
1567 static int LoadTape_BODY(struct TapeInfo *tape, FILE *file, int chunk_size)
1568 {
1569   int i, j;
1570   int chunk_size_expected =
1571     (tape->num_participating_players + 1) * tape->length;
1572
1573   if (chunk_size_expected != chunk_size)
1574   {
1575     ReadUnusedBytesFromFile(file, chunk_size);
1576     return chunk_size_expected;
1577   }
1578
1579   for(i=0; i<tape->length; i++)
1580   {
1581     if (i >= MAX_TAPELEN)
1582       break;
1583
1584     for(j=0; j<MAX_PLAYERS; j++)
1585     {
1586       tape->pos[i].action[j] = MV_NO_MOVING;
1587
1588       if (tape->player_participates[j])
1589         tape->pos[i].action[j] = fgetc(file);
1590     }
1591
1592     tape->pos[i].delay = fgetc(file);
1593
1594     if (tape->file_version == FILE_VERSION_1_0)
1595     {
1596       /* eliminate possible diagonal moves in old tapes */
1597       /* this is only for backward compatibility */
1598
1599       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1600       byte action = tape->pos[i].action[0];
1601       int k, num_moves = 0;
1602
1603       for (k=0; k<4; k++)
1604       {
1605         if (action & joy_dir[k])
1606         {
1607           tape->pos[i + num_moves].action[0] = joy_dir[k];
1608           if (num_moves > 0)
1609             tape->pos[i + num_moves].delay = 0;
1610           num_moves++;
1611         }
1612       }
1613
1614       if (num_moves > 1)
1615       {
1616         num_moves--;
1617         i += num_moves;
1618         tape->length += num_moves;
1619       }
1620     }
1621     else if (tape->file_version < FILE_VERSION_2_0)
1622     {
1623       if (tape->pos[i].delay > 1)
1624       {
1625         /* action part */
1626         tape->pos[i + 1] = tape->pos[i];
1627         tape->pos[i + 1].delay = 1;
1628
1629         /* delay part */
1630         for(j=0; j<MAX_PLAYERS; j++)
1631           tape->pos[i].action[j] = MV_NO_MOVING;
1632         tape->pos[i].delay--;
1633
1634         i++;
1635         tape->length++;
1636       }
1637     }
1638
1639     if (feof(file))
1640       break;
1641   }
1642
1643   if (i != tape->length)
1644     chunk_size = (tape->num_participating_players + 1) * i;
1645
1646   return chunk_size;
1647 }
1648
1649 void LoadTape(int level_nr)
1650 {
1651   char *filename = getTapeFilename(level_nr);
1652   char cookie[MAX_LINE_LEN];
1653   char chunk_name[CHUNK_ID_LEN + 1];
1654   FILE *file;
1655   int chunk_size;
1656
1657   /* always start with reliable default values */
1658   setTapeInfoToDefaults();
1659
1660   if (!(file = fopen(filename, MODE_READ)))
1661     return;
1662
1663   /* check file identifier */
1664   fgets(cookie, MAX_LINE_LEN, file);
1665   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1666     cookie[strlen(cookie) - 1] = '\0';
1667
1668   if (!checkCookieString(cookie, TAPE_COOKIE))  /* unknown file format */
1669   {
1670     Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1671     fclose(file);
1672     return;
1673   }
1674
1675   if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1676   {
1677     Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1678     fclose(file);
1679     return;
1680   }
1681
1682   tape.game_version = tape.file_version;
1683
1684   if (tape.file_version < FILE_VERSION_1_2)
1685   {
1686     /* tape files from versions before 1.2.0 without chunk structure */
1687     LoadTape_HEAD(&tape, file, TAPE_HEADER_SIZE);
1688     LoadTape_BODY(&tape, file, 2 * tape.length);
1689   }
1690   else
1691   {
1692     static struct
1693     {
1694       char *name;
1695       int size;
1696       int (*loader)(struct TapeInfo *, FILE *, int);
1697     }
1698     chunk_info[] =
1699     {
1700       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1701       { "BODY", -1,                     LoadTape_BODY },
1702       {  NULL,  0,                      NULL }
1703     };
1704
1705     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1706     {
1707       int i = 0;
1708
1709       while (chunk_info[i].name != NULL &&
1710              strcmp(chunk_name, chunk_info[i].name) != 0)
1711         i++;
1712
1713       if (chunk_info[i].name == NULL)
1714       {
1715         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1716               chunk_name, filename);
1717         ReadUnusedBytesFromFile(file, chunk_size);
1718       }
1719       else if (chunk_info[i].size != -1 &&
1720                chunk_info[i].size != chunk_size)
1721       {
1722         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1723               chunk_size, chunk_name, filename);
1724         ReadUnusedBytesFromFile(file, chunk_size);
1725       }
1726       else
1727       {
1728         /* call function to load this tape chunk */
1729         int chunk_size_expected =
1730           (chunk_info[i].loader)(&tape, file, chunk_size);
1731
1732         /* the size of some chunks cannot be checked before reading other
1733            chunks first (like "HEAD" and "BODY") that contain some header
1734            information, so check them here */
1735         if (chunk_size_expected != chunk_size)
1736         {
1737           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1738                 chunk_size, chunk_name, filename);
1739         }
1740       }
1741     }
1742   }
1743
1744   fclose(file);
1745
1746   tape.length_seconds = GetTapeLength();
1747 }
1748
1749 void OLD_SaveTape(int level_nr)
1750 {
1751   int i;
1752   char *filename = getTapeFilename(level_nr);
1753   FILE *file;
1754   boolean new_tape = TRUE;
1755   byte store_participating_players;
1756   int num_participating_players;
1757
1758   InitTapeDirectory(leveldir_current->filename);
1759
1760   /* if a tape still exists, ask to overwrite it */
1761   if (access(filename, F_OK) == 0)
1762   {
1763     new_tape = FALSE;
1764     if (!Request("Replace old tape ?", REQ_ASK))
1765       return;
1766   }
1767
1768   /* count number of players and set corresponding bits for compact storage */
1769   store_participating_players = 0;
1770   num_participating_players = 0;
1771   for(i=0; i<MAX_PLAYERS; i++)
1772   {
1773     if (tape.player_participates[i])
1774     {
1775       num_participating_players++;
1776       store_participating_players |= (1 << i);
1777     }
1778   }
1779
1780   if (!(file = fopen(filename, MODE_WRITE)))
1781   {
1782     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1783     return;
1784   }
1785
1786   fputs(TAPE_COOKIE, file);             /* file identifier */
1787   fputc('\n', file);
1788
1789   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1790
1791   putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1792   putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1793   putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1794
1795   fputc(store_participating_players, file);
1796
1797   for(i=0; i<TAPE_HEADER_UNUSED; i++)   /* set unused header bytes to zero */
1798     fputc(0, file);
1799
1800   putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1801                BYTE_ORDER_BIG_ENDIAN);
1802
1803   for(i=0; i<tape.length; i++)
1804   {
1805     int j;
1806
1807     for(j=0; j<MAX_PLAYERS; j++)
1808       if (tape.player_participates[j])
1809         fputc(tape.pos[i].action[j], file);
1810
1811     fputc(tape.pos[i].delay, file);
1812   }
1813
1814   fclose(file);
1815
1816   chmod(filename, TAPE_PERMS);
1817
1818   tape.changed = FALSE;
1819
1820   if (new_tape)
1821     Request("tape saved !", REQ_CONFIRM);
1822 }
1823
1824 static void SaveTape_HEAD(struct TapeInfo *tape, FILE *file)
1825 {
1826   int i;
1827   byte store_participating_players = 0;
1828
1829   /* set bits for participating players for compact storage */
1830   for(i=0; i<MAX_PLAYERS; i++)
1831     if (tape->player_participates[i])
1832       store_participating_players |= (1 << i);
1833
1834   putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1835   putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1836   putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1837
1838   fputc(store_participating_players, file);
1839
1840   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1841 }
1842
1843 static void SaveTape_BODY(struct TapeInfo *tape, FILE *file)
1844 {
1845   int i, j;
1846
1847   for(i=0; i<tape->length; i++)
1848   {
1849     for(j=0; j<MAX_PLAYERS; j++)
1850       if (tape->player_participates[j])
1851         fputc(tape->pos[i].action[j], file);
1852
1853     fputc(tape->pos[i].delay, file);
1854   }
1855 }
1856
1857 void SaveTape(int level_nr)
1858 {
1859   int i;
1860   char *filename = getTapeFilename(level_nr);
1861   FILE *file;
1862   boolean new_tape = TRUE;
1863   int num_participating_players = 0;
1864   int body_chunk_size;
1865
1866   InitTapeDirectory(leveldir_current->filename);
1867
1868   /* if a tape still exists, ask to overwrite it */
1869   if (access(filename, F_OK) == 0)
1870   {
1871     new_tape = FALSE;
1872     if (!Request("Replace old tape ?", REQ_ASK))
1873       return;
1874   }
1875
1876   if (!(file = fopen(filename, MODE_WRITE)))
1877   {
1878     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1879     return;
1880   }
1881
1882   /* count number of participating players  */
1883   for(i=0; i<MAX_PLAYERS; i++)
1884     if (tape.player_participates[i])
1885       num_participating_players++;
1886
1887   body_chunk_size = (num_participating_players + 1) * tape.length;
1888
1889   fputs(TAPE_COOKIE, file);             /* file identifier */
1890   fputc('\n', file);
1891
1892   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1893   SaveTape_HEAD(&tape, file);
1894
1895   putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1896   SaveTape_BODY(&tape, file);
1897
1898   fclose(file);
1899
1900   chmod(filename, TAPE_PERMS);
1901
1902   tape.changed = FALSE;
1903
1904   if (new_tape)
1905     Request("tape saved !", REQ_CONFIRM);
1906 }
1907
1908 void DumpTape(struct TapeInfo *tape)
1909 {
1910   int i, j;
1911
1912   if (TAPE_IS_EMPTY(*tape))
1913   {
1914     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1915     return;
1916   }
1917
1918   printf("\n");
1919   printf("-------------------------------------------------------------------------------\n");
1920   printf("TAPE OF LEVEL %d\n", tape->level_nr);
1921   printf("-------------------------------------------------------------------------------\n");
1922
1923   for(i=0; i<tape->length; i++)
1924   {
1925     if (i >= MAX_TAPELEN)
1926       break;
1927
1928     for(j=0; j<MAX_PLAYERS; j++)
1929     {
1930       if (tape->player_participates[j])
1931       {
1932         int action = tape->pos[i].action[j];
1933
1934         printf("%d:%02x ", j, action);
1935         printf("[%c%c%c%c|%c%c] - ",
1936                (action & JOY_LEFT ? '<' : ' '),
1937                (action & JOY_RIGHT ? '>' : ' '),
1938                (action & JOY_UP ? '^' : ' '),
1939                (action & JOY_DOWN ? 'v' : ' '),
1940                (action & JOY_BUTTON_1 ? '1' : ' '),
1941                (action & JOY_BUTTON_2 ? '2' : ' '));
1942       }
1943     }
1944
1945     printf("(%03d)\n", tape->pos[i].delay);
1946   }
1947
1948   printf("-------------------------------------------------------------------------------\n");
1949 }
1950
1951 void LoadScore(int level_nr)
1952 {
1953   int i;
1954   char *filename = getScoreFilename(level_nr);
1955   char cookie[MAX_LINE_LEN];
1956   char line[MAX_LINE_LEN];
1957   char *line_ptr;
1958   FILE *file;
1959
1960   /* always start with reliable default values */
1961   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1962   {
1963     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1964     highscore[i].Score = 0;
1965   }
1966
1967   if (!(file = fopen(filename, MODE_READ)))
1968     return;
1969
1970   /* check file identifier */
1971   fgets(cookie, MAX_LINE_LEN, file);
1972   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1973     cookie[strlen(cookie) - 1] = '\0';
1974
1975 #if 0
1976   if (strcmp(cookie, SCORE_COOKIE) != 0)
1977   {
1978     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1979     fclose(file);
1980     return;
1981   }
1982 #else
1983   if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1984   {
1985     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1986     fclose(file);
1987     return;
1988   }
1989 #endif
1990
1991   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1992   {
1993     fscanf(file, "%d", &highscore[i].Score);
1994     fgets(line, MAX_LINE_LEN, file);
1995
1996     if (line[strlen(line) - 1] == '\n')
1997       line[strlen(line) - 1] = '\0';
1998
1999     for (line_ptr = line; *line_ptr; line_ptr++)
2000     {
2001       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2002       {
2003         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2004         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2005         break;
2006       }
2007     }
2008   }
2009
2010   fclose(file);
2011 }
2012
2013 void SaveScore(int level_nr)
2014 {
2015   int i;
2016   char *filename = getScoreFilename(level_nr);
2017   FILE *file;
2018
2019   InitScoreDirectory(leveldir_current->filename);
2020
2021   if (!(file = fopen(filename, MODE_WRITE)))
2022   {
2023     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2024     return;
2025   }
2026
2027   fprintf(file, "%s\n\n", SCORE_COOKIE);
2028
2029   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2030     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2031
2032   fclose(file);
2033
2034   chmod(filename, SCORE_PERMS);
2035 }
2036
2037 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
2038 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
2039 #define TOKEN_STR_LAST_PLAYED_LEVEL     "last_played_level"
2040 #define TOKEN_STR_HANDICAP_LEVEL        "handicap_level"
2041 #define TOKEN_STR_PLAYER_PREFIX         "player_"
2042
2043 #define TOKEN_VALUE_POSITION            30
2044
2045 /* global setup */
2046 #define SETUP_TOKEN_PLAYER_NAME         0
2047 #define SETUP_TOKEN_SOUND               1
2048 #define SETUP_TOKEN_SOUND_LOOPS         2
2049 #define SETUP_TOKEN_SOUND_MUSIC         3
2050 #define SETUP_TOKEN_SOUND_SIMPLE        4
2051
2052 #if 0
2053 #define SETUP_TOKEN_TOONS               5
2054 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
2055 #endif
2056
2057 #define SETUP_TOKEN_SCROLL_DELAY        5
2058 #define SETUP_TOKEN_SOFT_SCROLLING      6
2059 #define SETUP_TOKEN_FADING              7
2060 #define SETUP_TOKEN_AUTORECORD          8
2061 #define SETUP_TOKEN_QUICK_DOORS         9
2062 #define SETUP_TOKEN_TEAM_MODE           10
2063 #define SETUP_TOKEN_HANDICAP            11
2064 #define SETUP_TOKEN_TIME_LIMIT          12
2065 #define SETUP_TOKEN_FULLSCREEN          13
2066
2067 /* player setup */
2068 #define SETUP_TOKEN_USE_JOYSTICK        14
2069 #define SETUP_TOKEN_JOY_DEVICE_NAME     15
2070 #define SETUP_TOKEN_JOY_XLEFT           16
2071 #define SETUP_TOKEN_JOY_XMIDDLE         17
2072 #define SETUP_TOKEN_JOY_XRIGHT          18
2073 #define SETUP_TOKEN_JOY_YUPPER          19
2074 #define SETUP_TOKEN_JOY_YMIDDLE         20
2075 #define SETUP_TOKEN_JOY_YLOWER          21
2076 #define SETUP_TOKEN_JOY_SNAP            22
2077 #define SETUP_TOKEN_JOY_BOMB            23
2078 #define SETUP_TOKEN_KEY_LEFT            24
2079 #define SETUP_TOKEN_KEY_RIGHT           25
2080 #define SETUP_TOKEN_KEY_UP              26
2081 #define SETUP_TOKEN_KEY_DOWN            27
2082 #define SETUP_TOKEN_KEY_SNAP            28
2083 #define SETUP_TOKEN_KEY_BOMB            29
2084
2085 /* level directory info */
2086 #define LEVELINFO_TOKEN_NAME            30
2087 #define LEVELINFO_TOKEN_NAME_SHORT      31
2088 #define LEVELINFO_TOKEN_NAME_SORTING    32
2089 #define LEVELINFO_TOKEN_AUTHOR          33
2090 #define LEVELINFO_TOKEN_IMPORTED_FROM   34
2091 #define LEVELINFO_TOKEN_LEVELS          35
2092 #define LEVELINFO_TOKEN_FIRST_LEVEL     36
2093 #define LEVELINFO_TOKEN_SORT_PRIORITY   37
2094 #define LEVELINFO_TOKEN_LEVEL_GROUP     38
2095 #define LEVELINFO_TOKEN_READONLY        39
2096
2097 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
2098 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_FULLSCREEN
2099
2100 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
2101 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
2102
2103 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
2104 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
2105
2106 #define TYPE_BOOLEAN                    1
2107 #define TYPE_SWITCH                     2
2108 #define TYPE_KEY                        3
2109 #define TYPE_INTEGER                    4
2110 #define TYPE_STRING                     5
2111
2112 static struct SetupInfo si;
2113 static struct SetupInputInfo sii;
2114 static struct LevelDirInfo ldi;
2115 static struct
2116 {
2117   int type;
2118   void *value;
2119   char *text;
2120 } token_info[] =
2121 {
2122   /* global setup */
2123   { TYPE_STRING,  &si.player_name,      "player_name"                   },
2124   { TYPE_SWITCH,  &si.sound,            "sound"                         },
2125   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
2126   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
2127   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
2128
2129 #if 0
2130   { TYPE_SWITCH,  &si.toons,            "toons"                         },
2131   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
2132 #endif
2133
2134   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
2135   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
2136   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
2137   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
2138   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
2139   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
2140   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
2141   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
2142   { TYPE_SWITCH,  &si.fullscreen,       "fullscreen"                    },
2143
2144   /* player setup */
2145   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2146   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2147   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2148   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2149   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2150   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2151   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2152   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2153   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2154   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2155   { TYPE_KEY,     &sii.key.left,        ".key.move_left"                },
2156   { TYPE_KEY,     &sii.key.right,       ".key.move_right"               },
2157   { TYPE_KEY,     &sii.key.up,          ".key.move_up"                  },
2158   { TYPE_KEY,     &sii.key.down,        ".key.move_down"                },
2159   { TYPE_KEY,     &sii.key.snap,        ".key.snap_field"               },
2160   { TYPE_KEY,     &sii.key.bomb,        ".key.place_bomb"               },
2161
2162   /* level directory info */
2163   { TYPE_STRING,  &ldi.name,            "name"                          },
2164   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
2165   { TYPE_STRING,  &ldi.name_sorting,    "name_sorting"                  },
2166   { TYPE_STRING,  &ldi.author,          "author"                        },
2167   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
2168   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
2169   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
2170   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
2171   { TYPE_BOOLEAN, &ldi.level_group,     "level_group"                   },
2172   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
2173 };
2174
2175 static char *string_tolower(char *s)
2176 {
2177   static char s_lower[100];
2178   int i;
2179
2180   if (strlen(s) >= 100)
2181     return s;
2182
2183   strcpy(s_lower, s);
2184
2185   for (i=0; i<strlen(s_lower); i++)
2186     s_lower[i] = tolower(s_lower[i]);
2187
2188   return s_lower;
2189 }
2190
2191 static int get_string_integer_value(char *s)
2192 {
2193   static char *number_text[][3] =
2194   {
2195     { "0", "zero", "null", },
2196     { "1", "one", "first" },
2197     { "2", "two", "second" },
2198     { "3", "three", "third" },
2199     { "4", "four", "fourth" },
2200     { "5", "five", "fifth" },
2201     { "6", "six", "sixth" },
2202     { "7", "seven", "seventh" },
2203     { "8", "eight", "eighth" },
2204     { "9", "nine", "ninth" },
2205     { "10", "ten", "tenth" },
2206     { "11", "eleven", "eleventh" },
2207     { "12", "twelve", "twelfth" },
2208   };
2209
2210   int i, j;
2211
2212   for (i=0; i<13; i++)
2213     for (j=0; j<3; j++)
2214       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2215         return i;
2216
2217   return atoi(s);
2218 }
2219
2220 static boolean get_string_boolean_value(char *s)
2221 {
2222   if (strcmp(string_tolower(s), "true") == 0 ||
2223       strcmp(string_tolower(s), "yes") == 0 ||
2224       strcmp(string_tolower(s), "on") == 0 ||
2225       get_string_integer_value(s) == 1)
2226     return TRUE;
2227   else
2228     return FALSE;
2229 }
2230
2231 static char *getFormattedSetupEntry(char *token, char *value)
2232 {
2233   int i;
2234   static char entry[MAX_LINE_LEN];
2235
2236   sprintf(entry, "%s:", token);
2237   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2238     entry[i] = ' ';
2239   entry[i] = '\0';
2240
2241   strcat(entry, value);
2242
2243   return entry;
2244 }
2245
2246 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2247 {
2248   if (!setup_file_list)
2249     return;
2250
2251   if (setup_file_list->token)
2252     free(setup_file_list->token);
2253   if (setup_file_list->value)
2254     free(setup_file_list->value);
2255   if (setup_file_list->next)
2256     freeSetupFileList(setup_file_list->next);
2257   free(setup_file_list);
2258 }
2259
2260 static struct SetupFileList *newSetupFileList(char *token, char *value)
2261 {
2262   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2263
2264   new->token = checked_malloc(strlen(token) + 1);
2265   strcpy(new->token, token);
2266
2267   new->value = checked_malloc(strlen(value) + 1);
2268   strcpy(new->value, value);
2269
2270   new->next = NULL;
2271
2272   return new;
2273 }
2274
2275 static char *getTokenValue(struct SetupFileList *setup_file_list,
2276                            char *token)
2277 {
2278   if (!setup_file_list)
2279     return NULL;
2280
2281   if (strcmp(setup_file_list->token, token) == 0)
2282     return setup_file_list->value;
2283   else
2284     return getTokenValue(setup_file_list->next, token);
2285 }
2286
2287 static void setTokenValue(struct SetupFileList *setup_file_list,
2288                           char *token, char *value)
2289 {
2290   if (!setup_file_list)
2291     return;
2292
2293   if (strcmp(setup_file_list->token, token) == 0)
2294   {
2295     free(setup_file_list->value);
2296     setup_file_list->value = checked_malloc(strlen(value) + 1);
2297     strcpy(setup_file_list->value, value);
2298   }
2299   else if (setup_file_list->next == NULL)
2300     setup_file_list->next = newSetupFileList(token, value);
2301   else
2302     setTokenValue(setup_file_list->next, token, value);
2303 }
2304
2305 #ifdef DEBUG
2306 static void printSetupFileList(struct SetupFileList *setup_file_list)
2307 {
2308   if (!setup_file_list)
2309     return;
2310
2311   printf("token: '%s'\n", setup_file_list->token);
2312   printf("value: '%s'\n", setup_file_list->value);
2313
2314   printSetupFileList(setup_file_list->next);
2315 }
2316 #endif
2317
2318 static struct SetupFileList *loadSetupFileList(char *filename)
2319 {
2320   int line_len;
2321   char line[MAX_LINE_LEN];
2322   char *token, *value, *line_ptr;
2323   struct SetupFileList *setup_file_list = newSetupFileList("", "");
2324   struct SetupFileList *first_valid_list_entry;
2325
2326   FILE *file;
2327
2328   if (!(file = fopen(filename, MODE_READ)))
2329   {
2330     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2331     return NULL;
2332   }
2333
2334   while(!feof(file))
2335   {
2336     /* read next line of input file */
2337     if (!fgets(line, MAX_LINE_LEN, file))
2338       break;
2339
2340     /* cut trailing comment or whitespace from input line */
2341     for (line_ptr = line; *line_ptr; line_ptr++)
2342     {
2343       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2344       {
2345         *line_ptr = '\0';
2346         break;
2347       }
2348     }
2349
2350     /* cut trailing whitespaces from input line */
2351     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2352       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2353         *line_ptr = '\0';
2354
2355     /* ignore empty lines */
2356     if (*line == '\0')
2357       continue;
2358
2359     line_len = strlen(line);
2360
2361     /* cut leading whitespaces from token */
2362     for (token = line; *token; token++)
2363       if (*token != ' ' && *token != '\t')
2364         break;
2365
2366     /* find end of token */
2367     for (line_ptr = token; *line_ptr; line_ptr++)
2368     {
2369       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2370       {
2371         *line_ptr = '\0';
2372         break;
2373       }
2374     }
2375
2376     if (line_ptr < line + line_len)
2377       value = line_ptr + 1;
2378     else
2379       value = "\0";
2380
2381     /* cut leading whitespaces from value */
2382     for (; *value; value++)
2383       if (*value != ' ' && *value != '\t')
2384         break;
2385
2386     if (*token && *value)
2387       setTokenValue(setup_file_list, token, value);
2388   }
2389
2390   fclose(file);
2391
2392   first_valid_list_entry = setup_file_list->next;
2393
2394   /* free empty list header */
2395   setup_file_list->next = NULL;
2396   freeSetupFileList(setup_file_list);
2397
2398   if (first_valid_list_entry == NULL)
2399     Error(ERR_WARN, "configuration file '%s' is empty", filename);
2400
2401   return first_valid_list_entry;
2402 }
2403
2404 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2405                                          char *identifier)
2406 {
2407   if (!setup_file_list)
2408     return;
2409
2410   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2411   {
2412     if (strcmp(setup_file_list->value, identifier) != 0)
2413     {
2414       Error(ERR_WARN, "configuration file has wrong version");
2415       return;
2416     }
2417     else
2418       return;
2419   }
2420
2421   if (setup_file_list->next)
2422     checkSetupFileListIdentifier(setup_file_list->next, identifier);
2423   else
2424   {
2425     Error(ERR_WARN, "configuration file has no version information");
2426     return;
2427   }
2428 }
2429
2430 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2431 {
2432   ldi->filename = NULL;
2433   ldi->fullpath = NULL;
2434   ldi->basepath = NULL;
2435   ldi->name = getStringCopy(ANONYMOUS_NAME);
2436   ldi->name_short = NULL;
2437   ldi->name_sorting = NULL;
2438   ldi->author = getStringCopy(ANONYMOUS_NAME);
2439   ldi->imported_from = NULL;
2440   ldi->levels = 0;
2441   ldi->first_level = 0;
2442   ldi->last_level = 0;
2443   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
2444   ldi->level_group = FALSE;
2445   ldi->parent_link = FALSE;
2446   ldi->user_defined = FALSE;
2447   ldi->readonly = TRUE;
2448   ldi->color = 0;
2449   ldi->class_desc = NULL;
2450   ldi->handicap_level = 0;
2451   ldi->cl_first = -1;
2452   ldi->cl_cursor = -1;
2453
2454   ldi->node_parent = NULL;
2455   ldi->node_group = NULL;
2456   ldi->next = NULL;
2457 }
2458
2459 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2460                                                 struct LevelDirInfo *parent)
2461 {
2462   if (parent == NULL)
2463   {
2464     setLevelDirInfoToDefaults(ldi);
2465     return;
2466   }
2467
2468   /* first copy all values from the parent structure ... */
2469   *ldi = *parent;
2470
2471   /* ... then set all fields to default that cannot be inherited from parent.
2472      This is especially important for all those fields that can be set from
2473      the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2474      calls 'free()' for all already set token values which requires that no
2475      other structure's pointer may point to them!
2476   */
2477
2478   ldi->filename = NULL;
2479   ldi->fullpath = NULL;
2480   ldi->basepath = NULL;
2481   ldi->name = getStringCopy(ANONYMOUS_NAME);
2482   ldi->name_short = NULL;
2483   ldi->name_sorting = NULL;
2484   ldi->author = getStringCopy(parent->author);
2485   ldi->imported_from = getStringCopy(parent->imported_from);
2486
2487   ldi->level_group = FALSE;
2488   ldi->parent_link = FALSE;
2489
2490   ldi->node_parent = parent;
2491   ldi->node_group = NULL;
2492   ldi->next = NULL;
2493 }
2494
2495 static void setSetupInfoToDefaults(struct SetupInfo *si)
2496 {
2497   int i;
2498
2499   si->player_name = getStringCopy(getLoginName());
2500
2501   si->sound = TRUE;
2502   si->sound_loops = TRUE;
2503   si->sound_music = TRUE;
2504   si->sound_simple = TRUE;
2505   si->toons = TRUE;
2506   si->double_buffering = TRUE;
2507   si->direct_draw = !si->double_buffering;
2508   si->scroll_delay = TRUE;
2509   si->soft_scrolling = TRUE;
2510   si->fading = FALSE;
2511   si->autorecord = TRUE;
2512   si->quick_doors = FALSE;
2513   si->team_mode = FALSE;
2514   si->handicap = TRUE;
2515   si->time_limit = TRUE;
2516   si->fullscreen = FALSE;
2517
2518   for (i=0; i<MAX_PLAYERS; i++)
2519   {
2520     si->input[i].use_joystick = FALSE;
2521     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2522     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2523     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2524     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2525     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2526     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2527     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2528     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2529     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2530     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2531     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2532     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2533     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2534     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2535     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2536   }
2537 }
2538
2539 static void setSetupInfo(int token_nr, char *token_value)
2540 {
2541   int token_type = token_info[token_nr].type;
2542   void *setup_value = token_info[token_nr].value;
2543
2544   if (token_value == NULL)
2545     return;
2546
2547   /* set setup field to corresponding token value */
2548   switch (token_type)
2549   {
2550     case TYPE_BOOLEAN:
2551     case TYPE_SWITCH:
2552       *(boolean *)setup_value = get_string_boolean_value(token_value);
2553       break;
2554
2555     case TYPE_KEY:
2556       *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2557       break;
2558
2559     case TYPE_INTEGER:
2560       *(int *)setup_value = get_string_integer_value(token_value);
2561       break;
2562
2563     case TYPE_STRING:
2564       if (*(char **)setup_value != NULL)
2565         free(*(char **)setup_value);
2566       *(char **)setup_value = getStringCopy(token_value);
2567       break;
2568
2569     default:
2570       break;
2571   }
2572 }
2573
2574 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2575 {
2576   int i, pnr;
2577
2578   if (!setup_file_list)
2579     return;
2580
2581   /* handle global setup values */
2582   si = setup;
2583   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2584     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2585   setup = si;
2586
2587   /* handle player specific setup values */
2588   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2589   {
2590     char prefix[30];
2591
2592     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2593
2594     sii = setup.input[pnr];
2595     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2596     {
2597       char full_token[100];
2598
2599       sprintf(full_token, "%s%s", prefix, token_info[i].text);
2600       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2601     }
2602     setup.input[pnr] = sii;
2603   }
2604 }
2605
2606 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2607 {
2608   const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2609   const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2610   int compare_result;
2611
2612   if (entry1->parent_link || entry2->parent_link)
2613     compare_result = (entry1->parent_link ? -1 : +1);
2614   else if (entry1->sort_priority == entry2->sort_priority)
2615   {
2616     char *name1 = getStringToLower(entry1->name_sorting);
2617     char *name2 = getStringToLower(entry2->name_sorting);
2618
2619     compare_result = strcmp(name1, name2);
2620
2621     free(name1);
2622     free(name2);
2623   }
2624   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2625     compare_result = entry1->sort_priority - entry2->sort_priority;
2626   else
2627     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2628
2629   return compare_result;
2630 }
2631
2632 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2633 {
2634   struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2635
2636   setLevelDirInfoToDefaults(leveldir_new);
2637
2638   leveldir_new->node_parent = node_parent;
2639   leveldir_new->parent_link = TRUE;
2640
2641   leveldir_new->name = ".. (parent directory)";
2642   leveldir_new->name_short = getStringCopy(leveldir_new->name);
2643   leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2644
2645   leveldir_new->filename = "..";
2646   leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2647
2648   leveldir_new->sort_priority = node_parent->sort_priority;
2649   leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2650
2651   pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2652 }
2653
2654 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2655                                       struct LevelDirInfo *node_parent,
2656                                       char *level_directory)
2657 {
2658   DIR *dir;
2659   struct dirent *dir_entry;
2660   boolean valid_entry_found = FALSE;
2661
2662   if ((dir = opendir(level_directory)) == NULL)
2663   {
2664     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2665     return;
2666   }
2667
2668   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2669   {
2670     struct SetupFileList *setup_file_list = NULL;
2671     struct stat file_status;
2672     char *directory_name = dir_entry->d_name;
2673     char *directory_path = getPath2(level_directory, directory_name);
2674     char *filename = NULL;
2675
2676     /* skip entries for current and parent directory */
2677     if (strcmp(directory_name, ".")  == 0 ||
2678         strcmp(directory_name, "..") == 0)
2679     {
2680       free(directory_path);
2681       continue;
2682     }
2683
2684     /* find out if directory entry is itself a directory */
2685     if (stat(directory_path, &file_status) != 0 ||      /* cannot stat file */
2686         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
2687     {
2688       free(directory_path);
2689       continue;
2690     }
2691
2692     filename = getPath2(directory_path, LEVELINFO_FILENAME);
2693     setup_file_list = loadSetupFileList(filename);
2694
2695     if (setup_file_list)
2696     {
2697       struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2698       int i;
2699
2700       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2701       setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2702
2703       /* set all structure fields according to the token/value pairs */
2704       ldi = *leveldir_new;
2705       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2706         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2707       *leveldir_new = ldi;
2708
2709       DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2710
2711       if (leveldir_new->name_short == NULL)
2712         leveldir_new->name_short = getStringCopy(leveldir_new->name);
2713
2714       if (leveldir_new->name_sorting == NULL)
2715         leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2716
2717       leveldir_new->filename = getStringCopy(directory_name);
2718
2719       if (node_parent == NULL)          /* top level group */
2720       {
2721         leveldir_new->basepath = level_directory;
2722         leveldir_new->fullpath = leveldir_new->filename;
2723       }
2724       else                              /* sub level group */
2725       {
2726         leveldir_new->basepath = node_parent->basepath;
2727         leveldir_new->fullpath = getPath2(node_parent->fullpath,
2728                                           directory_name);
2729       }
2730
2731       if (leveldir_new->levels < 1)
2732         leveldir_new->levels = 1;
2733
2734       leveldir_new->last_level =
2735         leveldir_new->first_level + leveldir_new->levels - 1;
2736
2737       leveldir_new->user_defined =
2738         (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2739
2740       leveldir_new->color = LEVELCOLOR(leveldir_new);
2741       leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2742
2743       leveldir_new->handicap_level =    /* set handicap to default value */
2744         (leveldir_new->user_defined ?
2745          leveldir_new->last_level :
2746          leveldir_new->first_level);
2747
2748       pushLevelDirInfo(node_first, leveldir_new);
2749
2750       freeSetupFileList(setup_file_list);
2751       valid_entry_found = TRUE;
2752
2753       if (leveldir_new->level_group)
2754       {
2755         /* create node to link back to current level directory */
2756         createParentLevelDirNode(leveldir_new);
2757
2758         /* step into sub-directory and look for more level series */
2759         LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2760                                   leveldir_new, directory_path);
2761       }
2762     }
2763     else
2764       Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2765
2766     free(directory_path);
2767     free(filename);
2768   }
2769
2770   closedir(dir);
2771
2772   if (!valid_entry_found)
2773     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2774           level_directory);
2775 }
2776
2777 void LoadLevelInfo()
2778 {
2779   InitUserLevelDirectory(getLoginName());
2780
2781   DrawInitText("Loading level series:", 120, FC_GREEN);
2782
2783   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2784   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2785
2786   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2787
2788   if (leveldir_first == NULL)
2789     Error(ERR_EXIT, "cannot find any valid level series in any directory");
2790
2791   sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2792
2793 #if 0
2794   dumpLevelDirInfo(leveldir_first, 0);
2795 #endif
2796 }
2797
2798 static void SaveUserLevelInfo()
2799 {
2800   char *filename;
2801   FILE *file;
2802   int i;
2803
2804   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2805
2806   if (!(file = fopen(filename, MODE_WRITE)))
2807   {
2808     Error(ERR_WARN, "cannot write level info file '%s'", filename);
2809     free(filename);
2810     return;
2811   }
2812
2813   /* always start with reliable default values */
2814   setLevelDirInfoToDefaults(&ldi);
2815
2816   ldi.name = getLoginName();
2817   ldi.author = getRealName();
2818   ldi.levels = 100;
2819   ldi.first_level = 1;
2820   ldi.sort_priority = LEVELCLASS_USER_START;
2821   ldi.readonly = FALSE;
2822
2823   fprintf(file, "%s\n\n",
2824           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2825
2826   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2827     if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2828         i != LEVELINFO_TOKEN_NAME_SORTING &&
2829         i != LEVELINFO_TOKEN_IMPORTED_FROM)
2830       fprintf(file, "%s\n", getSetupLine("", i));
2831
2832   fclose(file);
2833   free(filename);
2834
2835   chmod(filename, SETUP_PERMS);
2836 }
2837
2838 void LoadSetup()
2839 {
2840   char *filename;
2841   struct SetupFileList *setup_file_list = NULL;
2842
2843   /* always start with reliable default values */
2844   setSetupInfoToDefaults(&setup);
2845
2846   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2847
2848   setup_file_list = loadSetupFileList(filename);
2849
2850   if (setup_file_list)
2851   {
2852     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2853     decodeSetupFileList(setup_file_list);
2854
2855     setup.direct_draw = !setup.double_buffering;
2856
2857     freeSetupFileList(setup_file_list);
2858
2859     /* needed to work around problems with fixed length strings */
2860     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2861       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2862     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2863     {
2864       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2865
2866       strcpy(new_name, setup.player_name);
2867       free(setup.player_name);
2868       setup.player_name = new_name;
2869     }
2870   }
2871   else
2872     Error(ERR_WARN, "using default setup values");
2873
2874   free(filename);
2875 }
2876
2877 static char *getSetupLine(char *prefix, int token_nr)
2878 {
2879   int i;
2880   static char entry[MAX_LINE_LEN];
2881   int token_type = token_info[token_nr].type;
2882   void *setup_value = token_info[token_nr].value;
2883   char *token_text = token_info[token_nr].text;
2884
2885   /* start with the prefix, token and some spaces to format output line */
2886   sprintf(entry, "%s%s:", prefix, token_text);
2887   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2888     strcat(entry, " ");
2889
2890   /* continue with the token's value (which can have different types) */
2891   switch (token_type)
2892   {
2893     case TYPE_BOOLEAN:
2894       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2895       break;
2896
2897     case TYPE_SWITCH:
2898       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2899       break;
2900
2901     case TYPE_KEY:
2902       {
2903         Key key = *(Key *)setup_value;
2904         char *keyname = getKeyNameFromKey(key);
2905
2906         strcat(entry, getX11KeyNameFromKey(key));
2907         for (i=strlen(entry); i<50; i++)
2908           strcat(entry, " ");
2909
2910         /* add comment, if useful */
2911         if (strcmp(keyname, "(undefined)") != 0 &&
2912             strcmp(keyname, "(unknown)") != 0)
2913         {
2914           strcat(entry, "# ");
2915           strcat(entry, keyname);
2916         }
2917       }
2918       break;
2919
2920     case TYPE_INTEGER:
2921       {
2922         char buffer[MAX_LINE_LEN];
2923
2924         sprintf(buffer, "%d", *(int *)setup_value);
2925         strcat(entry, buffer);
2926       }
2927       break;
2928
2929     case TYPE_STRING:
2930       strcat(entry, *(char **)setup_value);
2931       break;
2932
2933     default:
2934       break;
2935   }
2936
2937   return entry;
2938 }
2939
2940 void SaveSetup()
2941 {
2942   int i, pnr;
2943   char *filename;
2944   FILE *file;
2945
2946   InitUserDataDirectory();
2947
2948   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2949
2950   if (!(file = fopen(filename, MODE_WRITE)))
2951   {
2952     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2953     free(filename);
2954     return;
2955   }
2956
2957   fprintf(file, "%s\n",
2958           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2959   fprintf(file, "\n");
2960
2961   /* handle global setup values */
2962   si = setup;
2963   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2964   {
2965     fprintf(file, "%s\n", getSetupLine("", i));
2966
2967     /* just to make things nicer :) */
2968     if (i == SETUP_TOKEN_PLAYER_NAME)
2969       fprintf(file, "\n");
2970   }
2971
2972   /* handle player specific setup values */
2973   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2974   {
2975     char prefix[30];
2976
2977     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2978     fprintf(file, "\n");
2979
2980     sii = setup.input[pnr];
2981     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2982       fprintf(file, "%s\n", getSetupLine(prefix, i));
2983   }
2984
2985   fclose(file);
2986   free(filename);
2987
2988   chmod(filename, SETUP_PERMS);
2989 }
2990
2991 void LoadLevelSetup_LastSeries()
2992 {
2993   char *filename;
2994   struct SetupFileList *level_setup_list = NULL;
2995
2996   /* always start with reliable default values */
2997   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2998
2999   /* ----------------------------------------------------------------------- */
3000   /* ~/.rocksndiamonds/levelsetup.conf                                       */
3001   /* ----------------------------------------------------------------------- */
3002
3003   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3004
3005   if ((level_setup_list = loadSetupFileList(filename)))
3006   {
3007     char *last_level_series =
3008       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
3009
3010     leveldir_current = getLevelDirInfoFromFilename(last_level_series);
3011     if (leveldir_current == NULL)
3012       leveldir_current = leveldir_first;
3013
3014     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3015
3016     freeSetupFileList(level_setup_list);
3017   }
3018   else
3019     Error(ERR_WARN, "using default setup values");
3020
3021   free(filename);
3022 }
3023
3024 void SaveLevelSetup_LastSeries()
3025 {
3026   char *filename;
3027   char *level_subdir = leveldir_current->filename;
3028   FILE *file;
3029
3030   /* ----------------------------------------------------------------------- */
3031   /* ~/.rocksndiamonds/levelsetup.conf                                       */
3032   /* ----------------------------------------------------------------------- */
3033
3034   InitUserDataDirectory();
3035
3036   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3037
3038   if (!(file = fopen(filename, MODE_WRITE)))
3039   {
3040     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3041     free(filename);
3042     return;
3043   }
3044
3045   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3046                                                  LEVELSETUP_COOKIE));
3047   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3048                                                level_subdir));
3049
3050   fclose(file);
3051   free(filename);
3052
3053   chmod(filename, SETUP_PERMS);
3054 }
3055
3056 static void checkSeriesInfo()
3057 {
3058   static char *level_directory = NULL;
3059   DIR *dir;
3060   struct dirent *dir_entry;
3061
3062   /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3063
3064   level_directory = getPath2((leveldir_current->user_defined ?
3065                               getUserLevelDir("") :
3066                               options.level_directory),
3067                              leveldir_current->fullpath);
3068
3069   if ((dir = opendir(level_directory)) == NULL)
3070   {
3071     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3072     return;
3073   }
3074
3075   while ((dir_entry = readdir(dir)) != NULL)    /* last directory entry */
3076   {
3077     if (strlen(dir_entry->d_name) > 4 &&
3078         dir_entry->d_name[3] == '.' &&
3079         strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3080     {
3081       char levelnum_str[4];
3082       int levelnum_value;
3083
3084       strncpy(levelnum_str, dir_entry->d_name, 3);
3085       levelnum_str[3] = '\0';
3086
3087       levelnum_value = atoi(levelnum_str);
3088
3089       if (levelnum_value < leveldir_current->first_level)
3090       {
3091         Error(ERR_WARN, "additional level %d found", levelnum_value);
3092         leveldir_current->first_level = levelnum_value;
3093       }
3094       else if (levelnum_value > leveldir_current->last_level)
3095       {
3096         Error(ERR_WARN, "additional level %d found", levelnum_value);
3097         leveldir_current->last_level = levelnum_value;
3098       }
3099     }
3100   }
3101
3102   closedir(dir);
3103 }
3104
3105 void LoadLevelSetup_SeriesInfo()
3106 {
3107   char *filename;
3108   struct SetupFileList *level_setup_list = NULL;
3109   char *level_subdir = leveldir_current->filename;
3110
3111   /* always start with reliable default values */
3112   level_nr = leveldir_current->first_level;
3113
3114   checkSeriesInfo(leveldir_current);
3115
3116   /* ----------------------------------------------------------------------- */
3117   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
3118   /* ----------------------------------------------------------------------- */
3119
3120   level_subdir = leveldir_current->filename;
3121
3122   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3123
3124   if ((level_setup_list = loadSetupFileList(filename)))
3125   {
3126     char *token_value;
3127
3128     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3129
3130     if (token_value)
3131     {
3132       level_nr = atoi(token_value);
3133
3134       if (level_nr < leveldir_current->first_level)
3135         level_nr = leveldir_current->first_level;
3136       if (level_nr > leveldir_current->last_level)
3137         level_nr = leveldir_current->last_level;
3138     }
3139
3140     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3141
3142     if (token_value)
3143     {
3144       int level_nr = atoi(token_value);
3145
3146       if (level_nr < leveldir_current->first_level)
3147         level_nr = leveldir_current->first_level;
3148       if (level_nr > leveldir_current->last_level + 1)
3149         level_nr = leveldir_current->last_level;
3150
3151       if (leveldir_current->user_defined)
3152         level_nr = leveldir_current->last_level;
3153
3154       leveldir_current->handicap_level = level_nr;
3155     }
3156
3157     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3158
3159     freeSetupFileList(level_setup_list);
3160   }
3161   else
3162     Error(ERR_WARN, "using default setup values");
3163
3164   free(filename);
3165 }
3166
3167 void SaveLevelSetup_SeriesInfo()
3168 {
3169   char *filename;
3170   char *level_subdir = leveldir_current->filename;
3171   char *level_nr_str = int2str(level_nr, 0);
3172   char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3173   FILE *file;
3174
3175   /* ----------------------------------------------------------------------- */
3176   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
3177   /* ----------------------------------------------------------------------- */
3178
3179   InitLevelSetupDirectory(level_subdir);
3180
3181   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3182
3183   if (!(file = fopen(filename, MODE_WRITE)))
3184   {
3185     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3186     free(filename);
3187     return;
3188   }
3189
3190   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3191                                                  LEVELSETUP_COOKIE));
3192   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3193                                                level_nr_str));
3194   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3195                                                handicap_level_str));
3196
3197   fclose(file);
3198   free(filename);
3199
3200   chmod(filename, SETUP_PERMS);
3201 }
3202 /*  LocalWords:  Rocks'n
3203  */