rnd-19990920-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_AUTHOR          31
1065 #define LEVELINFO_TOKEN_IMPORTED_FROM   32
1066 #define LEVELINFO_TOKEN_LEVELS          33
1067 #define LEVELINFO_TOKEN_FIRST_LEVEL     34
1068 #define LEVELINFO_TOKEN_SORT_PRIORITY   35
1069 #define LEVELINFO_TOKEN_READONLY        36
1070
1071 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
1072 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_TIME_LIMIT
1073
1074 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
1075 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
1076
1077 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
1078 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
1079
1080 #define TYPE_BOOLEAN                    1
1081 #define TYPE_SWITCH                     2
1082 #define TYPE_KEYSYM                     3
1083 #define TYPE_INTEGER                    4
1084 #define TYPE_STRING                     5
1085
1086 static struct SetupInfo si;
1087 static struct SetupInputInfo sii;
1088 static struct LevelDirInfo ldi;
1089 static struct
1090 {
1091   int type;
1092   void *value;
1093   char *text;
1094 } token_info[] =
1095 {
1096   /* global setup */
1097   { TYPE_STRING,  &si.player_name,      "player_name"                   },
1098   { TYPE_SWITCH,  &si.sound,            "sound"                         },
1099   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
1100   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
1101   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
1102
1103 #if 0
1104   { TYPE_SWITCH,  &si.toons,            "toons"                         },
1105   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
1106 #endif
1107
1108   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
1109   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
1110   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
1111   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
1112   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
1113   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
1114   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
1115   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
1116
1117   /* player setup */
1118   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1119   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1120   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1121   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1122   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1123   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1124   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1125   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1126   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1127   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1128   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
1129   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
1130   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
1131   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
1132   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
1133   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               },
1134
1135   /* level directory info */
1136   { TYPE_STRING,  &ldi.name,            "name"                          },
1137   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
1138   { TYPE_STRING,  &ldi.author,          "author"                        },
1139   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
1140   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
1141   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
1142   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
1143   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
1144 };
1145
1146 static char *string_tolower(char *s)
1147 {
1148   static char s_lower[100];
1149   int i;
1150
1151   if (strlen(s) >= 100)
1152     return s;
1153
1154   strcpy(s_lower, s);
1155
1156   for (i=0; i<strlen(s_lower); i++)
1157     s_lower[i] = tolower(s_lower[i]);
1158
1159   return s_lower;
1160 }
1161
1162 static int get_string_integer_value(char *s)
1163 {
1164   static char *number_text[][3] =
1165   {
1166     { "0", "zero", "null", },
1167     { "1", "one", "first" },
1168     { "2", "two", "second" },
1169     { "3", "three", "third" },
1170     { "4", "four", "fourth" },
1171     { "5", "five", "fifth" },
1172     { "6", "six", "sixth" },
1173     { "7", "seven", "seventh" },
1174     { "8", "eight", "eighth" },
1175     { "9", "nine", "ninth" },
1176     { "10", "ten", "tenth" },
1177     { "11", "eleven", "eleventh" },
1178     { "12", "twelve", "twelfth" },
1179   };
1180
1181   int i, j;
1182
1183   for (i=0; i<13; i++)
1184     for (j=0; j<3; j++)
1185       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1186         return i;
1187
1188   return atoi(s);
1189 }
1190
1191 static boolean get_string_boolean_value(char *s)
1192 {
1193   if (strcmp(string_tolower(s), "true") == 0 ||
1194       strcmp(string_tolower(s), "yes") == 0 ||
1195       strcmp(string_tolower(s), "on") == 0 ||
1196       get_string_integer_value(s) == 1)
1197     return TRUE;
1198   else
1199     return FALSE;
1200 }
1201
1202 static char *getFormattedSetupEntry(char *token, char *value)
1203 {
1204   int i;
1205   static char entry[MAX_LINE_LEN];
1206
1207   sprintf(entry, "%s:", token);
1208   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1209     entry[i] = ' ';
1210   entry[i] = '\0';
1211
1212   strcat(entry, value);
1213
1214   return entry;
1215 }
1216
1217 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1218 {
1219   if (!setup_file_list)
1220     return;
1221
1222   if (setup_file_list->token)
1223     free(setup_file_list->token);
1224   if (setup_file_list->value)
1225     free(setup_file_list->value);
1226   if (setup_file_list->next)
1227     freeSetupFileList(setup_file_list->next);
1228   free(setup_file_list);
1229 }
1230
1231 static struct SetupFileList *newSetupFileList(char *token, char *value)
1232 {
1233   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1234
1235   new->token = checked_malloc(strlen(token) + 1);
1236   strcpy(new->token, token);
1237
1238   new->value = checked_malloc(strlen(value) + 1);
1239   strcpy(new->value, value);
1240
1241   new->next = NULL;
1242
1243   return new;
1244 }
1245
1246 static char *getTokenValue(struct SetupFileList *setup_file_list,
1247                            char *token)
1248 {
1249   if (!setup_file_list)
1250     return NULL;
1251
1252   if (strcmp(setup_file_list->token, token) == 0)
1253     return setup_file_list->value;
1254   else
1255     return getTokenValue(setup_file_list->next, token);
1256 }
1257
1258 static void setTokenValue(struct SetupFileList *setup_file_list,
1259                           char *token, char *value)
1260 {
1261   if (!setup_file_list)
1262     return;
1263
1264   if (strcmp(setup_file_list->token, token) == 0)
1265   {
1266     free(setup_file_list->value);
1267     setup_file_list->value = checked_malloc(strlen(value) + 1);
1268     strcpy(setup_file_list->value, value);
1269   }
1270   else if (setup_file_list->next == NULL)
1271     setup_file_list->next = newSetupFileList(token, value);
1272   else
1273     setTokenValue(setup_file_list->next, token, value);
1274 }
1275
1276 #ifdef DEBUG
1277 static void printSetupFileList(struct SetupFileList *setup_file_list)
1278 {
1279   if (!setup_file_list)
1280     return;
1281
1282   printf("token: '%s'\n", setup_file_list->token);
1283   printf("value: '%s'\n", setup_file_list->value);
1284
1285   printSetupFileList(setup_file_list->next);
1286 }
1287 #endif
1288
1289 static struct SetupFileList *loadSetupFileList(char *filename)
1290 {
1291   int line_len;
1292   char line[MAX_LINE_LEN];
1293   char *token, *value, *line_ptr;
1294   struct SetupFileList *setup_file_list = newSetupFileList("", "");
1295   struct SetupFileList *first_valid_list_entry;
1296
1297   FILE *file;
1298
1299   if (!(file = fopen(filename, "r")))
1300   {
1301     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1302     return NULL;
1303   }
1304
1305   while(!feof(file))
1306   {
1307     /* read next line of input file */
1308     if (!fgets(line, MAX_LINE_LEN, file))
1309       break;
1310
1311     /* cut trailing comment or whitespace from input line */
1312     for (line_ptr = line; *line_ptr; line_ptr++)
1313     {
1314       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1315       {
1316         *line_ptr = '\0';
1317         break;
1318       }
1319     }
1320
1321     /* cut trailing whitespaces from input line */
1322     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1323       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1324         *line_ptr = '\0';
1325
1326     /* ignore empty lines */
1327     if (*line == '\0')
1328       continue;
1329
1330     line_len = strlen(line);
1331
1332     /* cut leading whitespaces from token */
1333     for (token = line; *token; token++)
1334       if (*token != ' ' && *token != '\t')
1335         break;
1336
1337     /* find end of token */
1338     for (line_ptr = token; *line_ptr; line_ptr++)
1339     {
1340       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1341       {
1342         *line_ptr = '\0';
1343         break;
1344       }
1345     }
1346
1347     if (line_ptr < line + line_len)
1348       value = line_ptr + 1;
1349     else
1350       value = "\0";
1351
1352     /* cut leading whitespaces from value */
1353     for (; *value; value++)
1354       if (*value != ' ' && *value != '\t')
1355         break;
1356
1357     if (*token && *value)
1358       setTokenValue(setup_file_list, token, value);
1359   }
1360
1361   fclose(file);
1362
1363   first_valid_list_entry = setup_file_list->next;
1364
1365   /* free empty list header */
1366   setup_file_list->next = NULL;
1367   freeSetupFileList(setup_file_list);
1368
1369   if (first_valid_list_entry == NULL)
1370     Error(ERR_WARN, "configuration file '%s' is empty", filename);
1371
1372   return first_valid_list_entry;
1373 }
1374
1375 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1376                                          char *identifier)
1377 {
1378   if (!setup_file_list)
1379     return;
1380
1381   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1382   {
1383     if (strcmp(setup_file_list->value, identifier) != 0)
1384     {
1385       Error(ERR_WARN, "configuration file has wrong version");
1386       return;
1387     }
1388     else
1389       return;
1390   }
1391
1392   if (setup_file_list->next)
1393     checkSetupFileListIdentifier(setup_file_list->next, identifier);
1394   else
1395   {
1396     Error(ERR_WARN, "configuration file has no version information");
1397     return;
1398   }
1399 }
1400
1401 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1402 {
1403   ldi->filename = NULL;
1404   ldi->name = getStringCopy(ANONYMOUS_NAME);
1405   ldi->name_short = NULL;
1406   ldi->author = getStringCopy(ANONYMOUS_NAME);
1407   ldi->imported_from = NULL;
1408   ldi->levels = 0;
1409   ldi->first_level = 0;
1410   ldi->last_level = 0;
1411   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
1412   ldi->readonly = TRUE;
1413   ldi->user_defined = FALSE;
1414   ldi->color = 0;
1415   ldi->class_desc = NULL;
1416   ldi->handicap_level = 0;
1417 }
1418
1419 static void setSetupInfoToDefaults(struct SetupInfo *si)
1420 {
1421   int i;
1422
1423   si->player_name = getStringCopy(getLoginName());
1424
1425   si->sound = TRUE;
1426   si->sound_loops = TRUE;
1427   si->sound_music = TRUE;
1428   si->sound_simple = TRUE;
1429   si->toons = TRUE;
1430   si->double_buffering = TRUE;
1431   si->direct_draw = !si->double_buffering;
1432   si->scroll_delay = TRUE;
1433   si->soft_scrolling = TRUE;
1434   si->fading = FALSE;
1435   si->autorecord = TRUE;
1436   si->quick_doors = FALSE;
1437   si->team_mode = FALSE;
1438   si->handicap = TRUE;
1439   si->time_limit = TRUE;
1440
1441   for (i=0; i<MAX_PLAYERS; i++)
1442   {
1443     si->input[i].use_joystick = FALSE;
1444     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1445     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1446     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1447     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1448     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1449     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1450     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1451     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1452     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1453     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
1454     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1455     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
1456     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
1457     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
1458     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
1459   }
1460 }
1461
1462 static void setSetupInfo(int token_nr, char *token_value)
1463 {
1464   int token_type = token_info[token_nr].type;
1465   void *setup_value = token_info[token_nr].value;
1466
1467   if (token_value == NULL)
1468     return;
1469
1470   /* set setup field to corresponding token value */
1471   switch (token_type)
1472   {
1473     case TYPE_BOOLEAN:
1474     case TYPE_SWITCH:
1475       *(boolean *)setup_value = get_string_boolean_value(token_value);
1476       break;
1477
1478     case TYPE_KEYSYM:
1479       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
1480       break;
1481
1482     case TYPE_INTEGER:
1483       *(int *)setup_value = get_string_integer_value(token_value);
1484       break;
1485
1486     case TYPE_STRING:
1487       if (*(char **)setup_value != NULL)
1488         free(*(char **)setup_value);
1489       *(char **)setup_value = getStringCopy(token_value);
1490       break;
1491
1492     default:
1493       break;
1494   }
1495 }
1496
1497 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1498 {
1499   int i, pnr;
1500
1501   if (!setup_file_list)
1502     return;
1503
1504   /* handle global setup values */
1505   si = setup;
1506   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1507     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1508   setup = si;
1509
1510   /* handle player specific setup values */
1511   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1512   {
1513     char prefix[30];
1514
1515     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1516
1517     sii = setup.input[pnr];
1518     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1519     {
1520       char full_token[100];
1521
1522       sprintf(full_token, "%s%s", prefix, token_info[i].text);
1523       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1524     }
1525     setup.input[pnr] = sii;
1526   }
1527 }
1528
1529 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
1530 {
1531   int i;
1532
1533   if (!level_series_name)
1534     return 0;
1535
1536   for (i=0; i<num_leveldirs; i++)
1537     if (strcmp(level_series_name, leveldir[i].filename) == 0)
1538       return i;
1539
1540   return 0;
1541 }
1542
1543 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1544 {
1545   const struct LevelDirInfo *entry1 = object1;
1546   const struct LevelDirInfo *entry2 = object2;
1547   int compare_result;
1548
1549   if (entry1->sort_priority == entry2->sort_priority)
1550   {
1551     char *name1 = getStringToLower(entry1->name_short);
1552     char *name2 = getStringToLower(entry2->name_short);
1553
1554     compare_result = strcmp(name1, name2);
1555
1556     free(name1);
1557     free(name2);
1558   }
1559   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1560     compare_result = entry1->sort_priority - entry2->sort_priority;
1561   else
1562     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1563
1564   return compare_result;
1565 }
1566
1567 static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
1568 {
1569   DIR *dir;
1570   struct stat file_status;
1571   char *directory = NULL;
1572   char *filename = NULL;
1573   struct SetupFileList *setup_file_list = NULL;
1574   struct dirent *dir_entry;
1575   int i, current_entry = start_entry;
1576
1577   if ((dir = opendir(level_directory)) == NULL)
1578   {
1579     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1580     return current_entry;
1581   }
1582
1583   while (current_entry < MAX_LEVDIR_ENTRIES)
1584   {
1585     if ((dir_entry = readdir(dir)) == NULL)     /* last directory entry */
1586       break;
1587
1588     /* skip entries for current and parent directory */
1589     if (strcmp(dir_entry->d_name, ".")  == 0 ||
1590         strcmp(dir_entry->d_name, "..") == 0)
1591       continue;
1592
1593     /* find out if directory entry is itself a directory */
1594     directory = getPath2(level_directory, dir_entry->d_name);
1595     if (stat(directory, &file_status) != 0 ||           /* cannot stat file */
1596         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
1597     {
1598       free(directory);
1599       continue;
1600     }
1601
1602     filename = getPath2(directory, LEVELINFO_FILENAME);
1603     setup_file_list = loadSetupFileList(filename);
1604
1605     if (setup_file_list)
1606     {
1607       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1608       setLevelDirInfoToDefaults(&leveldir[current_entry]);
1609
1610       ldi = leveldir[current_entry];
1611       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1612         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1613       leveldir[current_entry] = ldi;
1614
1615       if (leveldir[current_entry].name_short == NULL)
1616         leveldir[current_entry].name_short =
1617           getStringCopy(leveldir[current_entry].name);
1618
1619       leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
1620       leveldir[current_entry].last_level =
1621         leveldir[current_entry].first_level +
1622         leveldir[current_entry].levels - 1;
1623       leveldir[current_entry].user_defined =
1624         (level_directory == options.level_directory ? FALSE : TRUE);
1625       leveldir[current_entry].color = LEVELCOLOR(&leveldir[current_entry]);
1626       leveldir[current_entry].class_desc =
1627         getLevelClassDescription(&leveldir[current_entry]);
1628
1629       freeSetupFileList(setup_file_list);
1630       current_entry++;
1631     }
1632     else
1633       Error(ERR_WARN, "ignoring level directory '%s'", directory);
1634
1635     free(directory);
1636     free(filename);
1637   }
1638
1639   if (current_entry == MAX_LEVDIR_ENTRIES)
1640     Error(ERR_WARN, "using %d level directories -- ignoring the rest",
1641           current_entry);
1642
1643   closedir(dir);
1644
1645   if (current_entry == start_entry)
1646     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1647           level_directory);
1648
1649   return current_entry;
1650 }
1651
1652 void LoadLevelInfo()
1653 {
1654   InitUserLevelDirectory(getLoginName());
1655
1656   num_leveldirs = 0;
1657   leveldir_nr = 0;
1658
1659   num_leveldirs = LoadLevelInfoFromLevelDir(options.level_directory,
1660                                             num_leveldirs);
1661   num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
1662                                             num_leveldirs);
1663
1664   if (num_leveldirs == 0)
1665     Error(ERR_EXIT, "cannot find any valid level series in any directory");
1666
1667   if (num_leveldirs > 1)
1668     qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
1669           compareLevelDirInfoEntries);
1670 }
1671
1672 static void SaveUserLevelInfo()
1673 {
1674   char *filename;
1675   FILE *file;
1676   int i;
1677
1678   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1679
1680   if (!(file = fopen(filename, "w")))
1681   {
1682     Error(ERR_WARN, "cannot write level info file '%s'", filename);
1683     free(filename);
1684     return;
1685   }
1686
1687   /* always start with reliable default values */
1688   setLevelDirInfoToDefaults(&ldi);
1689
1690   ldi.name = getLoginName();
1691   ldi.author = getRealName();
1692   ldi.levels = 100;
1693   ldi.first_level = 1;
1694   ldi.sort_priority = LEVELCLASS_USER_START;
1695   ldi.readonly = FALSE;
1696
1697   fprintf(file, "%s\n\n",
1698           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1699
1700   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1701     if (i != LEVELINFO_TOKEN_NAME_SHORT && i != LEVELINFO_TOKEN_IMPORTED_FROM)
1702       fprintf(file, "%s\n", getSetupLine("", i));
1703
1704   fclose(file);
1705   free(filename);
1706
1707   chmod(filename, SETUP_PERMS);
1708 }
1709
1710 void LoadSetup()
1711 {
1712   char *filename;
1713   struct SetupFileList *setup_file_list = NULL;
1714
1715   /* always start with reliable default values */
1716   setSetupInfoToDefaults(&setup);
1717
1718   filename = getPath2(getSetupDir(), SETUP_FILENAME);
1719
1720   setup_file_list = loadSetupFileList(filename);
1721
1722   if (setup_file_list)
1723   {
1724     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1725     decodeSetupFileList(setup_file_list);
1726
1727     setup.direct_draw = !setup.double_buffering;
1728
1729     freeSetupFileList(setup_file_list);
1730
1731     /* needed to work around problems with fixed length strings */
1732     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1733       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1734     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1735     {
1736       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1737
1738       strcpy(new_name, setup.player_name);
1739       free(setup.player_name);
1740       setup.player_name = new_name;
1741     }
1742   }
1743   else
1744     Error(ERR_WARN, "using default setup values");
1745
1746   free(filename);
1747 }
1748
1749 static char *getSetupLine(char *prefix, int token_nr)
1750 {
1751   int i;
1752   static char entry[MAX_LINE_LEN];
1753   int token_type = token_info[token_nr].type;
1754   void *setup_value = token_info[token_nr].value;
1755   char *token_text = token_info[token_nr].text;
1756
1757   /* start with the prefix, token and some spaces to format output line */
1758   sprintf(entry, "%s%s:", prefix, token_text);
1759   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1760     strcat(entry, " ");
1761
1762   /* continue with the token's value (which can have different types) */
1763   switch (token_type)
1764   {
1765     case TYPE_BOOLEAN:
1766       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1767       break;
1768
1769     case TYPE_SWITCH:
1770       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1771       break;
1772
1773     case TYPE_KEYSYM:
1774       {
1775         KeySym keysym = *(KeySym *)setup_value;
1776         char *keyname = getKeyNameFromKeySym(keysym);
1777
1778         strcat(entry, getX11KeyNameFromKeySym(keysym));
1779         for (i=strlen(entry); i<50; i++)
1780           strcat(entry, " ");
1781
1782         /* add comment, if useful */
1783         if (strcmp(keyname, "(undefined)") != 0 &&
1784             strcmp(keyname, "(unknown)") != 0)
1785         {
1786           strcat(entry, "# ");
1787           strcat(entry, keyname);
1788         }
1789       }
1790       break;
1791
1792     case TYPE_INTEGER:
1793       {
1794         char buffer[MAX_LINE_LEN];
1795
1796         sprintf(buffer, "%d", *(int *)setup_value);
1797         strcat(entry, buffer);
1798       }
1799       break;
1800
1801     case TYPE_STRING:
1802       strcat(entry, *(char **)setup_value);
1803       break;
1804
1805     default:
1806       break;
1807   }
1808
1809   return entry;
1810 }
1811
1812 void SaveSetup()
1813 {
1814   int i, pnr;
1815   char *filename;
1816   FILE *file;
1817
1818   InitUserDataDirectory();
1819
1820   filename = getPath2(getSetupDir(), SETUP_FILENAME);
1821
1822   if (!(file = fopen(filename, "w")))
1823   {
1824     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1825     free(filename);
1826     return;
1827   }
1828
1829   fprintf(file, "%s\n",
1830           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1831   fprintf(file, "\n");
1832
1833   /* handle global setup values */
1834   si = setup;
1835   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1836   {
1837     fprintf(file, "%s\n", getSetupLine("", i));
1838
1839     /* just to make things nicer :) */
1840     if (i == SETUP_TOKEN_PLAYER_NAME)
1841       fprintf(file, "\n");
1842   }
1843
1844   /* handle player specific setup values */
1845   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1846   {
1847     char prefix[30];
1848
1849     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1850     fprintf(file, "\n");
1851
1852     sii = setup.input[pnr];
1853     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1854       fprintf(file, "%s\n", getSetupLine(prefix, i));
1855   }
1856
1857   fclose(file);
1858   free(filename);
1859
1860   chmod(filename, SETUP_PERMS);
1861 }
1862
1863 void LoadLevelSetup_LastSeries()
1864 {
1865   char *filename;
1866   struct SetupFileList *level_setup_list = NULL;
1867
1868   /* always start with reliable default values */
1869   leveldir_nr = 0;
1870
1871   /* ----------------------------------------------------------------------- */
1872   /* ~/.rocksndiamonds/levelsetup.conf                                       */
1873   /* ----------------------------------------------------------------------- */
1874
1875   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1876
1877   if ((level_setup_list = loadSetupFileList(filename)))
1878   {
1879     char *last_level_series =
1880       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1881
1882     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1883
1884     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1885
1886     freeSetupFileList(level_setup_list);
1887   }
1888   else
1889     Error(ERR_WARN, "using default setup values");
1890
1891   free(filename);
1892 }
1893
1894 void SaveLevelSetup_LastSeries()
1895 {
1896   char *filename;
1897   char *level_subdir = leveldir[leveldir_nr].filename;
1898   FILE *file;
1899
1900   /* ----------------------------------------------------------------------- */
1901   /* ~/.rocksndiamonds/levelsetup.conf                                       */
1902   /* ----------------------------------------------------------------------- */
1903
1904   InitUserDataDirectory();
1905
1906   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1907
1908   if (!(file = fopen(filename, "w")))
1909   {
1910     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1911     free(filename);
1912     return;
1913   }
1914
1915   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1916                                                  LEVELSETUP_COOKIE));
1917   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1918                                                level_subdir));
1919
1920   fclose(file);
1921   free(filename);
1922
1923   chmod(filename, SETUP_PERMS);
1924 }
1925
1926 void LoadLevelSetup_SeriesInfo(int leveldir_nr)
1927 {
1928   char *filename;
1929   struct SetupFileList *level_setup_list = NULL;
1930   char *level_subdir = leveldir[leveldir_nr].filename;
1931
1932   /* always start with reliable default values */
1933   level_nr = 0;
1934   leveldir[leveldir_nr].handicap_level = 0;
1935
1936   /* ----------------------------------------------------------------------- */
1937   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
1938   /* ----------------------------------------------------------------------- */
1939
1940   level_subdir = leveldir[leveldir_nr].filename;
1941
1942   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1943
1944   if ((level_setup_list = loadSetupFileList(filename)))
1945   {
1946     char *token_value;
1947
1948     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1949
1950     if (token_value)
1951     {
1952       level_nr = atoi(token_value);
1953
1954       if (level_nr < leveldir[leveldir_nr].first_level)
1955         level_nr = leveldir[leveldir_nr].first_level;
1956       if (level_nr > leveldir[leveldir_nr].last_level)
1957         level_nr = leveldir[leveldir_nr].last_level;
1958     }
1959
1960     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
1961
1962     if (token_value)
1963     {
1964       int level_nr = atoi(token_value);
1965
1966       if (level_nr < leveldir[leveldir_nr].first_level)
1967         level_nr = leveldir[leveldir_nr].first_level;
1968       if (level_nr > leveldir[leveldir_nr].last_level + 1)
1969         level_nr = leveldir[leveldir_nr].last_level;
1970
1971       leveldir[leveldir_nr].handicap_level = level_nr;
1972     }
1973
1974     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1975
1976     freeSetupFileList(level_setup_list);
1977   }
1978   else
1979     Error(ERR_WARN, "using default setup values");
1980
1981   free(filename);
1982 }
1983
1984 void SaveLevelSetup_SeriesInfo(int leveldir_nr)
1985 {
1986   char *filename;
1987   char *level_subdir = leveldir[leveldir_nr].filename;
1988   char *level_nr_str = int2str(level_nr, 0);
1989   char *handicap_level_str = int2str(leveldir[leveldir_nr].handicap_level, 0);
1990   FILE *file;
1991
1992   /* ----------------------------------------------------------------------- */
1993   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
1994   /* ----------------------------------------------------------------------- */
1995
1996   InitLevelSetupDirectory(level_subdir);
1997
1998   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1999
2000   if (!(file = fopen(filename, "w")))
2001   {
2002     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2003     free(filename);
2004     return;
2005   }
2006
2007   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2008                                                  LEVELSETUP_COOKIE));
2009   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2010                                                level_nr_str));
2011   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2012                                                handicap_level_str));
2013
2014   fclose(file);
2015   free(filename);
2016
2017   chmod(filename, SETUP_PERMS);
2018 }
2019
2020 #ifdef MSDOS
2021 static boolean initErrorFile()
2022 {
2023   char *filename;
2024   FILE *error_file;
2025
2026   InitUserDataDirectory();
2027
2028   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2029   error_file = fopen(filename, "w");
2030   free(filename);
2031
2032   if (error_file == NULL)
2033     return FALSE;
2034
2035   fclose(error_file);
2036
2037   return TRUE;
2038 }
2039
2040 FILE *openErrorFile()
2041 {
2042   static boolean first_access = TRUE;
2043   char *filename;
2044   FILE *error_file;
2045
2046   if (first_access)
2047   {
2048     if (!initErrorFile())
2049       return NULL;
2050
2051     first_access = FALSE;
2052   }
2053
2054   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2055   error_file = fopen(filename, "a");
2056   free(filename);
2057
2058   return error_file;
2059 }
2060
2061 void dumpErrorFile()
2062 {
2063   char *filename;
2064   FILE *error_file;
2065
2066   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2067   error_file = fopen(filename, "r");
2068   free(filename);
2069
2070   if (error_file != NULL)
2071   {
2072     while (!feof(error_file))
2073       fputc(fgetc(error_file), stderr);
2074
2075     fclose(error_file);
2076   }
2077 }
2078 #endif