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