rnd-20020314-2-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <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
1317 void OLD_LoadTape(int level_nr)
1318 {
1319   int i, j;
1320   char *filename = getTapeFilename(level_nr);
1321   char cookie[MAX_LINE_LEN];
1322   char chunk_name[CHUNK_ID_LEN + 1];
1323   FILE *file;
1324   int num_participating_players;
1325   int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1326   int chunk_size;
1327
1328   /* always start with reliable default values (empty tape) */
1329   tape.file_version = FILE_VERSION_ACTUAL;
1330   tape.game_version = GAME_VERSION_ACTUAL;
1331   TapeErase();
1332
1333   /* default values (also for pre-1.2 tapes) with only the first player */
1334   tape.player_participates[0] = TRUE;
1335   for(i=1; i<MAX_PLAYERS; i++)
1336     tape.player_participates[i] = FALSE;
1337
1338   /* at least one (default: the first) player participates in every tape */
1339   num_participating_players = 1;
1340
1341   if (!(file = fopen(filename, MODE_READ)))
1342     return;
1343
1344   /* check file identifier */
1345   fgets(cookie, MAX_LINE_LEN, file);
1346   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1347     cookie[strlen(cookie) - 1] = '\0';
1348
1349 #if 0
1350   if (strcmp(cookie, TAPE_COOKIE_10) == 0)      /* old 1.0 tape format */
1351     file_version = FILE_VERSION_1_0;
1352   else if (strcmp(cookie, TAPE_COOKIE) != 0)    /* unknown tape format */
1353   {
1354     Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1355     fclose(file);
1356     return;
1357   }
1358 #else
1359   if (!checkCookieString(cookie, TAPE_COOKIE))  /* unknown file format */
1360   {
1361     Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1362     fclose(file);
1363     return;
1364   }
1365
1366   file_version = getFileVersionFromCookieString(cookie);
1367 #endif
1368
1369   tape.file_version = file_version;
1370   tape.game_version = file_version;
1371
1372   /* read chunk "HEAD" */
1373   if (file_version >= FILE_VERSION_1_2)
1374   {
1375     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1376     if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1377     {
1378       Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1379       fclose(file);
1380       return;
1381     }
1382   }
1383
1384   tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1385   tape.date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1386   tape.length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1387
1388   /* read header fields that are new since version 1.2 */
1389   if (file_version >= FILE_VERSION_1_2)
1390   {
1391     byte store_participating_players = fgetc(file);
1392
1393     for(i=0; i<TAPE_HEADER_UNUSED; i++)         /* skip unused header bytes */
1394       fgetc(file);
1395
1396     /* since version 1.2, tapes store which players participate in the tape */
1397     num_participating_players = 0;
1398     for(i=0; i<MAX_PLAYERS; i++)
1399     {
1400       tape.player_participates[i] = FALSE;
1401
1402       if (store_participating_players & (1 << i))
1403       {
1404         tape.player_participates[i] = TRUE;
1405         num_participating_players++;
1406       }
1407     }
1408   }
1409
1410   tape.level_nr = level_nr;
1411   tape.counter = 0;
1412   tape.changed = FALSE;
1413
1414   tape.recording = FALSE;
1415   tape.playing = FALSE;
1416   tape.pausing = FALSE;
1417
1418   /* read chunk "BODY" */
1419   if (file_version >= FILE_VERSION_1_2)
1420   {
1421     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1422     if (strcmp(chunk_name, "BODY") ||
1423         chunk_size != (num_participating_players + 1) * tape.length)
1424     {
1425       Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1426       fclose(file);
1427       return;
1428     }
1429   }
1430
1431 #if DEBUG
1432   printf("\nTAPE OF LEVEL %d\n", level_nr);
1433 #endif
1434
1435   for(i=0; i<tape.length; i++)
1436   {
1437     if (i >= MAX_TAPELEN)
1438       break;
1439
1440     for(j=0; j<MAX_PLAYERS; j++)
1441     {
1442       tape.pos[i].action[j] = MV_NO_MOVING;
1443
1444       if (tape.player_participates[j])
1445         tape.pos[i].action[j] = fgetc(file);
1446
1447 #if DEBUG
1448       {
1449         int x = tape.pos[i].action[j];
1450
1451         printf("%d:%02x ", j, x);
1452         printf("[%c%c%c%c|%c%c] - ",
1453                (x & JOY_LEFT ? '<' : ' '),
1454                (x & JOY_RIGHT ? '>' : ' '),
1455                (x & JOY_UP ? '^' : ' '),
1456                (x & JOY_DOWN ? 'v' : ' '),
1457                (x & JOY_BUTTON_1 ? '1' : ' '),
1458                (x & JOY_BUTTON_2 ? '2' : ' '));
1459       }
1460 #endif
1461
1462     }
1463
1464     tape.pos[i].delay = fgetc(file);
1465
1466 #if DEBUG
1467     printf("[%03d]\n", tape.pos[i].delay);
1468 #endif
1469
1470     if (file_version == FILE_VERSION_1_0)
1471     {
1472       /* eliminate possible diagonal moves in old tapes */
1473       /* this is only for backward compatibility */
1474
1475       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1476       byte action = tape.pos[i].action[0];
1477       int k, num_moves = 0;
1478
1479       for (k=0; k<4; k++)
1480       {
1481         if (action & joy_dir[k])
1482         {
1483           tape.pos[i + num_moves].action[0] = joy_dir[k];
1484           if (num_moves > 0)
1485             tape.pos[i + num_moves].delay = 0;
1486           num_moves++;
1487         }
1488       }
1489
1490       if (num_moves > 1)
1491       {
1492         num_moves--;
1493         i += num_moves;
1494         tape.length += num_moves;
1495       }
1496     }
1497     else if (file_version < FILE_VERSION_2_0)
1498     {
1499       if (tape.pos[i].delay > 1)
1500       {
1501         /* action part */
1502         tape.pos[i + 1] = tape.pos[i];
1503         tape.pos[i + 1].delay = 1;
1504
1505         /* delay part */
1506         for(j=0; j<MAX_PLAYERS; j++)
1507           tape.pos[i].action[j] = MV_NO_MOVING;
1508         tape.pos[i].delay--;
1509
1510         i++;
1511         tape.length++;
1512       }
1513     }
1514
1515     if (feof(file))
1516       break;
1517   }
1518
1519   fclose(file);
1520
1521   if (i != tape.length)
1522     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1523
1524   tape.length_seconds = GetTapeLength();
1525 }
1526
1527 static int LoadTape_HEAD(struct TapeInfo *tape, FILE *file, int chunk_size)
1528 {
1529   int i;
1530
1531   tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1532   tape->date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1533   tape->length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1534
1535   /* read header fields that are new since version 1.2 */
1536   if (tape->file_version >= FILE_VERSION_1_2)
1537   {
1538     byte store_participating_players = fgetc(file);
1539
1540     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1541
1542     /* since version 1.2, tapes store which players participate in the tape */
1543     tape->num_participating_players = 0;
1544     for(i=0; i<MAX_PLAYERS; i++)
1545     {
1546       tape->player_participates[i] = FALSE;
1547
1548       if (store_participating_players & (1 << i))
1549       {
1550         tape->player_participates[i] = TRUE;
1551         tape->num_participating_players++;
1552       }
1553     }
1554   }
1555
1556   tape->level_nr = level_nr;
1557   tape->counter = 0;
1558   tape->changed = FALSE;
1559
1560   tape->recording = FALSE;
1561   tape->playing = FALSE;
1562   tape->pausing = FALSE;
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 #if DEBUG
1580   printf("\nTAPE OF LEVEL %d\n", level_nr);
1581 #endif
1582
1583   for(i=0; i<tape->length; i++)
1584   {
1585     if (i >= MAX_TAPELEN)
1586       break;
1587
1588     for(j=0; j<MAX_PLAYERS; j++)
1589     {
1590       tape->pos[i].action[j] = MV_NO_MOVING;
1591
1592       if (tape->player_participates[j])
1593         tape->pos[i].action[j] = fgetc(file);
1594
1595 #if DEBUG
1596       {
1597         int x = tape->pos[i].action[j];
1598
1599         printf("%d:%02x ", j, x);
1600         printf("[%c%c%c%c|%c%c] - ",
1601                (x & JOY_LEFT ? '<' : ' '),
1602                (x & JOY_RIGHT ? '>' : ' '),
1603                (x & JOY_UP ? '^' : ' '),
1604                (x & JOY_DOWN ? 'v' : ' '),
1605                (x & JOY_BUTTON_1 ? '1' : ' '),
1606                (x & JOY_BUTTON_2 ? '2' : ' '));
1607       }
1608 #endif
1609
1610     }
1611
1612     tape->pos[i].delay = fgetc(file);
1613
1614 #if DEBUG
1615     printf("[%03d]\n", tape->pos[i].delay);
1616 #endif
1617
1618     if (tape->file_version == FILE_VERSION_1_0)
1619     {
1620       /* eliminate possible diagonal moves in old tapes */
1621       /* this is only for backward compatibility */
1622
1623       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1624       byte action = tape->pos[i].action[0];
1625       int k, num_moves = 0;
1626
1627       for (k=0; k<4; k++)
1628       {
1629         if (action & joy_dir[k])
1630         {
1631           tape->pos[i + num_moves].action[0] = joy_dir[k];
1632           if (num_moves > 0)
1633             tape->pos[i + num_moves].delay = 0;
1634           num_moves++;
1635         }
1636       }
1637
1638       if (num_moves > 1)
1639       {
1640         num_moves--;
1641         i += num_moves;
1642         tape->length += num_moves;
1643       }
1644     }
1645     else if (tape->file_version < FILE_VERSION_2_0)
1646     {
1647       if (tape->pos[i].delay > 1)
1648       {
1649         /* action part */
1650         tape->pos[i + 1] = tape->pos[i];
1651         tape->pos[i + 1].delay = 1;
1652
1653         /* delay part */
1654         for(j=0; j<MAX_PLAYERS; j++)
1655           tape->pos[i].action[j] = MV_NO_MOVING;
1656         tape->pos[i].delay--;
1657
1658         i++;
1659         tape->length++;
1660       }
1661     }
1662
1663     if (feof(file))
1664       break;
1665   }
1666
1667   if (i != tape->length)
1668     chunk_size = (tape->num_participating_players + 1) * i;
1669
1670   return chunk_size;
1671 }
1672
1673 void LoadTape(int level_nr)
1674 {
1675   char *filename = getTapeFilename(level_nr);
1676   char cookie[MAX_LINE_LEN];
1677   char chunk_name[CHUNK_ID_LEN + 1];
1678   FILE *file;
1679   int chunk_size;
1680
1681   /* always start with reliable default values */
1682   setTapeInfoToDefaults();
1683
1684   if (!(file = fopen(filename, MODE_READ)))
1685     return;
1686
1687   /* check file identifier */
1688   fgets(cookie, MAX_LINE_LEN, file);
1689   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1690     cookie[strlen(cookie) - 1] = '\0';
1691
1692   if (!checkCookieString(cookie, TAPE_COOKIE))  /* unknown file format */
1693   {
1694     Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1695     fclose(file);
1696     return;
1697   }
1698
1699   if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1700   {
1701     Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1702     fclose(file);
1703     return;
1704   }
1705
1706   tape.game_version = tape.file_version;
1707
1708   if (tape.file_version < FILE_VERSION_1_2)
1709   {
1710     /* tape files from versions before 1.2.0 without chunk structure */
1711     LoadTape_HEAD(&tape, file, TAPE_HEADER_SIZE);
1712     LoadTape_BODY(&tape, file, 2 * tape.length);
1713   }
1714   else
1715   {
1716     static struct
1717     {
1718       char *name;
1719       int size;
1720       int (*loader)(struct TapeInfo *, FILE *, int);
1721     }
1722     chunk_info[] =
1723     {
1724       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1725       { "BODY", -1,                     LoadTape_BODY },
1726       {  NULL,  0,                      NULL }
1727     };
1728
1729     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1730     {
1731       int i = 0;
1732
1733       while (chunk_info[i].name != NULL &&
1734              strcmp(chunk_name, chunk_info[i].name) != 0)
1735         i++;
1736
1737       if (chunk_info[i].name == NULL)
1738       {
1739         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1740               chunk_name, filename);
1741         ReadUnusedBytesFromFile(file, chunk_size);
1742       }
1743       else if (chunk_info[i].size != -1 &&
1744                chunk_info[i].size != chunk_size)
1745       {
1746         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1747               chunk_size, chunk_name, filename);
1748         ReadUnusedBytesFromFile(file, chunk_size);
1749       }
1750       else
1751       {
1752         /* call function to load this tape chunk */
1753         int chunk_size_expected =
1754           (chunk_info[i].loader)(&tape, file, chunk_size);
1755
1756         /* the size of some chunks cannot be checked before reading other
1757            chunks first (like "HEAD" and "BODY") that contain some header
1758            information, so check them here */
1759         if (chunk_size_expected != chunk_size)
1760         {
1761           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1762                 chunk_size, chunk_name, filename);
1763         }
1764       }
1765     }
1766   }
1767
1768   fclose(file);
1769
1770   tape.length_seconds = GetTapeLength();
1771 }
1772
1773 void OLD_SaveTape(int level_nr)
1774 {
1775   int i;
1776   char *filename = getTapeFilename(level_nr);
1777   FILE *file;
1778   boolean new_tape = TRUE;
1779   byte store_participating_players;
1780   int num_participating_players;
1781
1782   InitTapeDirectory(leveldir_current->filename);
1783
1784   /* if a tape still exists, ask to overwrite it */
1785   if (access(filename, F_OK) == 0)
1786   {
1787     new_tape = FALSE;
1788     if (!Request("Replace old tape ?", REQ_ASK))
1789       return;
1790   }
1791
1792   /* count number of players and set corresponding bits for compact storage */
1793   store_participating_players = 0;
1794   num_participating_players = 0;
1795   for(i=0; i<MAX_PLAYERS; i++)
1796   {
1797     if (tape.player_participates[i])
1798     {
1799       num_participating_players++;
1800       store_participating_players |= (1 << i);
1801     }
1802   }
1803
1804   if (!(file = fopen(filename, MODE_WRITE)))
1805   {
1806     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1807     return;
1808   }
1809
1810   fputs(TAPE_COOKIE, file);             /* file identifier */
1811   fputc('\n', file);
1812
1813   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1814
1815   putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1816   putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1817   putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1818
1819   fputc(store_participating_players, file);
1820
1821   for(i=0; i<TAPE_HEADER_UNUSED; i++)   /* set unused header bytes to zero */
1822     fputc(0, file);
1823
1824   putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1825                BYTE_ORDER_BIG_ENDIAN);
1826
1827   for(i=0; i<tape.length; i++)
1828   {
1829     int j;
1830
1831     for(j=0; j<MAX_PLAYERS; j++)
1832       if (tape.player_participates[j])
1833         fputc(tape.pos[i].action[j], file);
1834
1835     fputc(tape.pos[i].delay, file);
1836   }
1837
1838   fclose(file);
1839
1840   chmod(filename, TAPE_PERMS);
1841
1842   tape.changed = FALSE;
1843
1844   if (new_tape)
1845     Request("tape saved !", REQ_CONFIRM);
1846 }
1847
1848 static void SaveTape_HEAD(struct TapeInfo *tape, FILE *file)
1849 {
1850   int i;
1851   byte store_participating_players = 0;
1852
1853   /* set bits for participating players for compact storage */
1854   for(i=0; i<MAX_PLAYERS; i++)
1855     if (tape->player_participates[i])
1856       store_participating_players |= (1 << i);
1857
1858   putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1859   putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1860   putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1861
1862   fputc(store_participating_players, file);
1863
1864   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1865 }
1866
1867 static void SaveTape_BODY(struct TapeInfo *tape, FILE *file)
1868 {
1869   int i, j;
1870
1871   for(i=0; i<tape->length; i++)
1872   {
1873     for(j=0; j<MAX_PLAYERS; j++)
1874       if (tape->player_participates[j])
1875         fputc(tape->pos[i].action[j], file);
1876
1877     fputc(tape->pos[i].delay, file);
1878   }
1879 }
1880
1881 void SaveTape(int level_nr)
1882 {
1883   int i;
1884   char *filename = getTapeFilename(level_nr);
1885   FILE *file;
1886   boolean new_tape = TRUE;
1887   int num_participating_players = 0;
1888   int body_chunk_size;
1889
1890   InitTapeDirectory(leveldir_current->filename);
1891
1892   /* if a tape still exists, ask to overwrite it */
1893   if (access(filename, F_OK) == 0)
1894   {
1895     new_tape = FALSE;
1896     if (!Request("Replace old tape ?", REQ_ASK))
1897       return;
1898   }
1899
1900   if (!(file = fopen(filename, MODE_WRITE)))
1901   {
1902     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1903     return;
1904   }
1905
1906   /* count number of participating players  */
1907   for(i=0; i<MAX_PLAYERS; i++)
1908     if (tape.player_participates[i])
1909       num_participating_players++;
1910
1911   body_chunk_size = (num_participating_players + 1) * tape.length;
1912
1913   fputs(TAPE_COOKIE, file);             /* file identifier */
1914   fputc('\n', file);
1915
1916   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1917   SaveTape_HEAD(&tape, file);
1918
1919   putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1920   SaveTape_BODY(&tape, file);
1921
1922   fclose(file);
1923
1924   chmod(filename, TAPE_PERMS);
1925
1926   tape.changed = FALSE;
1927
1928   if (new_tape)
1929     Request("tape saved !", REQ_CONFIRM);
1930 }
1931
1932 void LoadScore(int level_nr)
1933 {
1934   int i;
1935   char *filename = getScoreFilename(level_nr);
1936   char cookie[MAX_LINE_LEN];
1937   char line[MAX_LINE_LEN];
1938   char *line_ptr;
1939   FILE *file;
1940
1941   /* always start with reliable default values */
1942   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1943   {
1944     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1945     highscore[i].Score = 0;
1946   }
1947
1948   if (!(file = fopen(filename, MODE_READ)))
1949     return;
1950
1951   /* check file identifier */
1952   fgets(cookie, MAX_LINE_LEN, file);
1953   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1954     cookie[strlen(cookie) - 1] = '\0';
1955
1956 #if 0
1957   if (strcmp(cookie, SCORE_COOKIE) != 0)
1958   {
1959     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1960     fclose(file);
1961     return;
1962   }
1963 #else
1964   if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1965   {
1966     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1967     fclose(file);
1968     return;
1969   }
1970 #endif
1971
1972   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1973   {
1974     fscanf(file, "%d", &highscore[i].Score);
1975     fgets(line, MAX_LINE_LEN, file);
1976
1977     if (line[strlen(line) - 1] == '\n')
1978       line[strlen(line) - 1] = '\0';
1979
1980     for (line_ptr = line; *line_ptr; line_ptr++)
1981     {
1982       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1983       {
1984         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1985         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1986         break;
1987       }
1988     }
1989   }
1990
1991   fclose(file);
1992 }
1993
1994 void SaveScore(int level_nr)
1995 {
1996   int i;
1997   char *filename = getScoreFilename(level_nr);
1998   FILE *file;
1999
2000   InitScoreDirectory(leveldir_current->filename);
2001
2002   if (!(file = fopen(filename, MODE_WRITE)))
2003   {
2004     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2005     return;
2006   }
2007
2008   fprintf(file, "%s\n\n", SCORE_COOKIE);
2009
2010   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2011     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2012
2013   fclose(file);
2014
2015   chmod(filename, SCORE_PERMS);
2016 }
2017
2018 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
2019 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
2020 #define TOKEN_STR_LAST_PLAYED_LEVEL     "last_played_level"
2021 #define TOKEN_STR_HANDICAP_LEVEL        "handicap_level"
2022 #define TOKEN_STR_PLAYER_PREFIX         "player_"
2023
2024 #define TOKEN_VALUE_POSITION            30
2025
2026 /* global setup */
2027 #define SETUP_TOKEN_PLAYER_NAME         0
2028 #define SETUP_TOKEN_SOUND               1
2029 #define SETUP_TOKEN_SOUND_LOOPS         2
2030 #define SETUP_TOKEN_SOUND_MUSIC         3
2031 #define SETUP_TOKEN_SOUND_SIMPLE        4
2032
2033 #if 0
2034 #define SETUP_TOKEN_TOONS               5
2035 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
2036 #endif
2037
2038 #define SETUP_TOKEN_SCROLL_DELAY        5
2039 #define SETUP_TOKEN_SOFT_SCROLLING      6
2040 #define SETUP_TOKEN_FADING              7
2041 #define SETUP_TOKEN_AUTORECORD          8
2042 #define SETUP_TOKEN_QUICK_DOORS         9
2043 #define SETUP_TOKEN_TEAM_MODE           10
2044 #define SETUP_TOKEN_HANDICAP            11
2045 #define SETUP_TOKEN_TIME_LIMIT          12
2046 #define SETUP_TOKEN_FULLSCREEN          13
2047
2048 /* player setup */
2049 #define SETUP_TOKEN_USE_JOYSTICK        14
2050 #define SETUP_TOKEN_JOY_DEVICE_NAME     15
2051 #define SETUP_TOKEN_JOY_XLEFT           16
2052 #define SETUP_TOKEN_JOY_XMIDDLE         17
2053 #define SETUP_TOKEN_JOY_XRIGHT          18
2054 #define SETUP_TOKEN_JOY_YUPPER          19
2055 #define SETUP_TOKEN_JOY_YMIDDLE         20
2056 #define SETUP_TOKEN_JOY_YLOWER          21
2057 #define SETUP_TOKEN_JOY_SNAP            22
2058 #define SETUP_TOKEN_JOY_BOMB            23
2059 #define SETUP_TOKEN_KEY_LEFT            24
2060 #define SETUP_TOKEN_KEY_RIGHT           25
2061 #define SETUP_TOKEN_KEY_UP              26
2062 #define SETUP_TOKEN_KEY_DOWN            27
2063 #define SETUP_TOKEN_KEY_SNAP            28
2064 #define SETUP_TOKEN_KEY_BOMB            29
2065
2066 /* level directory info */
2067 #define LEVELINFO_TOKEN_NAME            30
2068 #define LEVELINFO_TOKEN_NAME_SHORT      31
2069 #define LEVELINFO_TOKEN_NAME_SORTING    32
2070 #define LEVELINFO_TOKEN_AUTHOR          33
2071 #define LEVELINFO_TOKEN_IMPORTED_FROM   34
2072 #define LEVELINFO_TOKEN_LEVELS          35
2073 #define LEVELINFO_TOKEN_FIRST_LEVEL     36
2074 #define LEVELINFO_TOKEN_SORT_PRIORITY   37
2075 #define LEVELINFO_TOKEN_LEVEL_GROUP     38
2076 #define LEVELINFO_TOKEN_READONLY        39
2077
2078 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
2079 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_FULLSCREEN
2080
2081 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
2082 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
2083
2084 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
2085 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
2086
2087 #define TYPE_BOOLEAN                    1
2088 #define TYPE_SWITCH                     2
2089 #define TYPE_KEY                        3
2090 #define TYPE_INTEGER                    4
2091 #define TYPE_STRING                     5
2092
2093 static struct SetupInfo si;
2094 static struct SetupInputInfo sii;
2095 static struct LevelDirInfo ldi;
2096 static struct
2097 {
2098   int type;
2099   void *value;
2100   char *text;
2101 } token_info[] =
2102 {
2103   /* global setup */
2104   { TYPE_STRING,  &si.player_name,      "player_name"                   },
2105   { TYPE_SWITCH,  &si.sound,            "sound"                         },
2106   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
2107   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
2108   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
2109
2110 #if 0
2111   { TYPE_SWITCH,  &si.toons,            "toons"                         },
2112   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
2113 #endif
2114
2115   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
2116   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
2117   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
2118   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
2119   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
2120   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
2121   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
2122   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
2123   { TYPE_SWITCH,  &si.fullscreen,       "fullscreen"                    },
2124
2125   /* player setup */
2126   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2127   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2128   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2129   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2130   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2131   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2132   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2133   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2134   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2135   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2136   { TYPE_KEY,     &sii.key.left,        ".key.move_left"                },
2137   { TYPE_KEY,     &sii.key.right,       ".key.move_right"               },
2138   { TYPE_KEY,     &sii.key.up,          ".key.move_up"                  },
2139   { TYPE_KEY,     &sii.key.down,        ".key.move_down"                },
2140   { TYPE_KEY,     &sii.key.snap,        ".key.snap_field"               },
2141   { TYPE_KEY,     &sii.key.bomb,        ".key.place_bomb"               },
2142
2143   /* level directory info */
2144   { TYPE_STRING,  &ldi.name,            "name"                          },
2145   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
2146   { TYPE_STRING,  &ldi.name_sorting,    "name_sorting"                  },
2147   { TYPE_STRING,  &ldi.author,          "author"                        },
2148   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
2149   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
2150   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
2151   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
2152   { TYPE_BOOLEAN, &ldi.level_group,     "level_group"                   },
2153   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
2154 };
2155
2156 static char *string_tolower(char *s)
2157 {
2158   static char s_lower[100];
2159   int i;
2160
2161   if (strlen(s) >= 100)
2162     return s;
2163
2164   strcpy(s_lower, s);
2165
2166   for (i=0; i<strlen(s_lower); i++)
2167     s_lower[i] = tolower(s_lower[i]);
2168
2169   return s_lower;
2170 }
2171
2172 static int get_string_integer_value(char *s)
2173 {
2174   static char *number_text[][3] =
2175   {
2176     { "0", "zero", "null", },
2177     { "1", "one", "first" },
2178     { "2", "two", "second" },
2179     { "3", "three", "third" },
2180     { "4", "four", "fourth" },
2181     { "5", "five", "fifth" },
2182     { "6", "six", "sixth" },
2183     { "7", "seven", "seventh" },
2184     { "8", "eight", "eighth" },
2185     { "9", "nine", "ninth" },
2186     { "10", "ten", "tenth" },
2187     { "11", "eleven", "eleventh" },
2188     { "12", "twelve", "twelfth" },
2189   };
2190
2191   int i, j;
2192
2193   for (i=0; i<13; i++)
2194     for (j=0; j<3; j++)
2195       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2196         return i;
2197
2198   return atoi(s);
2199 }
2200
2201 static boolean get_string_boolean_value(char *s)
2202 {
2203   if (strcmp(string_tolower(s), "true") == 0 ||
2204       strcmp(string_tolower(s), "yes") == 0 ||
2205       strcmp(string_tolower(s), "on") == 0 ||
2206       get_string_integer_value(s) == 1)
2207     return TRUE;
2208   else
2209     return FALSE;
2210 }
2211
2212 static char *getFormattedSetupEntry(char *token, char *value)
2213 {
2214   int i;
2215   static char entry[MAX_LINE_LEN];
2216
2217   sprintf(entry, "%s:", token);
2218   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2219     entry[i] = ' ';
2220   entry[i] = '\0';
2221
2222   strcat(entry, value);
2223
2224   return entry;
2225 }
2226
2227 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2228 {
2229   if (!setup_file_list)
2230     return;
2231
2232   if (setup_file_list->token)
2233     free(setup_file_list->token);
2234   if (setup_file_list->value)
2235     free(setup_file_list->value);
2236   if (setup_file_list->next)
2237     freeSetupFileList(setup_file_list->next);
2238   free(setup_file_list);
2239 }
2240
2241 static struct SetupFileList *newSetupFileList(char *token, char *value)
2242 {
2243   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2244
2245   new->token = checked_malloc(strlen(token) + 1);
2246   strcpy(new->token, token);
2247
2248   new->value = checked_malloc(strlen(value) + 1);
2249   strcpy(new->value, value);
2250
2251   new->next = NULL;
2252
2253   return new;
2254 }
2255
2256 static char *getTokenValue(struct SetupFileList *setup_file_list,
2257                            char *token)
2258 {
2259   if (!setup_file_list)
2260     return NULL;
2261
2262   if (strcmp(setup_file_list->token, token) == 0)
2263     return setup_file_list->value;
2264   else
2265     return getTokenValue(setup_file_list->next, token);
2266 }
2267
2268 static void setTokenValue(struct SetupFileList *setup_file_list,
2269                           char *token, char *value)
2270 {
2271   if (!setup_file_list)
2272     return;
2273
2274   if (strcmp(setup_file_list->token, token) == 0)
2275   {
2276     free(setup_file_list->value);
2277     setup_file_list->value = checked_malloc(strlen(value) + 1);
2278     strcpy(setup_file_list->value, value);
2279   }
2280   else if (setup_file_list->next == NULL)
2281     setup_file_list->next = newSetupFileList(token, value);
2282   else
2283     setTokenValue(setup_file_list->next, token, value);
2284 }
2285
2286 #ifdef DEBUG
2287 static void printSetupFileList(struct SetupFileList *setup_file_list)
2288 {
2289   if (!setup_file_list)
2290     return;
2291
2292   printf("token: '%s'\n", setup_file_list->token);
2293   printf("value: '%s'\n", setup_file_list->value);
2294
2295   printSetupFileList(setup_file_list->next);
2296 }
2297 #endif
2298
2299 static struct SetupFileList *loadSetupFileList(char *filename)
2300 {
2301   int line_len;
2302   char line[MAX_LINE_LEN];
2303   char *token, *value, *line_ptr;
2304   struct SetupFileList *setup_file_list = newSetupFileList("", "");
2305   struct SetupFileList *first_valid_list_entry;
2306
2307   FILE *file;
2308
2309   if (!(file = fopen(filename, MODE_READ)))
2310   {
2311     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2312     return NULL;
2313   }
2314
2315   while(!feof(file))
2316   {
2317     /* read next line of input file */
2318     if (!fgets(line, MAX_LINE_LEN, file))
2319       break;
2320
2321     /* cut trailing comment or whitespace from input line */
2322     for (line_ptr = line; *line_ptr; line_ptr++)
2323     {
2324       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2325       {
2326         *line_ptr = '\0';
2327         break;
2328       }
2329     }
2330
2331     /* cut trailing whitespaces from input line */
2332     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2333       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2334         *line_ptr = '\0';
2335
2336     /* ignore empty lines */
2337     if (*line == '\0')
2338       continue;
2339
2340     line_len = strlen(line);
2341
2342     /* cut leading whitespaces from token */
2343     for (token = line; *token; token++)
2344       if (*token != ' ' && *token != '\t')
2345         break;
2346
2347     /* find end of token */
2348     for (line_ptr = token; *line_ptr; line_ptr++)
2349     {
2350       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2351       {
2352         *line_ptr = '\0';
2353         break;
2354       }
2355     }
2356
2357     if (line_ptr < line + line_len)
2358       value = line_ptr + 1;
2359     else
2360       value = "\0";
2361
2362     /* cut leading whitespaces from value */
2363     for (; *value; value++)
2364       if (*value != ' ' && *value != '\t')
2365         break;
2366
2367     if (*token && *value)
2368       setTokenValue(setup_file_list, token, value);
2369   }
2370
2371   fclose(file);
2372
2373   first_valid_list_entry = setup_file_list->next;
2374
2375   /* free empty list header */
2376   setup_file_list->next = NULL;
2377   freeSetupFileList(setup_file_list);
2378
2379   if (first_valid_list_entry == NULL)
2380     Error(ERR_WARN, "configuration file '%s' is empty", filename);
2381
2382   return first_valid_list_entry;
2383 }
2384
2385 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2386                                          char *identifier)
2387 {
2388   if (!setup_file_list)
2389     return;
2390
2391   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2392   {
2393     if (strcmp(setup_file_list->value, identifier) != 0)
2394     {
2395       Error(ERR_WARN, "configuration file has wrong version");
2396       return;
2397     }
2398     else
2399       return;
2400   }
2401
2402   if (setup_file_list->next)
2403     checkSetupFileListIdentifier(setup_file_list->next, identifier);
2404   else
2405   {
2406     Error(ERR_WARN, "configuration file has no version information");
2407     return;
2408   }
2409 }
2410
2411 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2412 {
2413   ldi->filename = NULL;
2414   ldi->fullpath = NULL;
2415   ldi->basepath = NULL;
2416   ldi->name = getStringCopy(ANONYMOUS_NAME);
2417   ldi->name_short = NULL;
2418   ldi->name_sorting = NULL;
2419   ldi->author = getStringCopy(ANONYMOUS_NAME);
2420   ldi->imported_from = NULL;
2421   ldi->levels = 0;
2422   ldi->first_level = 0;
2423   ldi->last_level = 0;
2424   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
2425   ldi->level_group = FALSE;
2426   ldi->parent_link = FALSE;
2427   ldi->user_defined = FALSE;
2428   ldi->readonly = TRUE;
2429   ldi->color = 0;
2430   ldi->class_desc = NULL;
2431   ldi->handicap_level = 0;
2432   ldi->cl_first = -1;
2433   ldi->cl_cursor = -1;
2434
2435   ldi->node_parent = NULL;
2436   ldi->node_group = NULL;
2437   ldi->next = NULL;
2438 }
2439
2440 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2441                                                 struct LevelDirInfo *parent)
2442 {
2443   if (parent == NULL)
2444   {
2445     setLevelDirInfoToDefaults(ldi);
2446     return;
2447   }
2448
2449   /* first copy all values from the parent structure ... */
2450   *ldi = *parent;
2451
2452   /* ... then set all fields to default that cannot be inherited from parent.
2453      This is especially important for all those fields that can be set from
2454      the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2455      calls 'free()' for all already set token values which requires that no
2456      other structure's pointer may point to them!
2457   */
2458
2459   ldi->filename = NULL;
2460   ldi->fullpath = NULL;
2461   ldi->basepath = NULL;
2462   ldi->name = getStringCopy(ANONYMOUS_NAME);
2463   ldi->name_short = NULL;
2464   ldi->name_sorting = NULL;
2465   ldi->author = getStringCopy(parent->author);
2466   ldi->imported_from = getStringCopy(parent->imported_from);
2467
2468   ldi->level_group = FALSE;
2469   ldi->parent_link = FALSE;
2470
2471   ldi->node_parent = parent;
2472   ldi->node_group = NULL;
2473   ldi->next = NULL;
2474 }
2475
2476 static void setSetupInfoToDefaults(struct SetupInfo *si)
2477 {
2478   int i;
2479
2480   si->player_name = getStringCopy(getLoginName());
2481
2482   si->sound = TRUE;
2483   si->sound_loops = TRUE;
2484   si->sound_music = TRUE;
2485   si->sound_simple = TRUE;
2486   si->toons = TRUE;
2487   si->double_buffering = TRUE;
2488   si->direct_draw = !si->double_buffering;
2489   si->scroll_delay = TRUE;
2490   si->soft_scrolling = TRUE;
2491   si->fading = FALSE;
2492   si->autorecord = TRUE;
2493   si->quick_doors = FALSE;
2494   si->team_mode = FALSE;
2495   si->handicap = TRUE;
2496   si->time_limit = TRUE;
2497   si->fullscreen = FALSE;
2498
2499   for (i=0; i<MAX_PLAYERS; i++)
2500   {
2501     si->input[i].use_joystick = FALSE;
2502     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2503     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2504     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2505     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2506     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2507     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2508     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2509     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2510     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2511     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2512     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2513     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2514     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2515     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2516     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2517   }
2518 }
2519
2520 static void setSetupInfo(int token_nr, char *token_value)
2521 {
2522   int token_type = token_info[token_nr].type;
2523   void *setup_value = token_info[token_nr].value;
2524
2525   if (token_value == NULL)
2526     return;
2527
2528   /* set setup field to corresponding token value */
2529   switch (token_type)
2530   {
2531     case TYPE_BOOLEAN:
2532     case TYPE_SWITCH:
2533       *(boolean *)setup_value = get_string_boolean_value(token_value);
2534       break;
2535
2536     case TYPE_KEY:
2537       *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2538       break;
2539
2540     case TYPE_INTEGER:
2541       *(int *)setup_value = get_string_integer_value(token_value);
2542       break;
2543
2544     case TYPE_STRING:
2545       if (*(char **)setup_value != NULL)
2546         free(*(char **)setup_value);
2547       *(char **)setup_value = getStringCopy(token_value);
2548       break;
2549
2550     default:
2551       break;
2552   }
2553 }
2554
2555 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2556 {
2557   int i, pnr;
2558
2559   if (!setup_file_list)
2560     return;
2561
2562   /* handle global setup values */
2563   si = setup;
2564   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2565     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2566   setup = si;
2567
2568   /* handle player specific setup values */
2569   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2570   {
2571     char prefix[30];
2572
2573     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2574
2575     sii = setup.input[pnr];
2576     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2577     {
2578       char full_token[100];
2579
2580       sprintf(full_token, "%s%s", prefix, token_info[i].text);
2581       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2582     }
2583     setup.input[pnr] = sii;
2584   }
2585 }
2586
2587 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2588 {
2589   const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2590   const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2591   int compare_result;
2592
2593   if (entry1->parent_link || entry2->parent_link)
2594     compare_result = (entry1->parent_link ? -1 : +1);
2595   else if (entry1->sort_priority == entry2->sort_priority)
2596   {
2597     char *name1 = getStringToLower(entry1->name_sorting);
2598     char *name2 = getStringToLower(entry2->name_sorting);
2599
2600     compare_result = strcmp(name1, name2);
2601
2602     free(name1);
2603     free(name2);
2604   }
2605   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2606     compare_result = entry1->sort_priority - entry2->sort_priority;
2607   else
2608     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2609
2610   return compare_result;
2611 }
2612
2613 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2614 {
2615   struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2616
2617   setLevelDirInfoToDefaults(leveldir_new);
2618
2619   leveldir_new->node_parent = node_parent;
2620   leveldir_new->parent_link = TRUE;
2621
2622   leveldir_new->name = ".. (parent directory)";
2623   leveldir_new->name_short = getStringCopy(leveldir_new->name);
2624   leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2625
2626   leveldir_new->filename = "..";
2627   leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2628
2629   leveldir_new->sort_priority = node_parent->sort_priority;
2630   leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2631
2632   pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2633 }
2634
2635 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2636                                       struct LevelDirInfo *node_parent,
2637                                       char *level_directory)
2638 {
2639   DIR *dir;
2640   struct dirent *dir_entry;
2641   boolean valid_entry_found = FALSE;
2642
2643   if ((dir = opendir(level_directory)) == NULL)
2644   {
2645     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2646     return;
2647   }
2648
2649   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2650   {
2651     struct SetupFileList *setup_file_list = NULL;
2652     struct stat file_status;
2653     char *directory_name = dir_entry->d_name;
2654     char *directory_path = getPath2(level_directory, directory_name);
2655     char *filename = NULL;
2656
2657     /* skip entries for current and parent directory */
2658     if (strcmp(directory_name, ".")  == 0 ||
2659         strcmp(directory_name, "..") == 0)
2660     {
2661       free(directory_path);
2662       continue;
2663     }
2664
2665     /* find out if directory entry is itself a directory */
2666     if (stat(directory_path, &file_status) != 0 ||      /* cannot stat file */
2667         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
2668     {
2669       free(directory_path);
2670       continue;
2671     }
2672
2673     filename = getPath2(directory_path, LEVELINFO_FILENAME);
2674     setup_file_list = loadSetupFileList(filename);
2675
2676     if (setup_file_list)
2677     {
2678       struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2679       int i;
2680
2681       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2682       setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2683
2684       /* set all structure fields according to the token/value pairs */
2685       ldi = *leveldir_new;
2686       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2687         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2688       *leveldir_new = ldi;
2689
2690       DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2691
2692       if (leveldir_new->name_short == NULL)
2693         leveldir_new->name_short = getStringCopy(leveldir_new->name);
2694
2695       if (leveldir_new->name_sorting == NULL)
2696         leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2697
2698       leveldir_new->filename = getStringCopy(directory_name);
2699
2700       if (node_parent == NULL)          /* top level group */
2701       {
2702         leveldir_new->basepath = level_directory;
2703         leveldir_new->fullpath = leveldir_new->filename;
2704       }
2705       else                              /* sub level group */
2706       {
2707         leveldir_new->basepath = node_parent->basepath;
2708         leveldir_new->fullpath = getPath2(node_parent->fullpath,
2709                                           directory_name);
2710       }
2711
2712       if (leveldir_new->levels < 1)
2713         leveldir_new->levels = 1;
2714
2715       leveldir_new->last_level =
2716         leveldir_new->first_level + leveldir_new->levels - 1;
2717
2718       leveldir_new->user_defined =
2719         (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2720
2721       leveldir_new->color = LEVELCOLOR(leveldir_new);
2722       leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2723
2724       leveldir_new->handicap_level =    /* set handicap to default value */
2725         (leveldir_new->user_defined ?
2726          leveldir_new->last_level :
2727          leveldir_new->first_level);
2728
2729       pushLevelDirInfo(node_first, leveldir_new);
2730
2731       freeSetupFileList(setup_file_list);
2732       valid_entry_found = TRUE;
2733
2734       if (leveldir_new->level_group)
2735       {
2736         /* create node to link back to current level directory */
2737         createParentLevelDirNode(leveldir_new);
2738
2739         /* step into sub-directory and look for more level series */
2740         LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2741                                   leveldir_new, directory_path);
2742       }
2743     }
2744     else
2745       Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2746
2747     free(directory_path);
2748     free(filename);
2749   }
2750
2751   closedir(dir);
2752
2753   if (!valid_entry_found)
2754     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2755           level_directory);
2756 }
2757
2758 void LoadLevelInfo()
2759 {
2760   InitUserLevelDirectory(getLoginName());
2761
2762   DrawInitText("Loading level series:", 120, FC_GREEN);
2763
2764   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2765   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2766
2767   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2768
2769   if (leveldir_first == NULL)
2770     Error(ERR_EXIT, "cannot find any valid level series in any directory");
2771
2772   sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2773
2774 #if 0
2775   dumpLevelDirInfo(leveldir_first, 0);
2776 #endif
2777 }
2778
2779 static void SaveUserLevelInfo()
2780 {
2781   char *filename;
2782   FILE *file;
2783   int i;
2784
2785   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2786
2787   if (!(file = fopen(filename, MODE_WRITE)))
2788   {
2789     Error(ERR_WARN, "cannot write level info file '%s'", filename);
2790     free(filename);
2791     return;
2792   }
2793
2794   /* always start with reliable default values */
2795   setLevelDirInfoToDefaults(&ldi);
2796
2797   ldi.name = getLoginName();
2798   ldi.author = getRealName();
2799   ldi.levels = 100;
2800   ldi.first_level = 1;
2801   ldi.sort_priority = LEVELCLASS_USER_START;
2802   ldi.readonly = FALSE;
2803
2804   fprintf(file, "%s\n\n",
2805           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2806
2807   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2808     if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2809         i != LEVELINFO_TOKEN_NAME_SORTING &&
2810         i != LEVELINFO_TOKEN_IMPORTED_FROM)
2811       fprintf(file, "%s\n", getSetupLine("", i));
2812
2813   fclose(file);
2814   free(filename);
2815
2816   chmod(filename, SETUP_PERMS);
2817 }
2818
2819 void LoadSetup()
2820 {
2821   char *filename;
2822   struct SetupFileList *setup_file_list = NULL;
2823
2824   /* always start with reliable default values */
2825   setSetupInfoToDefaults(&setup);
2826
2827   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2828
2829   setup_file_list = loadSetupFileList(filename);
2830
2831   if (setup_file_list)
2832   {
2833     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2834     decodeSetupFileList(setup_file_list);
2835
2836     setup.direct_draw = !setup.double_buffering;
2837
2838     freeSetupFileList(setup_file_list);
2839
2840     /* needed to work around problems with fixed length strings */
2841     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2842       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2843     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2844     {
2845       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2846
2847       strcpy(new_name, setup.player_name);
2848       free(setup.player_name);
2849       setup.player_name = new_name;
2850     }
2851   }
2852   else
2853     Error(ERR_WARN, "using default setup values");
2854
2855   free(filename);
2856 }
2857
2858 static char *getSetupLine(char *prefix, int token_nr)
2859 {
2860   int i;
2861   static char entry[MAX_LINE_LEN];
2862   int token_type = token_info[token_nr].type;
2863   void *setup_value = token_info[token_nr].value;
2864   char *token_text = token_info[token_nr].text;
2865
2866   /* start with the prefix, token and some spaces to format output line */
2867   sprintf(entry, "%s%s:", prefix, token_text);
2868   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2869     strcat(entry, " ");
2870
2871   /* continue with the token's value (which can have different types) */
2872   switch (token_type)
2873   {
2874     case TYPE_BOOLEAN:
2875       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2876       break;
2877
2878     case TYPE_SWITCH:
2879       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2880       break;
2881
2882     case TYPE_KEY:
2883       {
2884         Key key = *(Key *)setup_value;
2885         char *keyname = getKeyNameFromKey(key);
2886
2887         strcat(entry, getX11KeyNameFromKey(key));
2888         for (i=strlen(entry); i<50; i++)
2889           strcat(entry, " ");
2890
2891         /* add comment, if useful */
2892         if (strcmp(keyname, "(undefined)") != 0 &&
2893             strcmp(keyname, "(unknown)") != 0)
2894         {
2895           strcat(entry, "# ");
2896           strcat(entry, keyname);
2897         }
2898       }
2899       break;
2900
2901     case TYPE_INTEGER:
2902       {
2903         char buffer[MAX_LINE_LEN];
2904
2905         sprintf(buffer, "%d", *(int *)setup_value);
2906         strcat(entry, buffer);
2907       }
2908       break;
2909
2910     case TYPE_STRING:
2911       strcat(entry, *(char **)setup_value);
2912       break;
2913
2914     default:
2915       break;
2916   }
2917
2918   return entry;
2919 }
2920
2921 void SaveSetup()
2922 {
2923   int i, pnr;
2924   char *filename;
2925   FILE *file;
2926
2927   InitUserDataDirectory();
2928
2929   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2930
2931   if (!(file = fopen(filename, MODE_WRITE)))
2932   {
2933     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2934     free(filename);
2935     return;
2936   }
2937
2938   fprintf(file, "%s\n",
2939           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2940   fprintf(file, "\n");
2941
2942   /* handle global setup values */
2943   si = setup;
2944   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2945   {
2946     fprintf(file, "%s\n", getSetupLine("", i));
2947
2948     /* just to make things nicer :) */
2949     if (i == SETUP_TOKEN_PLAYER_NAME)
2950       fprintf(file, "\n");
2951   }
2952
2953   /* handle player specific setup values */
2954   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2955   {
2956     char prefix[30];
2957
2958     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2959     fprintf(file, "\n");
2960
2961     sii = setup.input[pnr];
2962     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2963       fprintf(file, "%s\n", getSetupLine(prefix, i));
2964   }
2965
2966   fclose(file);
2967   free(filename);
2968
2969   chmod(filename, SETUP_PERMS);
2970 }
2971
2972 void LoadLevelSetup_LastSeries()
2973 {
2974   char *filename;
2975   struct SetupFileList *level_setup_list = NULL;
2976
2977   /* always start with reliable default values */
2978   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2979
2980   /* ----------------------------------------------------------------------- */
2981   /* ~/.rocksndiamonds/levelsetup.conf                                       */
2982   /* ----------------------------------------------------------------------- */
2983
2984   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2985
2986   if ((level_setup_list = loadSetupFileList(filename)))
2987   {
2988     char *last_level_series =
2989       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2990
2991     leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2992     if (leveldir_current == NULL)
2993       leveldir_current = leveldir_first;
2994
2995     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2996
2997     freeSetupFileList(level_setup_list);
2998   }
2999   else
3000     Error(ERR_WARN, "using default setup values");
3001
3002   free(filename);
3003 }
3004
3005 void SaveLevelSetup_LastSeries()
3006 {
3007   char *filename;
3008   char *level_subdir = leveldir_current->filename;
3009   FILE *file;
3010
3011   /* ----------------------------------------------------------------------- */
3012   /* ~/.rocksndiamonds/levelsetup.conf                                       */
3013   /* ----------------------------------------------------------------------- */
3014
3015   InitUserDataDirectory();
3016
3017   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3018
3019   if (!(file = fopen(filename, MODE_WRITE)))
3020   {
3021     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3022     free(filename);
3023     return;
3024   }
3025
3026   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3027                                                  LEVELSETUP_COOKIE));
3028   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3029                                                level_subdir));
3030
3031   fclose(file);
3032   free(filename);
3033
3034   chmod(filename, SETUP_PERMS);
3035 }
3036
3037 static void checkSeriesInfo()
3038 {
3039   static char *level_directory = NULL;
3040   DIR *dir;
3041   struct dirent *dir_entry;
3042
3043   /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3044
3045   level_directory = getPath2((leveldir_current->user_defined ?
3046                               getUserLevelDir("") :
3047                               options.level_directory),
3048                              leveldir_current->fullpath);
3049
3050   if ((dir = opendir(level_directory)) == NULL)
3051   {
3052     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3053     return;
3054   }
3055
3056   while ((dir_entry = readdir(dir)) != NULL)    /* last directory entry */
3057   {
3058     if (strlen(dir_entry->d_name) > 4 &&
3059         dir_entry->d_name[3] == '.' &&
3060         strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3061     {
3062       char levelnum_str[4];
3063       int levelnum_value;
3064
3065       strncpy(levelnum_str, dir_entry->d_name, 3);
3066       levelnum_str[3] = '\0';
3067
3068       levelnum_value = atoi(levelnum_str);
3069
3070       if (levelnum_value < leveldir_current->first_level)
3071       {
3072         Error(ERR_WARN, "additional level %d found", levelnum_value);
3073         leveldir_current->first_level = levelnum_value;
3074       }
3075       else if (levelnum_value > leveldir_current->last_level)
3076       {
3077         Error(ERR_WARN, "additional level %d found", levelnum_value);
3078         leveldir_current->last_level = levelnum_value;
3079       }
3080     }
3081   }
3082
3083   closedir(dir);
3084 }
3085
3086 void LoadLevelSetup_SeriesInfo()
3087 {
3088   char *filename;
3089   struct SetupFileList *level_setup_list = NULL;
3090   char *level_subdir = leveldir_current->filename;
3091
3092   /* always start with reliable default values */
3093   level_nr = leveldir_current->first_level;
3094
3095   checkSeriesInfo(leveldir_current);
3096
3097   /* ----------------------------------------------------------------------- */
3098   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
3099   /* ----------------------------------------------------------------------- */
3100
3101   level_subdir = leveldir_current->filename;
3102
3103   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3104
3105   if ((level_setup_list = loadSetupFileList(filename)))
3106   {
3107     char *token_value;
3108
3109     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3110
3111     if (token_value)
3112     {
3113       level_nr = atoi(token_value);
3114
3115       if (level_nr < leveldir_current->first_level)
3116         level_nr = leveldir_current->first_level;
3117       if (level_nr > leveldir_current->last_level)
3118         level_nr = leveldir_current->last_level;
3119     }
3120
3121     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3122
3123     if (token_value)
3124     {
3125       int level_nr = atoi(token_value);
3126
3127       if (level_nr < leveldir_current->first_level)
3128         level_nr = leveldir_current->first_level;
3129       if (level_nr > leveldir_current->last_level + 1)
3130         level_nr = leveldir_current->last_level;
3131
3132       if (leveldir_current->user_defined)
3133         level_nr = leveldir_current->last_level;
3134
3135       leveldir_current->handicap_level = level_nr;
3136     }
3137
3138     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3139
3140     freeSetupFileList(level_setup_list);
3141   }
3142   else
3143     Error(ERR_WARN, "using default setup values");
3144
3145   free(filename);
3146 }
3147
3148 void SaveLevelSetup_SeriesInfo()
3149 {
3150   char *filename;
3151   char *level_subdir = leveldir_current->filename;
3152   char *level_nr_str = int2str(level_nr, 0);
3153   char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3154   FILE *file;
3155
3156   /* ----------------------------------------------------------------------- */
3157   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
3158   /* ----------------------------------------------------------------------- */
3159
3160   InitLevelSetupDirectory(level_subdir);
3161
3162   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3163
3164   if (!(file = fopen(filename, MODE_WRITE)))
3165   {
3166     Error(ERR_WARN, "cannot write setup file '%s'", filename);
3167     free(filename);
3168     return;
3169   }
3170
3171   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3172                                                  LEVELSETUP_COOKIE));
3173   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3174                                                level_nr_str));
3175   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3176                                                handicap_level_str));
3177
3178   fclose(file);
3179   free(filename);
3180
3181   chmod(filename, SETUP_PERMS);
3182 }
3183 /*  LocalWords:  Rocks'n
3184  */