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