rnd-19990928-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[leveldir_nr].user_defined ?
288                        getUserLevelDir("") :
289                        options.level_directory),
290                       leveldir[leveldir_nr].filename,
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[leveldir_nr].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[leveldir_nr].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[leveldir_nr].author, ANONYMOUS_NAME) != 0)
415   {
416     strncpy(level.author, leveldir[leveldir_nr].author, MAX_LEVEL_AUTHOR_LEN);
417     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
418   }
419   else
420   {
421     switch (LEVELCLASS(&leveldir[leveldir_nr]))
422     {
423       case LEVELCLASS_TUTORIAL:
424         strcpy(level.author, PROGRAM_AUTHOR_STRING);
425         break;
426
427       case LEVELCLASS_CONTRIBUTION:
428         strncpy(level.author, leveldir[leveldir_nr].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 void LoadLevel(int level_nr)
445 {
446   int i, x, y;
447   char *filename = getLevelFilename(level_nr);
448   char cookie[MAX_LINE_LEN];
449   char chunk[CHUNK_ID_LEN + 1];
450   boolean encoding_16bit = FALSE;       /* default: maximal 256 elements */
451   int file_version = FILE_VERSION_1_4;  /* last version of level files */
452   int chunk_length;
453   FILE *file;
454
455   /* always start with reliable default values */
456   setLevelInfoToDefaults();
457
458   if (!(file = fopen(filename, "r")))
459   {
460     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
461     return;
462   }
463
464   /* check file identifier */
465   fgets(cookie, MAX_LINE_LEN, file);
466   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
467     cookie[strlen(cookie) - 1] = '\0';
468
469   if (strcmp(cookie, LEVEL_COOKIE_10) == 0)     /* old 1.0 level format */
470     file_version = FILE_VERSION_1_0;
471   else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
472     file_version = FILE_VERSION_1_2;
473   else if (strcmp(cookie, LEVEL_COOKIE) != 0)   /* unknown level format */
474   {
475     Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
476     fclose(file);
477     return;
478   }
479
480   /* read chunk "HEAD" */
481   if (file_version >= FILE_VERSION_1_2)
482   {
483     getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
484     if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
485     {
486       Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
487       fclose(file);
488       return;
489     }
490   }
491
492   lev_fieldx = level.fieldx = fgetc(file);
493   lev_fieldy = level.fieldy = fgetc(file);
494
495   level.time        = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
496   level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
497
498   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
499     level.name[i] = fgetc(file);
500   level.name[MAX_LEVEL_NAME_LEN] = 0;
501
502   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
503     level.score[i] = fgetc(file);
504
505   level.num_yam_contents = STD_ELEMENT_CONTENTS;
506   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
507   {
508     for(y=0; y<3; y++)
509     {
510       for(x=0; x<3; x++)
511       {
512         if (i < STD_ELEMENT_CONTENTS)
513           level.yam_content[i][x][y] = fgetc(file);
514         else
515           level.yam_content[i][x][y] = EL_LEERRAUM;
516       }
517     }
518   }
519
520   level.amoeba_speed    = fgetc(file);
521   level.time_magic_wall = fgetc(file);
522   level.time_wheel      = fgetc(file);
523   level.amoeba_content  = fgetc(file);
524   level.double_speed    = (fgetc(file) == 1 ? TRUE : FALSE);
525   level.gravity         = (fgetc(file) == 1 ? TRUE : FALSE);
526
527   encoding_16bit        = (fgetc(file) == 1 ? TRUE : FALSE);
528
529   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* skip unused header bytes */
530     fgetc(file);
531
532   if (file_version >= FILE_VERSION_1_2)
533   {
534     getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
535
536     /* look for optional author chunk */
537     if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
538     {
539       for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
540         level.author[i] = fgetc(file);
541       level.author[MAX_LEVEL_NAME_LEN] = 0;
542
543       getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
544     }
545
546     /* look for optional content chunk */
547     if (strcmp(chunk, "CONT") == 0 &&
548         chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
549     {
550       fgetc(file);
551       level.num_yam_contents = fgetc(file);
552       fgetc(file);
553       fgetc(file);
554
555       if (level.num_yam_contents < 1 ||
556           level.num_yam_contents > MAX_ELEMENT_CONTENTS)
557       {
558 #if DEBUG
559         printf("WARNING: num_yam_contents == %d (corrected)\n",
560                level.num_yam_contents);
561 #endif
562         level.num_yam_contents = STD_ELEMENT_CONTENTS;
563       }
564
565       for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
566         for(y=0; y<3; y++)
567           for(x=0; x<3; x++)
568             level.yam_content[i][x][y] =
569               (encoding_16bit ?
570                getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
571                fgetc(file));
572
573       getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
574     }
575
576     /* next check body chunk identifier and chunk length */
577     if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
578     {
579       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
580       fclose(file);
581       return;
582     }
583   }
584
585   /* clear all other level fields (needed if resized in level editor later) */
586   for(x=0; x<MAX_LEV_FIELDX; x++)
587     for(y=0; y<MAX_LEV_FIELDY; y++)
588       Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
589
590   /* now read in the valid level fields from level file */
591   for(y=0; y<lev_fieldy; y++)
592     for(x=0; x<lev_fieldx; x++)
593       Feld[x][y] = Ur[x][y] =
594         (encoding_16bit ?
595          getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
596          fgetc(file));
597
598   fclose(file);
599
600   /* player was faster than monsters in pre-1.0 levels */
601   if (file_version == FILE_VERSION_1_0 &&
602       IS_LEVELCLASS_CONTRIBUTION(&leveldir[leveldir_nr]))
603   {
604     Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
605     Error(ERR_WARN, "using high speed movement for player");
606     level.double_speed = TRUE;
607   }
608
609   /* determine border element for this level */
610   SetBorderElement();
611 }
612
613 void SaveLevel(int level_nr)
614 {
615   int i, x, y;
616   char *filename = getLevelFilename(level_nr);
617   boolean encoding_16bit = FALSE;       /* default: maximal 256 elements */
618   char *oldest_possible_cookie;
619   FILE *file;
620
621   if (!(file = fopen(filename, "w")))
622   {
623     Error(ERR_WARN, "cannot save level file '%s'", filename);
624     return;
625   }
626
627   /* check yam content for 16-bit elements */
628   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
629     for(y=0; y<3; y++)
630       for(x=0; x<3; x++)
631         if (level.yam_content[i][x][y] > 255)
632           encoding_16bit = TRUE;
633
634   /* check level field for 16-bit elements */
635   for(y=0; y<lev_fieldy; y++) 
636     for(x=0; x<lev_fieldx; x++) 
637       if (Ur[x][y] > 255)
638         encoding_16bit = TRUE;
639
640   oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
641
642   fputs(oldest_possible_cookie, file);          /* file identifier */
643   fputc('\n', file);
644
645   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
646
647   fputc(level.fieldx, file);
648   fputc(level.fieldy, file);
649
650   putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
651   putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
652
653   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
654     fputc(level.name[i], file);
655   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
656     fputc(level.score[i], file);
657   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
658     for(y=0; y<3; y++)
659       for(x=0; x<3; x++)
660         fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
661   fputc(level.amoeba_speed, file);
662   fputc(level.time_magic_wall, file);
663   fputc(level.time_wheel, file);
664   fputc(level.amoeba_content, file);
665   fputc((level.double_speed ? 1 : 0), file);
666   fputc((level.gravity ? 1 : 0), file);
667
668   fputc((encoding_16bit ? 1 : 0), file);
669
670   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* set unused header bytes to zero */
671     fputc(0, file);
672
673   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
674
675   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
676     fputc(level.author[i], file);
677
678   putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
679                BYTE_ORDER_BIG_ENDIAN);
680
681   fputc(EL_MAMPFER, file);
682   fputc(level.num_yam_contents, file);
683   fputc(0, file);
684   fputc(0, file);
685
686   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
687     for(y=0; y<3; y++)
688       for(x=0; x<3; x++)
689         if (encoding_16bit)
690           putFile16BitInteger(file, level.yam_content[i][x][y],
691                               BYTE_ORDER_BIG_ENDIAN);
692         else
693           fputc(level.yam_content[i][x][y], file);
694
695   putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
696
697   for(y=0; y<lev_fieldy; y++) 
698     for(x=0; x<lev_fieldx; x++) 
699       if (encoding_16bit)
700         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
701       else
702         fputc(Ur[x][y], file);
703
704   fclose(file);
705
706   chmod(filename, LEVEL_PERMS);
707 }
708
709 void LoadTape(int level_nr)
710 {
711   int i, j;
712   char *filename = getTapeFilename(level_nr);
713   char cookie[MAX_LINE_LEN];
714   char chunk[CHUNK_ID_LEN + 1];
715   FILE *file;
716   int num_participating_players;
717   int file_version = FILE_VERSION_1_2;  /* last version of tape files */
718   int chunk_length;
719
720   /* always start with reliable default values (empty tape) */
721   TapeErase();
722
723   /* default values (also for pre-1.2 tapes) with only the first player */
724   tape.player_participates[0] = TRUE;
725   for(i=1; i<MAX_PLAYERS; i++)
726     tape.player_participates[i] = FALSE;
727
728   /* at least one (default: the first) player participates in every tape */
729   num_participating_players = 1;
730
731   if (!(file = fopen(filename, "r")))
732     return;
733
734   /* check file identifier */
735   fgets(cookie, MAX_LINE_LEN, file);
736   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
737     cookie[strlen(cookie) - 1] = '\0';
738
739   if (strcmp(cookie, TAPE_COOKIE_10) == 0)      /* old 1.0 tape format */
740     file_version = FILE_VERSION_1_0;
741   else if (strcmp(cookie, TAPE_COOKIE) != 0)    /* unknown tape format */
742   {
743     Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
744     fclose(file);
745     return;
746   }
747
748   /* read chunk "HEAD" */
749   if (file_version >= FILE_VERSION_1_2)
750   {
751     getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
752     if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
753     {
754       Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
755       fclose(file);
756       return;
757     }
758   }
759
760   tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
761   tape.date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
762   tape.length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
763
764   /* read header fields that are new since version 1.2 */
765   if (file_version >= FILE_VERSION_1_2)
766   {
767     byte store_participating_players = fgetc(file);
768
769     for(i=0; i<TAPE_HEADER_UNUSED; i++)         /* skip unused header bytes */
770       fgetc(file);
771
772     /* since version 1.2, tapes store which players participate in the tape */
773     num_participating_players = 0;
774     for(i=0; i<MAX_PLAYERS; i++)
775     {
776       tape.player_participates[i] = FALSE;
777
778       if (store_participating_players & (1 << i))
779       {
780         tape.player_participates[i] = TRUE;
781         num_participating_players++;
782       }
783     }
784   }
785
786   tape.level_nr = level_nr;
787   tape.counter = 0;
788   tape.changed = FALSE;
789
790   tape.recording = FALSE;
791   tape.playing = FALSE;
792   tape.pausing = FALSE;
793
794   /* read chunk "BODY" */
795   if (file_version >= FILE_VERSION_1_2)
796   {
797     getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
798     if (strcmp(chunk, "BODY") ||
799         chunk_length != (num_participating_players + 1) * tape.length)
800     {
801       Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
802       fclose(file);
803       return;
804     }
805   }
806
807   for(i=0; i<tape.length; i++)
808   {
809     if (i >= MAX_TAPELEN)
810       break;
811
812     for(j=0; j<MAX_PLAYERS; j++)
813     {
814       tape.pos[i].action[j] = MV_NO_MOVING;
815
816       if (tape.player_participates[j])
817         tape.pos[i].action[j] = fgetc(file);
818     }
819
820     tape.pos[i].delay = fgetc(file);
821
822     if (file_version == FILE_VERSION_1_0)
823     {
824       /* eliminate possible diagonal moves in old tapes */
825       /* this is only for backward compatibility */
826
827       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
828       byte action = tape.pos[i].action[0];
829       int k, num_moves = 0;
830
831       for (k=0; k<4; k++)
832       {
833         if (action & joy_dir[k])
834         {
835           tape.pos[i + num_moves].action[0] = joy_dir[k];
836           if (num_moves > 0)
837             tape.pos[i + num_moves].delay = 0;
838           num_moves++;
839         }
840       }
841
842       if (num_moves > 1)
843       {
844         num_moves--;
845         i += num_moves;
846         tape.length += num_moves;
847       }
848     }
849
850     if (feof(file))
851       break;
852   }
853
854   fclose(file);
855
856   if (i != tape.length)
857     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
858
859   tape.length_seconds = GetTapeLength();
860 }
861
862 void SaveTape(int level_nr)
863 {
864   int i;
865   char *filename = getTapeFilename(level_nr);
866   FILE *file;
867   boolean new_tape = TRUE;
868   byte store_participating_players;
869   int num_participating_players;
870
871   InitTapeDirectory(leveldir[leveldir_nr].filename);
872
873   /* if a tape still exists, ask to overwrite it */
874   if (access(filename, F_OK) == 0)
875   {
876     new_tape = FALSE;
877     if (!Request("Replace old tape ?", REQ_ASK))
878       return;
879   }
880
881   /* count number of players and set corresponding bits for compact storage */
882   store_participating_players = 0;
883   num_participating_players = 0;
884   for(i=0; i<MAX_PLAYERS; i++)
885   {
886     if (tape.player_participates[i])
887     {
888       num_participating_players++;
889       store_participating_players |= (1 << i);
890     }
891   }
892
893   if (!(file = fopen(filename, "w")))
894   {
895     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
896     return;
897   }
898
899   fputs(TAPE_COOKIE, file);             /* file identifier */
900   fputc('\n', file);
901
902   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
903
904   putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
905   putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
906   putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
907
908   fputc(store_participating_players, file);
909
910   for(i=0; i<TAPE_HEADER_UNUSED; i++)   /* set unused header bytes to zero */
911     fputc(0, file);
912
913   putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
914                BYTE_ORDER_BIG_ENDIAN);
915
916   for(i=0; i<tape.length; i++)
917   {
918     int j;
919
920     for(j=0; j<MAX_PLAYERS; j++)
921       if (tape.player_participates[j])
922         fputc(tape.pos[i].action[j], file);
923
924     fputc(tape.pos[i].delay, file);
925   }
926
927   fclose(file);
928
929   chmod(filename, TAPE_PERMS);
930
931   tape.changed = FALSE;
932
933   if (new_tape)
934     Request("tape saved !", REQ_CONFIRM);
935 }
936
937 void LoadScore(int level_nr)
938 {
939   int i;
940   char *filename = getScoreFilename(level_nr);
941   char cookie[MAX_LINE_LEN];
942   char line[MAX_LINE_LEN];
943   char *line_ptr;
944   FILE *file;
945
946   /* always start with reliable default values */
947   for(i=0; i<MAX_SCORE_ENTRIES; i++)
948   {
949     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
950     highscore[i].Score = 0;
951   }
952
953   if (!(file = fopen(filename, "r")))
954     return;
955
956   /* check file identifier */
957   fgets(cookie, MAX_LINE_LEN, file);
958   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
959     cookie[strlen(cookie) - 1] = '\0';
960
961   if (strcmp(cookie, SCORE_COOKIE) != 0)
962   {
963     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
964     fclose(file);
965     return;
966   }
967
968   for(i=0; i<MAX_SCORE_ENTRIES; i++)
969   {
970     fscanf(file, "%d", &highscore[i].Score);
971     fgets(line, MAX_LINE_LEN, file);
972
973     if (line[strlen(line) - 1] == '\n')
974       line[strlen(line) - 1] = '\0';
975
976     for (line_ptr = line; *line_ptr; line_ptr++)
977     {
978       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
979       {
980         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
981         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
982         break;
983       }
984     }
985   }
986
987   fclose(file);
988 }
989
990 void SaveScore(int level_nr)
991 {
992   int i;
993   char *filename = getScoreFilename(level_nr);
994   FILE *file;
995
996   InitScoreDirectory(leveldir[leveldir_nr].filename);
997
998   if (!(file = fopen(filename, "w")))
999   {
1000     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1001     return;
1002   }
1003
1004   fprintf(file, "%s\n\n", SCORE_COOKIE);
1005
1006   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1007     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1008
1009   fclose(file);
1010
1011   chmod(filename, SCORE_PERMS);
1012 }
1013
1014 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
1015 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
1016 #define TOKEN_STR_LAST_PLAYED_LEVEL     "last_played_level"
1017 #define TOKEN_STR_HANDICAP_LEVEL        "handicap_level"
1018 #define TOKEN_STR_PLAYER_PREFIX         "player_"
1019
1020 #define TOKEN_VALUE_POSITION            30
1021
1022 /* global setup */
1023 #define SETUP_TOKEN_PLAYER_NAME         0
1024 #define SETUP_TOKEN_SOUND               1
1025 #define SETUP_TOKEN_SOUND_LOOPS         2
1026 #define SETUP_TOKEN_SOUND_MUSIC         3
1027 #define SETUP_TOKEN_SOUND_SIMPLE        4
1028
1029 #if 0
1030 #define SETUP_TOKEN_TOONS               5
1031 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
1032 #endif
1033
1034 #define SETUP_TOKEN_SCROLL_DELAY        5
1035 #define SETUP_TOKEN_SOFT_SCROLLING      6
1036 #define SETUP_TOKEN_FADING              7
1037 #define SETUP_TOKEN_AUTORECORD          8
1038 #define SETUP_TOKEN_QUICK_DOORS         9
1039 #define SETUP_TOKEN_TEAM_MODE           10
1040 #define SETUP_TOKEN_HANDICAP            11
1041 #define SETUP_TOKEN_TIME_LIMIT          12
1042
1043 /* player setup */
1044 #define SETUP_TOKEN_USE_JOYSTICK        13
1045 #define SETUP_TOKEN_JOY_DEVICE_NAME     14
1046 #define SETUP_TOKEN_JOY_XLEFT           15
1047 #define SETUP_TOKEN_JOY_XMIDDLE         16
1048 #define SETUP_TOKEN_JOY_XRIGHT          17
1049 #define SETUP_TOKEN_JOY_YUPPER          18
1050 #define SETUP_TOKEN_JOY_YMIDDLE         19
1051 #define SETUP_TOKEN_JOY_YLOWER          20
1052 #define SETUP_TOKEN_JOY_SNAP            21
1053 #define SETUP_TOKEN_JOY_BOMB            22
1054 #define SETUP_TOKEN_KEY_LEFT            23
1055 #define SETUP_TOKEN_KEY_RIGHT           24
1056 #define SETUP_TOKEN_KEY_UP              25
1057 #define SETUP_TOKEN_KEY_DOWN            26
1058 #define SETUP_TOKEN_KEY_SNAP            27
1059 #define SETUP_TOKEN_KEY_BOMB            28
1060
1061 /* level directory info */
1062 #define LEVELINFO_TOKEN_NAME            29
1063 #define LEVELINFO_TOKEN_NAME_SHORT      30
1064 #define LEVELINFO_TOKEN_NAME_SORTING    31
1065 #define LEVELINFO_TOKEN_AUTHOR          32
1066 #define LEVELINFO_TOKEN_IMPORTED_FROM   33
1067 #define LEVELINFO_TOKEN_LEVELS          34
1068 #define LEVELINFO_TOKEN_FIRST_LEVEL     35
1069 #define LEVELINFO_TOKEN_SORT_PRIORITY   36
1070 #define LEVELINFO_TOKEN_READONLY        37
1071
1072 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
1073 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_TIME_LIMIT
1074
1075 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
1076 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
1077
1078 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
1079 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
1080
1081 #define TYPE_BOOLEAN                    1
1082 #define TYPE_SWITCH                     2
1083 #define TYPE_KEYSYM                     3
1084 #define TYPE_INTEGER                    4
1085 #define TYPE_STRING                     5
1086
1087 static struct SetupInfo si;
1088 static struct SetupInputInfo sii;
1089 static struct LevelDirInfo ldi;
1090 static struct
1091 {
1092   int type;
1093   void *value;
1094   char *text;
1095 } token_info[] =
1096 {
1097   /* global setup */
1098   { TYPE_STRING,  &si.player_name,      "player_name"                   },
1099   { TYPE_SWITCH,  &si.sound,            "sound"                         },
1100   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
1101   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
1102   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
1103
1104 #if 0
1105   { TYPE_SWITCH,  &si.toons,            "toons"                         },
1106   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
1107 #endif
1108
1109   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
1110   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
1111   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
1112   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
1113   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
1114   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
1115   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
1116   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
1117
1118   /* player setup */
1119   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1120   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1121   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1122   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1123   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1124   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1125   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1126   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1127   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1128   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1129   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
1130   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
1131   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
1132   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
1133   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
1134   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               },
1135
1136   /* level directory info */
1137   { TYPE_STRING,  &ldi.name,            "name"                          },
1138   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
1139   { TYPE_STRING,  &ldi.name_sorting,    "name_sorting"                  },
1140   { TYPE_STRING,  &ldi.author,          "author"                        },
1141   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
1142   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
1143   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
1144   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
1145   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
1146 };
1147
1148 static char *string_tolower(char *s)
1149 {
1150   static char s_lower[100];
1151   int i;
1152
1153   if (strlen(s) >= 100)
1154     return s;
1155
1156   strcpy(s_lower, s);
1157
1158   for (i=0; i<strlen(s_lower); i++)
1159     s_lower[i] = tolower(s_lower[i]);
1160
1161   return s_lower;
1162 }
1163
1164 static int get_string_integer_value(char *s)
1165 {
1166   static char *number_text[][3] =
1167   {
1168     { "0", "zero", "null", },
1169     { "1", "one", "first" },
1170     { "2", "two", "second" },
1171     { "3", "three", "third" },
1172     { "4", "four", "fourth" },
1173     { "5", "five", "fifth" },
1174     { "6", "six", "sixth" },
1175     { "7", "seven", "seventh" },
1176     { "8", "eight", "eighth" },
1177     { "9", "nine", "ninth" },
1178     { "10", "ten", "tenth" },
1179     { "11", "eleven", "eleventh" },
1180     { "12", "twelve", "twelfth" },
1181   };
1182
1183   int i, j;
1184
1185   for (i=0; i<13; i++)
1186     for (j=0; j<3; j++)
1187       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1188         return i;
1189
1190   return atoi(s);
1191 }
1192
1193 static boolean get_string_boolean_value(char *s)
1194 {
1195   if (strcmp(string_tolower(s), "true") == 0 ||
1196       strcmp(string_tolower(s), "yes") == 0 ||
1197       strcmp(string_tolower(s), "on") == 0 ||
1198       get_string_integer_value(s) == 1)
1199     return TRUE;
1200   else
1201     return FALSE;
1202 }
1203
1204 static char *getFormattedSetupEntry(char *token, char *value)
1205 {
1206   int i;
1207   static char entry[MAX_LINE_LEN];
1208
1209   sprintf(entry, "%s:", token);
1210   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1211     entry[i] = ' ';
1212   entry[i] = '\0';
1213
1214   strcat(entry, value);
1215
1216   return entry;
1217 }
1218
1219 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1220 {
1221   if (!setup_file_list)
1222     return;
1223
1224   if (setup_file_list->token)
1225     free(setup_file_list->token);
1226   if (setup_file_list->value)
1227     free(setup_file_list->value);
1228   if (setup_file_list->next)
1229     freeSetupFileList(setup_file_list->next);
1230   free(setup_file_list);
1231 }
1232
1233 static struct SetupFileList *newSetupFileList(char *token, char *value)
1234 {
1235   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1236
1237   new->token = checked_malloc(strlen(token) + 1);
1238   strcpy(new->token, token);
1239
1240   new->value = checked_malloc(strlen(value) + 1);
1241   strcpy(new->value, value);
1242
1243   new->next = NULL;
1244
1245   return new;
1246 }
1247
1248 static char *getTokenValue(struct SetupFileList *setup_file_list,
1249                            char *token)
1250 {
1251   if (!setup_file_list)
1252     return NULL;
1253
1254   if (strcmp(setup_file_list->token, token) == 0)
1255     return setup_file_list->value;
1256   else
1257     return getTokenValue(setup_file_list->next, token);
1258 }
1259
1260 static void setTokenValue(struct SetupFileList *setup_file_list,
1261                           char *token, char *value)
1262 {
1263   if (!setup_file_list)
1264     return;
1265
1266   if (strcmp(setup_file_list->token, token) == 0)
1267   {
1268     free(setup_file_list->value);
1269     setup_file_list->value = checked_malloc(strlen(value) + 1);
1270     strcpy(setup_file_list->value, value);
1271   }
1272   else if (setup_file_list->next == NULL)
1273     setup_file_list->next = newSetupFileList(token, value);
1274   else
1275     setTokenValue(setup_file_list->next, token, value);
1276 }
1277
1278 #ifdef DEBUG
1279 static void printSetupFileList(struct SetupFileList *setup_file_list)
1280 {
1281   if (!setup_file_list)
1282     return;
1283
1284   printf("token: '%s'\n", setup_file_list->token);
1285   printf("value: '%s'\n", setup_file_list->value);
1286
1287   printSetupFileList(setup_file_list->next);
1288 }
1289 #endif
1290
1291 static struct SetupFileList *loadSetupFileList(char *filename)
1292 {
1293   int line_len;
1294   char line[MAX_LINE_LEN];
1295   char *token, *value, *line_ptr;
1296   struct SetupFileList *setup_file_list = newSetupFileList("", "");
1297   struct SetupFileList *first_valid_list_entry;
1298
1299   FILE *file;
1300
1301   if (!(file = fopen(filename, "r")))
1302   {
1303     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1304     return NULL;
1305   }
1306
1307   while(!feof(file))
1308   {
1309     /* read next line of input file */
1310     if (!fgets(line, MAX_LINE_LEN, file))
1311       break;
1312
1313     /* cut trailing comment or whitespace from input line */
1314     for (line_ptr = line; *line_ptr; line_ptr++)
1315     {
1316       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1317       {
1318         *line_ptr = '\0';
1319         break;
1320       }
1321     }
1322
1323     /* cut trailing whitespaces from input line */
1324     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1325       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1326         *line_ptr = '\0';
1327
1328     /* ignore empty lines */
1329     if (*line == '\0')
1330       continue;
1331
1332     line_len = strlen(line);
1333
1334     /* cut leading whitespaces from token */
1335     for (token = line; *token; token++)
1336       if (*token != ' ' && *token != '\t')
1337         break;
1338
1339     /* find end of token */
1340     for (line_ptr = token; *line_ptr; line_ptr++)
1341     {
1342       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1343       {
1344         *line_ptr = '\0';
1345         break;
1346       }
1347     }
1348
1349     if (line_ptr < line + line_len)
1350       value = line_ptr + 1;
1351     else
1352       value = "\0";
1353
1354     /* cut leading whitespaces from value */
1355     for (; *value; value++)
1356       if (*value != ' ' && *value != '\t')
1357         break;
1358
1359     if (*token && *value)
1360       setTokenValue(setup_file_list, token, value);
1361   }
1362
1363   fclose(file);
1364
1365   first_valid_list_entry = setup_file_list->next;
1366
1367   /* free empty list header */
1368   setup_file_list->next = NULL;
1369   freeSetupFileList(setup_file_list);
1370
1371   if (first_valid_list_entry == NULL)
1372     Error(ERR_WARN, "configuration file '%s' is empty", filename);
1373
1374   return first_valid_list_entry;
1375 }
1376
1377 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1378                                          char *identifier)
1379 {
1380   if (!setup_file_list)
1381     return;
1382
1383   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1384   {
1385     if (strcmp(setup_file_list->value, identifier) != 0)
1386     {
1387       Error(ERR_WARN, "configuration file has wrong version");
1388       return;
1389     }
1390     else
1391       return;
1392   }
1393
1394   if (setup_file_list->next)
1395     checkSetupFileListIdentifier(setup_file_list->next, identifier);
1396   else
1397   {
1398     Error(ERR_WARN, "configuration file has no version information");
1399     return;
1400   }
1401 }
1402
1403 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1404 {
1405   ldi->filename = NULL;
1406   ldi->name = getStringCopy(ANONYMOUS_NAME);
1407   ldi->name_short = NULL;
1408   ldi->name_sorting = NULL;
1409   ldi->author = getStringCopy(ANONYMOUS_NAME);
1410   ldi->imported_from = NULL;
1411   ldi->levels = 0;
1412   ldi->first_level = 0;
1413   ldi->last_level = 0;
1414   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
1415   ldi->readonly = TRUE;
1416   ldi->user_defined = FALSE;
1417   ldi->color = 0;
1418   ldi->class_desc = NULL;
1419   ldi->handicap_level = 0;
1420 }
1421
1422 static void setSetupInfoToDefaults(struct SetupInfo *si)
1423 {
1424   int i;
1425
1426   si->player_name = getStringCopy(getLoginName());
1427
1428   si->sound = TRUE;
1429   si->sound_loops = TRUE;
1430   si->sound_music = TRUE;
1431   si->sound_simple = TRUE;
1432   si->toons = TRUE;
1433   si->double_buffering = TRUE;
1434   si->direct_draw = !si->double_buffering;
1435   si->scroll_delay = TRUE;
1436   si->soft_scrolling = TRUE;
1437   si->fading = FALSE;
1438   si->autorecord = TRUE;
1439   si->quick_doors = FALSE;
1440   si->team_mode = FALSE;
1441   si->handicap = TRUE;
1442   si->time_limit = TRUE;
1443
1444   for (i=0; i<MAX_PLAYERS; i++)
1445   {
1446     si->input[i].use_joystick = FALSE;
1447     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1448     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1449     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1450     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1451     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1452     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1453     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1454     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1455     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1456     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
1457     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1458     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
1459     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
1460     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
1461     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
1462   }
1463 }
1464
1465 static void setSetupInfo(int token_nr, char *token_value)
1466 {
1467   int token_type = token_info[token_nr].type;
1468   void *setup_value = token_info[token_nr].value;
1469
1470   if (token_value == NULL)
1471     return;
1472
1473   /* set setup field to corresponding token value */
1474   switch (token_type)
1475   {
1476     case TYPE_BOOLEAN:
1477     case TYPE_SWITCH:
1478       *(boolean *)setup_value = get_string_boolean_value(token_value);
1479       break;
1480
1481     case TYPE_KEYSYM:
1482       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
1483       break;
1484
1485     case TYPE_INTEGER:
1486       *(int *)setup_value = get_string_integer_value(token_value);
1487       break;
1488
1489     case TYPE_STRING:
1490       if (*(char **)setup_value != NULL)
1491         free(*(char **)setup_value);
1492       *(char **)setup_value = getStringCopy(token_value);
1493       break;
1494
1495     default:
1496       break;
1497   }
1498 }
1499
1500 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1501 {
1502   int i, pnr;
1503
1504   if (!setup_file_list)
1505     return;
1506
1507   /* handle global setup values */
1508   si = setup;
1509   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1510     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1511   setup = si;
1512
1513   /* handle player specific setup values */
1514   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1515   {
1516     char prefix[30];
1517
1518     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1519
1520     sii = setup.input[pnr];
1521     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1522     {
1523       char full_token[100];
1524
1525       sprintf(full_token, "%s%s", prefix, token_info[i].text);
1526       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1527     }
1528     setup.input[pnr] = sii;
1529   }
1530 }
1531
1532 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
1533 {
1534   int i;
1535
1536   if (!level_series_name)
1537     return 0;
1538
1539   for (i=0; i<num_leveldirs; i++)
1540     if (strcmp(level_series_name, leveldir[i].filename) == 0)
1541       return i;
1542
1543   return 0;
1544 }
1545
1546 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1547 {
1548   const struct LevelDirInfo *entry1 = object1;
1549   const struct LevelDirInfo *entry2 = object2;
1550   int compare_result;
1551
1552   if (entry1->sort_priority == entry2->sort_priority)
1553   {
1554     char *name1 = getStringToLower(entry1->name_sorting);
1555     char *name2 = getStringToLower(entry2->name_sorting);
1556
1557     compare_result = strcmp(name1, name2);
1558
1559     free(name1);
1560     free(name2);
1561   }
1562   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1563     compare_result = entry1->sort_priority - entry2->sort_priority;
1564   else
1565     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1566
1567   return compare_result;
1568 }
1569
1570 static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
1571 {
1572   DIR *dir;
1573   struct stat file_status;
1574   char *directory = NULL;
1575   char *filename = NULL;
1576   struct SetupFileList *setup_file_list = NULL;
1577   struct dirent *dir_entry;
1578   int i, current_entry = start_entry;
1579
1580   if ((dir = opendir(level_directory)) == NULL)
1581   {
1582     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1583     return current_entry;
1584   }
1585
1586   while (current_entry < MAX_LEVDIR_ENTRIES)
1587   {
1588     if ((dir_entry = readdir(dir)) == NULL)     /* last directory entry */
1589       break;
1590
1591     /* skip entries for current and parent directory */
1592     if (strcmp(dir_entry->d_name, ".")  == 0 ||
1593         strcmp(dir_entry->d_name, "..") == 0)
1594       continue;
1595
1596     /* find out if directory entry is itself a directory */
1597     directory = getPath2(level_directory, dir_entry->d_name);
1598     if (stat(directory, &file_status) != 0 ||           /* cannot stat file */
1599         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
1600     {
1601       free(directory);
1602       continue;
1603     }
1604
1605     filename = getPath2(directory, LEVELINFO_FILENAME);
1606     setup_file_list = loadSetupFileList(filename);
1607
1608     if (setup_file_list)
1609     {
1610 #if 0
1611       DrawInitText(dir_entry->d_name, 150, FC_YELLOW);
1612 #endif
1613
1614       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1615       setLevelDirInfoToDefaults(&leveldir[current_entry]);
1616
1617       ldi = leveldir[current_entry];
1618       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1619         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1620       leveldir[current_entry] = ldi;
1621
1622 #if 1
1623       DrawInitText(leveldir[current_entry].name, 150, FC_YELLOW);
1624 #endif
1625
1626       if (leveldir[current_entry].name_short == NULL)
1627         leveldir[current_entry].name_short =
1628           getStringCopy(leveldir[current_entry].name);
1629
1630       if (leveldir[current_entry].name_sorting == NULL)
1631         leveldir[current_entry].name_sorting =
1632           getStringCopy(leveldir[current_entry].name_short);
1633
1634       leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
1635       leveldir[current_entry].last_level =
1636         leveldir[current_entry].first_level +
1637         leveldir[current_entry].levels - 1;
1638       leveldir[current_entry].user_defined =
1639         (level_directory == options.level_directory ? FALSE : TRUE);
1640       leveldir[current_entry].color = LEVELCOLOR(&leveldir[current_entry]);
1641       leveldir[current_entry].class_desc =
1642         getLevelClassDescription(&leveldir[current_entry]);
1643 #if 0
1644       leveldir[current_entry].handicap_level =
1645         leveldir[current_entry].first_level;    /* default value */
1646 #else
1647       leveldir[current_entry].handicap_level =
1648         (leveldir[current_entry].user_defined ?
1649          leveldir[current_entry].last_level :
1650          leveldir[current_entry].first_level);
1651 #endif
1652
1653       freeSetupFileList(setup_file_list);
1654       current_entry++;
1655     }
1656     else
1657       Error(ERR_WARN, "ignoring level directory '%s'", directory);
1658
1659     free(directory);
1660     free(filename);
1661   }
1662
1663   closedir(dir);
1664
1665   if (current_entry == MAX_LEVDIR_ENTRIES)
1666   {
1667     Error(ERR_WARN, "maximum of %d level directories reached", current_entry);
1668     Error(ERR_WARN, "remaining level directories ignored in directory '%s'",
1669           level_directory);
1670   }
1671   else if (current_entry == start_entry)
1672     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1673           level_directory);
1674
1675   return current_entry;
1676 }
1677
1678 void LoadLevelInfo()
1679 {
1680   InitUserLevelDirectory(getLoginName());
1681
1682   num_leveldirs = 0;
1683   leveldir_nr = 0;
1684
1685   DrawInitText("Loading level series:", 120, FC_GREEN);
1686
1687   num_leveldirs = LoadLevelInfoFromLevelDir(options.level_directory,
1688                                             num_leveldirs);
1689   num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
1690                                             num_leveldirs);
1691
1692   if (num_leveldirs == 0)
1693     Error(ERR_EXIT, "cannot find any valid level series in any directory");
1694
1695   if (num_leveldirs > 1)
1696     qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
1697           compareLevelDirInfoEntries);
1698 }
1699
1700 static void SaveUserLevelInfo()
1701 {
1702   char *filename;
1703   FILE *file;
1704   int i;
1705
1706   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1707
1708   if (!(file = fopen(filename, "w")))
1709   {
1710     Error(ERR_WARN, "cannot write level info file '%s'", filename);
1711     free(filename);
1712     return;
1713   }
1714
1715   /* always start with reliable default values */
1716   setLevelDirInfoToDefaults(&ldi);
1717
1718   ldi.name = getLoginName();
1719   ldi.author = getRealName();
1720   ldi.levels = 100;
1721   ldi.first_level = 1;
1722   ldi.sort_priority = LEVELCLASS_USER_START;
1723   ldi.readonly = FALSE;
1724
1725   fprintf(file, "%s\n\n",
1726           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1727
1728   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1729     if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1730         i != LEVELINFO_TOKEN_NAME_SORTING &&
1731         i != LEVELINFO_TOKEN_IMPORTED_FROM)
1732       fprintf(file, "%s\n", getSetupLine("", i));
1733
1734   fclose(file);
1735   free(filename);
1736
1737   chmod(filename, SETUP_PERMS);
1738 }
1739
1740 void LoadSetup()
1741 {
1742   char *filename;
1743   struct SetupFileList *setup_file_list = NULL;
1744
1745   /* always start with reliable default values */
1746   setSetupInfoToDefaults(&setup);
1747
1748   filename = getPath2(getSetupDir(), SETUP_FILENAME);
1749
1750   setup_file_list = loadSetupFileList(filename);
1751
1752   if (setup_file_list)
1753   {
1754     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1755     decodeSetupFileList(setup_file_list);
1756
1757     setup.direct_draw = !setup.double_buffering;
1758
1759     freeSetupFileList(setup_file_list);
1760
1761     /* needed to work around problems with fixed length strings */
1762     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1763       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1764     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1765     {
1766       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1767
1768       strcpy(new_name, setup.player_name);
1769       free(setup.player_name);
1770       setup.player_name = new_name;
1771     }
1772   }
1773   else
1774     Error(ERR_WARN, "using default setup values");
1775
1776   free(filename);
1777 }
1778
1779 static char *getSetupLine(char *prefix, int token_nr)
1780 {
1781   int i;
1782   static char entry[MAX_LINE_LEN];
1783   int token_type = token_info[token_nr].type;
1784   void *setup_value = token_info[token_nr].value;
1785   char *token_text = token_info[token_nr].text;
1786
1787   /* start with the prefix, token and some spaces to format output line */
1788   sprintf(entry, "%s%s:", prefix, token_text);
1789   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1790     strcat(entry, " ");
1791
1792   /* continue with the token's value (which can have different types) */
1793   switch (token_type)
1794   {
1795     case TYPE_BOOLEAN:
1796       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1797       break;
1798
1799     case TYPE_SWITCH:
1800       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1801       break;
1802
1803     case TYPE_KEYSYM:
1804       {
1805         KeySym keysym = *(KeySym *)setup_value;
1806         char *keyname = getKeyNameFromKeySym(keysym);
1807
1808         strcat(entry, getX11KeyNameFromKeySym(keysym));
1809         for (i=strlen(entry); i<50; i++)
1810           strcat(entry, " ");
1811
1812         /* add comment, if useful */
1813         if (strcmp(keyname, "(undefined)") != 0 &&
1814             strcmp(keyname, "(unknown)") != 0)
1815         {
1816           strcat(entry, "# ");
1817           strcat(entry, keyname);
1818         }
1819       }
1820       break;
1821
1822     case TYPE_INTEGER:
1823       {
1824         char buffer[MAX_LINE_LEN];
1825
1826         sprintf(buffer, "%d", *(int *)setup_value);
1827         strcat(entry, buffer);
1828       }
1829       break;
1830
1831     case TYPE_STRING:
1832       strcat(entry, *(char **)setup_value);
1833       break;
1834
1835     default:
1836       break;
1837   }
1838
1839   return entry;
1840 }
1841
1842 void SaveSetup()
1843 {
1844   int i, pnr;
1845   char *filename;
1846   FILE *file;
1847
1848   InitUserDataDirectory();
1849
1850   filename = getPath2(getSetupDir(), SETUP_FILENAME);
1851
1852   if (!(file = fopen(filename, "w")))
1853   {
1854     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1855     free(filename);
1856     return;
1857   }
1858
1859   fprintf(file, "%s\n",
1860           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1861   fprintf(file, "\n");
1862
1863   /* handle global setup values */
1864   si = setup;
1865   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1866   {
1867     fprintf(file, "%s\n", getSetupLine("", i));
1868
1869     /* just to make things nicer :) */
1870     if (i == SETUP_TOKEN_PLAYER_NAME)
1871       fprintf(file, "\n");
1872   }
1873
1874   /* handle player specific setup values */
1875   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1876   {
1877     char prefix[30];
1878
1879     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1880     fprintf(file, "\n");
1881
1882     sii = setup.input[pnr];
1883     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1884       fprintf(file, "%s\n", getSetupLine(prefix, i));
1885   }
1886
1887   fclose(file);
1888   free(filename);
1889
1890   chmod(filename, SETUP_PERMS);
1891 }
1892
1893 void LoadLevelSetup_LastSeries()
1894 {
1895   char *filename;
1896   struct SetupFileList *level_setup_list = NULL;
1897
1898   /* always start with reliable default values */
1899   leveldir_nr = 0;
1900
1901   /* ----------------------------------------------------------------------- */
1902   /* ~/.rocksndiamonds/levelsetup.conf                                       */
1903   /* ----------------------------------------------------------------------- */
1904
1905   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1906
1907   if ((level_setup_list = loadSetupFileList(filename)))
1908   {
1909     char *last_level_series =
1910       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1911
1912     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1913
1914     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1915
1916     freeSetupFileList(level_setup_list);
1917   }
1918   else
1919     Error(ERR_WARN, "using default setup values");
1920
1921   free(filename);
1922 }
1923
1924 void SaveLevelSetup_LastSeries()
1925 {
1926   char *filename;
1927   char *level_subdir = leveldir[leveldir_nr].filename;
1928   FILE *file;
1929
1930   /* ----------------------------------------------------------------------- */
1931   /* ~/.rocksndiamonds/levelsetup.conf                                       */
1932   /* ----------------------------------------------------------------------- */
1933
1934   InitUserDataDirectory();
1935
1936   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1937
1938   if (!(file = fopen(filename, "w")))
1939   {
1940     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1941     free(filename);
1942     return;
1943   }
1944
1945   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1946                                                  LEVELSETUP_COOKIE));
1947   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1948                                                level_subdir));
1949
1950   fclose(file);
1951   free(filename);
1952
1953   chmod(filename, SETUP_PERMS);
1954 }
1955
1956 static void checkSeriesInfo(int leveldir_nr)
1957 {
1958   static char *level_directory = NULL;
1959   DIR *dir;
1960   struct dirent *dir_entry;
1961
1962   /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1963
1964   level_directory = getPath2((leveldir[leveldir_nr].user_defined ?
1965                               getUserLevelDir("") :
1966                               options.level_directory),
1967                              leveldir[leveldir_nr].filename);
1968
1969   if ((dir = opendir(level_directory)) == NULL)
1970   {
1971     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1972     return;
1973   }
1974
1975   while ((dir_entry = readdir(dir)) != NULL)    /* last directory entry */
1976   {
1977     if (strlen(dir_entry->d_name) > 4 &&
1978         dir_entry->d_name[3] == '.' &&
1979         strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1980     {
1981       char levelnum_str[4];
1982       int levelnum_value;
1983
1984       strncpy(levelnum_str, dir_entry->d_name, 3);
1985       levelnum_str[3] = '\0';
1986
1987       levelnum_value = atoi(levelnum_str);
1988
1989       if (levelnum_value < leveldir[leveldir_nr].first_level)
1990       {
1991         Error(ERR_WARN, "additional level %d found", levelnum_value);
1992         leveldir[leveldir_nr].first_level = levelnum_value;
1993       }
1994       else if (levelnum_value > leveldir[leveldir_nr].last_level)
1995       {
1996         Error(ERR_WARN, "additional level %d found", levelnum_value);
1997         leveldir[leveldir_nr].last_level = levelnum_value;
1998       }
1999     }
2000   }
2001
2002   closedir(dir);
2003 }
2004
2005 void LoadLevelSetup_SeriesInfo(int leveldir_nr)
2006 {
2007   char *filename;
2008   struct SetupFileList *level_setup_list = NULL;
2009   char *level_subdir = leveldir[leveldir_nr].filename;
2010
2011   /* always start with reliable default values */
2012 #if 0
2013   level_nr = 0;
2014   leveldir[leveldir_nr].handicap_level = 0;
2015 #else
2016   level_nr = leveldir[leveldir_nr].first_level;
2017 #endif
2018
2019   checkSeriesInfo(leveldir_nr);
2020
2021   /* ----------------------------------------------------------------------- */
2022   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2023   /* ----------------------------------------------------------------------- */
2024
2025   level_subdir = leveldir[leveldir_nr].filename;
2026
2027   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2028
2029   if ((level_setup_list = loadSetupFileList(filename)))
2030   {
2031     char *token_value;
2032
2033     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2034
2035     if (token_value)
2036     {
2037       level_nr = atoi(token_value);
2038
2039       if (level_nr < leveldir[leveldir_nr].first_level)
2040         level_nr = leveldir[leveldir_nr].first_level;
2041       if (level_nr > leveldir[leveldir_nr].last_level)
2042         level_nr = leveldir[leveldir_nr].last_level;
2043     }
2044
2045     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2046
2047     if (token_value)
2048     {
2049       int level_nr = atoi(token_value);
2050
2051       if (level_nr < leveldir[leveldir_nr].first_level)
2052         level_nr = leveldir[leveldir_nr].first_level;
2053       if (level_nr > leveldir[leveldir_nr].last_level + 1)
2054         level_nr = leveldir[leveldir_nr].last_level;
2055
2056       if (leveldir[leveldir_nr].user_defined)
2057         level_nr = leveldir[leveldir_nr].last_level;
2058
2059       leveldir[leveldir_nr].handicap_level = level_nr;
2060     }
2061
2062     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2063
2064     freeSetupFileList(level_setup_list);
2065   }
2066   else
2067     Error(ERR_WARN, "using default setup values");
2068
2069   free(filename);
2070 }
2071
2072 void SaveLevelSetup_SeriesInfo(int leveldir_nr)
2073 {
2074   char *filename;
2075   char *level_subdir = leveldir[leveldir_nr].filename;
2076   char *level_nr_str = int2str(level_nr, 0);
2077   char *handicap_level_str = int2str(leveldir[leveldir_nr].handicap_level, 0);
2078   FILE *file;
2079
2080   /* ----------------------------------------------------------------------- */
2081   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2082   /* ----------------------------------------------------------------------- */
2083
2084   InitLevelSetupDirectory(level_subdir);
2085
2086   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2087
2088   if (!(file = fopen(filename, "w")))
2089   {
2090     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2091     free(filename);
2092     return;
2093   }
2094
2095   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2096                                                  LEVELSETUP_COOKIE));
2097   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2098                                                level_nr_str));
2099   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2100                                                handicap_level_str));
2101
2102   fclose(file);
2103   free(filename);
2104
2105   chmod(filename, SETUP_PERMS);
2106 }
2107
2108 #ifdef MSDOS
2109 void initErrorFile()
2110 {
2111   char *filename;
2112
2113   InitUserDataDirectory();
2114
2115   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2116   unlink(filename);
2117   free(filename);
2118 }
2119
2120 FILE *openErrorFile()
2121 {
2122   char *filename;
2123   FILE *error_file;
2124
2125   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2126   error_file = fopen(filename, "a");
2127   free(filename);
2128
2129   return error_file;
2130 }
2131
2132 void dumpErrorFile()
2133 {
2134   char *filename;
2135   FILE *error_file;
2136
2137   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2138   error_file = fopen(filename, "r");
2139   free(filename);
2140
2141   if (error_file != NULL)
2142   {
2143     while (!feof(error_file))
2144       fputc(fgetc(error_file), stderr);
2145
2146     fclose(error_file);
2147   }
2148 }
2149 #endif