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