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