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