rnd-19981116-1
[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_LINE_LEN                    1000
26
27 static char *getGlobalDataDir()
28 {
29   return GAME_DIR;
30 }
31
32 static char *getUserDataDir()
33 {
34   static char *userdata_dir = NULL;
35
36   if (!userdata_dir)
37   {
38     char *home_dir = getHomeDir();
39     char *data_dir = USERDATA_DIRECTORY;
40
41     userdata_dir = checked_malloc(strlen(home_dir) + strlen(data_dir) + 2);
42     sprintf(userdata_dir, "%s/%s", home_dir, data_dir);
43   }
44
45   return userdata_dir;
46 }
47
48 static char *getSetupDir()
49 {
50   return getUserDataDir();
51 }
52
53 static char *getTapeDir(char *level_subdir)
54 {
55   static char *tape_dir = NULL;
56   char *data_dir = getUserDataDir();
57   char *tape_subdir = TAPES_DIRECTORY;
58
59   if (tape_dir)
60     free(tape_dir);
61
62   tape_dir = checked_malloc(strlen(data_dir) + strlen(tape_subdir) +
63                             strlen(level_subdir) + 3);
64   sprintf(tape_dir, "%s/%s%s%s", data_dir, tape_subdir,
65           (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
66
67   return tape_dir;
68 }
69
70 static char *getScoreDir(char *level_subdir)
71 {
72   static char *score_dir = NULL;
73   char *data_dir = getGlobalDataDir();
74   char *score_subdir = SCORES_DIRECTORY;
75
76   if (score_dir)
77     free(score_dir);
78
79   score_dir = checked_malloc(strlen(data_dir) + strlen(score_subdir) +
80                              strlen(level_subdir) + 3);
81   sprintf(score_dir, "%s/%s%s%s", data_dir, score_subdir,
82           (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
83
84   return score_dir;
85 }
86
87 static void createDirectory(char *dir, char *text)
88 {
89   if (access(dir, F_OK) != 0)
90     if (mkdir(dir, USERDATA_DIR_MODE) != 0)
91       Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
92 }
93
94 static void InitUserDataDirectory()
95 {
96   createDirectory(getUserDataDir(), "user data");
97 }
98
99 static void InitTapeDirectory(char *level_subdir)
100 {
101   createDirectory(getTapeDir(""), "main tape");
102   createDirectory(getTapeDir(level_subdir), "level tape");
103 }
104
105 static void InitScoreDirectory(char *level_subdir)
106 {
107   createDirectory(getScoreDir(""), "main score");
108   createDirectory(getScoreDir(level_subdir), "level score");
109 }
110
111 void LoadLevel(int level_nr)
112 {
113   int i, x, y;
114   char filename[MAX_FILENAME_LEN];
115   char cookie[MAX_FILENAME_LEN];
116   FILE *file;
117
118   sprintf(filename, "%s/%s/%d",
119           options.level_directory, leveldir[leveldir_nr].filename, level_nr);
120
121   if (!(file = fopen(filename, "r")))
122     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
123   else
124   {
125     fgets(cookie, LEVEL_COOKIE_LEN, file);
126     fgetc(file);
127
128     if (strcmp(cookie,LEVEL_COOKIE))
129     {
130       Error(ERR_WARN, "wrong format of level file '%s'", filename);
131       fclose(file);
132       file = NULL;
133     }
134   }
135
136   if (file)
137   {
138     lev_fieldx = level.fieldx = fgetc(file);
139     lev_fieldy = level.fieldy = fgetc(file);
140
141     level.time          = (fgetc(file)<<8) | fgetc(file);
142     level.edelsteine    = (fgetc(file)<<8) | fgetc(file);
143     for(i=0; i<MAX_LEVNAMLEN; i++)
144       level.name[i]     = fgetc(file);
145     level.name[MAX_LEVNAMLEN-1] = 0;
146     for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
147       level.score[i]    = fgetc(file);
148     for(i=0; i<4; i++)
149       for(y=0; y<3; y++)
150         for(x=0; x<3; x++)
151           level.mampfer_inhalt[i][x][y] = fgetc(file);
152     level.tempo_amoebe  = fgetc(file);
153     level.dauer_sieb    = fgetc(file);
154     level.dauer_ablenk  = fgetc(file);
155     level.amoebe_inhalt = fgetc(file);
156
157     for(i=0; i<NUM_FREE_LVHD_BYTES; i++) /* Rest frei / Headergröße 80 Bytes */
158       fgetc(file);
159
160     for(y=0; y<MAX_LEV_FIELDY; y++) 
161       for(x=0; x<MAX_LEV_FIELDX; x++) 
162         Feld[x][y] = Ur[x][y] = EL_ERDREICH;
163
164     for(y=0; y<lev_fieldy; y++) 
165       for(x=0; x<lev_fieldx; x++) 
166         Feld[x][y] = Ur[x][y] = fgetc(file);
167
168     fclose(file);
169
170     if (level.time <= 10)       /* Mindestspieldauer */
171       level.time = 10;
172   }
173   else
174   {
175     lev_fieldx = level.fieldx = STD_LEV_FIELDX;
176     lev_fieldy = level.fieldy = STD_LEV_FIELDY;
177
178     level.time          = 100;
179     level.edelsteine    = 0;
180     strcpy(level.name, "Nameless Level");
181     for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
182       level.score[i]    = 10;
183     for(i=0; i<4; i++)
184       for(y=0; y<3; y++)
185         for(x=0; x<3; x++)
186           level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
187     level.tempo_amoebe  = 10;
188     level.dauer_sieb    = 10;
189     level.dauer_ablenk  = 10;
190     level.amoebe_inhalt = EL_DIAMANT;
191
192     for(y=0; y<STD_LEV_FIELDY; y++) 
193       for(x=0; x<STD_LEV_FIELDX; x++) 
194         Feld[x][y] = Ur[x][y] = EL_ERDREICH;
195     Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
196     Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
197       Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
198   }
199 }
200
201 void SaveLevel(int level_nr)
202 {
203   int i, x, y;
204   char filename[MAX_FILENAME_LEN];
205   FILE *file;
206
207   sprintf(filename, "%s/%s/%d",
208           options.level_directory, leveldir[leveldir_nr].filename, level_nr);
209
210   if (!(file = fopen(filename, "w")))
211   {
212     Error(ERR_WARN, "cannot save level file '%s'", filename);
213     return;
214   }
215
216   fputs(LEVEL_COOKIE,file);             /* Formatkennung */
217   fputc(0x0a, file);
218
219   fputc(level.fieldx, file);
220   fputc(level.fieldy, file);
221   fputc(level.time / 256, file);
222   fputc(level.time % 256, file);
223   fputc(level.edelsteine / 256, file);
224   fputc(level.edelsteine % 256, file);
225
226   for(i=0; i<MAX_LEVNAMLEN; i++)
227     fputc(level.name[i], file);
228   for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
229     fputc(level.score[i], file);
230   for(i=0; i<4; i++)
231     for(y=0; y<3; y++)
232       for(x=0; x<3; x++)
233         fputc(level.mampfer_inhalt[i][x][y], file);
234   fputc(level.tempo_amoebe, file);
235   fputc(level.dauer_sieb, file);
236   fputc(level.dauer_ablenk, file);
237   fputc(level.amoebe_inhalt, file);
238
239   for(i=0; i<NUM_FREE_LVHD_BYTES; i++)  /* Rest frei / Headergröße 80 Bytes */
240     fputc(0, file);
241
242   for(y=0; y<lev_fieldy; y++) 
243     for(x=0; x<lev_fieldx; x++) 
244       fputc(Ur[x][y], file);
245
246   fclose(file);
247
248   chmod(filename, LEVEL_PERMS);
249 }
250
251 void LoadTape(int level_nr)
252 {
253   int i;
254   char filename[MAX_FILENAME_LEN];
255   char cookie[MAX_FILENAME_LEN];
256   FILE *file;
257   boolean levelrec_10 = FALSE;
258
259   sprintf(filename, "%s/%d.%s",
260           getTapeDir(leveldir[leveldir_nr].filename),
261           level_nr, TAPEFILE_EXTENSION);
262
263   if ((file = fopen(filename, "r")))
264   {
265     fgets(cookie, LEVELREC_COOKIE_LEN, file);
266     fgetc(file);
267     if (!strcmp(cookie, LEVELREC_COOKIE_10))    /* old 1.0 tape format */
268       levelrec_10 = TRUE;
269     else if (strcmp(cookie, LEVELREC_COOKIE))   /* unknown tape format */
270     {
271       Error(ERR_WARN, "wrong format of level recording file '%s'", filename);
272       fclose(file);
273       file = NULL;
274     }
275   }
276
277   if (!file)
278     return;
279
280   tape.random_seed =
281     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
282   tape.date =
283     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
284   tape.length =
285     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
286
287   tape.level_nr = level_nr;
288   tape.counter = 0;
289   tape.changed = FALSE;
290
291   tape.recording = FALSE;
292   tape.playing = FALSE;
293   tape.pausing = FALSE;
294
295   for(i=0; i<tape.length; i++)
296   {
297     int j;
298
299     if (i >= MAX_TAPELEN)
300       break;
301
302     for(j=0; j<MAX_PLAYERS; j++)
303     {
304       if (levelrec_10 && j > 0)
305       {
306         tape.pos[i].action[j] = MV_NO_MOVING;
307         continue;
308       }
309       tape.pos[i].action[j] = fgetc(file);
310     }
311
312     tape.pos[i].delay = fgetc(file);
313
314     if (levelrec_10)
315     {
316       /* eliminate possible diagonal moves in old tapes */
317       /* this is only for backward compatibility */
318
319       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
320       byte action = tape.pos[i].action[0];
321       int k, num_moves = 0;
322
323       for (k=0; k<4; k++)
324       {
325         if (action & joy_dir[k])
326         {
327           tape.pos[i + num_moves].action[0] = joy_dir[k];
328           if (num_moves > 0)
329             tape.pos[i + num_moves].delay = 0;
330           num_moves++;
331         }
332       }
333
334       if (num_moves > 1)
335       {
336         num_moves--;
337         i += num_moves;
338         tape.length += num_moves;
339       }
340     }
341
342     if (feof(file))
343       break;
344   }
345
346   fclose(file);
347
348   if (i != tape.length)
349     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
350
351   tape.length_seconds = GetTapeLength();
352 }
353
354 void SaveTape(int level_nr)
355 {
356   int i;
357   char filename[MAX_FILENAME_LEN];
358   FILE *file;
359   boolean new_tape = TRUE;
360
361   sprintf(filename, "%s/%d.%s",
362           getTapeDir(leveldir[leveldir_nr].filename),
363           level_nr, TAPEFILE_EXTENSION);
364
365   InitTapeDirectory(leveldir[leveldir_nr].filename);
366
367   /* Testen, ob bereits eine Aufnahme existiert */
368   if ((file = fopen(filename, "r")))
369   {
370     new_tape = FALSE;
371     fclose(file);
372
373     if (!Request("Replace old tape ?", REQ_ASK))
374       return;
375   }
376
377   if (!(file = fopen(filename, "w")))
378   {
379     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
380     return;
381   }
382
383   fputs(LEVELREC_COOKIE, file);         /* Formatkennung */
384   fputc(0x0a, file);
385
386   fputc((tape.random_seed >> 24) & 0xff,file);
387   fputc((tape.random_seed >> 16) & 0xff,file);
388   fputc((tape.random_seed >>  8) & 0xff,file);
389   fputc((tape.random_seed >>  0) & 0xff,file);
390
391   fputc((tape.date >>  24) & 0xff,file);
392   fputc((tape.date >>  16) & 0xff,file);
393   fputc((tape.date >>   8) & 0xff,file);
394   fputc((tape.date >>   0) & 0xff,file);
395
396   fputc((tape.length >>  24) & 0xff,file);
397   fputc((tape.length >>  16) & 0xff,file);
398   fputc((tape.length >>   8) & 0xff,file);
399   fputc((tape.length >>   0) & 0xff,file);
400
401   for(i=0; i<tape.length; i++)
402   {
403     int j;
404
405     for(j=0; j<MAX_PLAYERS; j++)
406       fputc(tape.pos[i].action[j], file);
407
408     fputc(tape.pos[i].delay, file);
409   }
410
411   fclose(file);
412
413   chmod(filename, LEVREC_PERMS);
414
415   tape.changed = FALSE;
416
417   if (new_tape)
418     Request("tape saved !",REQ_CONFIRM);
419 }
420
421 void LoadScore(int level_nr)
422 {
423   int i;
424   char filename[MAX_FILENAME_LEN];
425   char cookie[MAX_FILENAME_LEN];
426   char line[MAX_LINE_LEN];
427   char *line_ptr;
428   FILE *file;
429
430   /* start with empty score table */
431   for(i=0; i<MAX_SCORE_ENTRIES; i++)
432   {
433     strcpy(highscore[i].Name, EMPTY_ALIAS);
434     highscore[i].Score = 0;
435   }
436
437   sprintf(filename, "%s/%d.%s",
438           getScoreDir(leveldir[leveldir_nr].filename),
439           level_nr, SCOREFILE_EXTENSION);
440
441   if (!(file = fopen(filename, "r")))
442     return;
443
444   fgets(cookie, SCORE_COOKIE_LEN, file);
445
446   if (strcmp(cookie, SCORE_COOKIE) != 0)
447   {
448     Error(ERR_WARN, "wrong format of score file '%s'", filename);
449     fclose(file);
450     return;
451   }
452
453   for(i=0; i<MAX_SCORE_ENTRIES; i++)
454   {
455     fscanf(file, "%d", &highscore[i].Score);
456     fgets(line, MAX_LINE_LEN, file);
457
458     if (line[strlen(line)-1] == '\n')
459       line[strlen(line)-1] = '\0';
460
461     for (line_ptr = line; *line_ptr; line_ptr++)
462     {
463       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
464       {
465         strncpy(highscore[i].Name, line_ptr, MAX_NAMELEN - 1);
466         highscore[i].Name[MAX_NAMELEN - 1] = '\0';
467         break;
468       }
469     }
470   }
471
472   fclose(file);
473 }
474
475 void SaveScore(int level_nr)
476 {
477   int i;
478   char filename[MAX_FILENAME_LEN];
479   FILE *file;
480
481   sprintf(filename, "%s/%d.%s",
482           getScoreDir(leveldir[leveldir_nr].filename),
483           level_nr, SCOREFILE_EXTENSION);
484
485   InitScoreDirectory(leveldir[leveldir_nr].filename);
486
487   if (!(file = fopen(filename, "w")))
488   {
489     Error(ERR_WARN, "cannot save score for level %d", level_nr);
490     return;
491   }
492
493   fprintf(file, "%s\n\n", SCORE_COOKIE);
494
495   for(i=0; i<MAX_SCORE_ENTRIES; i++)
496     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
497
498   fclose(file);
499
500   chmod(filename, SCORE_PERMS);
501 }
502
503 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
504 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
505 #define TOKEN_STR_PLAYER_PREFIX         "player_"
506
507 #define TOKEN_VALUE_POSITION            30
508
509 /* global setup */
510 #define SETUP_TOKEN_PLAYER_NAME         0
511 #define SETUP_TOKEN_SOUND               1
512 #define SETUP_TOKEN_SOUND_LOOPS         2
513 #define SETUP_TOKEN_SOUND_MUSIC         3
514 #define SETUP_TOKEN_SOUND_SIMPLE        4
515 #define SETUP_TOKEN_TOONS               5
516 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
517 #define SETUP_TOKEN_SCROLL_DELAY        7
518 #define SETUP_TOKEN_SOFT_SCROLLING      8
519 #define SETUP_TOKEN_FADING              9
520 #define SETUP_TOKEN_AUTORECORD          10
521 #define SETUP_TOKEN_QUICK_DOORS         11
522 #define SETUP_TOKEN_TEAM_MODE           12
523
524 /* player setup */
525 #define SETUP_TOKEN_USE_JOYSTICK        13
526 #define SETUP_TOKEN_JOY_DEVICE_NAME     14
527 #define SETUP_TOKEN_JOY_XLEFT           15
528 #define SETUP_TOKEN_JOY_XMIDDLE         16
529 #define SETUP_TOKEN_JOY_XRIGHT          17
530 #define SETUP_TOKEN_JOY_YUPPER          18
531 #define SETUP_TOKEN_JOY_YMIDDLE         19
532 #define SETUP_TOKEN_JOY_YLOWER          20
533 #define SETUP_TOKEN_JOY_SNAP            21
534 #define SETUP_TOKEN_JOY_BOMB            22
535 #define SETUP_TOKEN_KEY_LEFT            23
536 #define SETUP_TOKEN_KEY_RIGHT           24
537 #define SETUP_TOKEN_KEY_UP              25
538 #define SETUP_TOKEN_KEY_DOWN            26
539 #define SETUP_TOKEN_KEY_SNAP            27
540 #define SETUP_TOKEN_KEY_BOMB            28
541
542 /* level directory info */
543 #define LEVELINFO_TOKEN_NAME            29
544 #define LEVELINFO_TOKEN_LEVELS          30
545 #define LEVELINFO_TOKEN_SORT_PRIORITY   31
546 #define LEVELINFO_TOKEN_READONLY        32
547
548 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
549 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_TEAM_MODE
550
551 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
552 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
553
554 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
555 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
556
557 #define TYPE_BOOLEAN                    1
558 #define TYPE_SWITCH                     2
559 #define TYPE_KEYSYM                     3
560 #define TYPE_INTEGER                    4
561 #define TYPE_STRING                     5
562
563 static struct SetupInfo si;
564 static struct SetupInputInfo sii;
565 struct LevelDirInfo ldi;
566 static struct
567 {
568   int type;
569   void *value;
570   char *text;
571 } token_info[] =
572 {
573   /* global setup */
574   { TYPE_STRING,  &si.player_name,      "player_name"                   },
575   { TYPE_SWITCH,  &si.sound,            "sound"                         },
576   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
577   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
578   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
579   { TYPE_SWITCH,  &si.toons,            "toons"                         },
580   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
581   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
582   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
583   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
584   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
585   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
586   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
587
588   /* player setup */
589   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
590   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
591   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
592   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
593   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
594   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
595   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
596   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
597   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
598   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
599   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
600   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
601   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
602   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
603   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
604   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               },
605
606   /* level directory info */
607   { TYPE_STRING,  &ldi.name,            "name"                          },
608   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
609   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
610   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
611 };
612
613 static char *string_tolower(char *s)
614 {
615   static char s_lower[100];
616   int i;
617
618   if (strlen(s) >= 100)
619     return s;
620
621   strcpy(s_lower, s);
622
623   for (i=0; i<strlen(s_lower); i++)
624     s_lower[i] = tolower(s_lower[i]);
625
626   return s_lower;
627 }
628
629 static int get_string_integer_value(char *s)
630 {
631   static char *number_text[][3] =
632   {
633     { "0", "zero", "null", },
634     { "1", "one", "first" },
635     { "2", "two", "second" },
636     { "3", "three", "third" },
637     { "4", "four", "fourth" },
638     { "5", "five", "fifth" },
639     { "6", "six", "sixth" },
640     { "7", "seven", "seventh" },
641     { "8", "eight", "eighth" },
642     { "9", "nine", "ninth" },
643     { "10", "ten", "tenth" },
644     { "11", "eleven", "eleventh" },
645     { "12", "twelve", "twelfth" },
646   };
647
648   int i, j;
649
650   for (i=0; i<13; i++)
651     for (j=0; j<3; j++)
652       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
653         return i;
654
655   return atoi(s);
656 }
657
658 static boolean get_string_boolean_value(char *s)
659 {
660   if (strcmp(string_tolower(s), "true") == 0 ||
661       strcmp(string_tolower(s), "yes") == 0 ||
662       strcmp(string_tolower(s), "on") == 0 ||
663       get_string_integer_value(s) == 1)
664     return TRUE;
665   else
666     return FALSE;
667 }
668
669 static char *getFormattedSetupEntry(char *token, char *value)
670 {
671   int i;
672   static char entry[MAX_LINE_LEN];
673
674   sprintf(entry, "%s:", token);
675   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
676     entry[i] = ' ';
677   entry[i] = '\0';
678
679   strcat(entry, value);
680
681   return entry;
682 }
683
684 static void freeSetupFileList(struct SetupFileList *setup_file_list)
685 {
686   if (!setup_file_list)
687     return;
688
689   if (setup_file_list->token)
690     free(setup_file_list->token);
691   if (setup_file_list->value)
692     free(setup_file_list->value);
693   if (setup_file_list->next)
694     freeSetupFileList(setup_file_list->next);
695   free(setup_file_list);
696 }
697
698 static struct SetupFileList *newSetupFileList(char *token, char *value)
699 {
700   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
701
702   new->token = checked_malloc(strlen(token) + 1);
703   strcpy(new->token, token);
704
705   new->value = checked_malloc(strlen(value) + 1);
706   strcpy(new->value, value);
707
708   new->next = NULL;
709
710   return new;
711 }
712
713 static char *getTokenValue(struct SetupFileList *setup_file_list,
714                            char *token)
715 {
716   if (!setup_file_list)
717     return NULL;
718
719   if (strcmp(setup_file_list->token, token) == 0)
720     return setup_file_list->value;
721   else
722     return getTokenValue(setup_file_list->next, token);
723 }
724
725 static void setTokenValue(struct SetupFileList *setup_file_list,
726                           char *token, char *value)
727 {
728   if (!setup_file_list)
729     return;
730
731   if (strcmp(setup_file_list->token, token) == 0)
732   {
733     free(setup_file_list->value);
734     setup_file_list->value = checked_malloc(strlen(value) + 1);
735     strcpy(setup_file_list->value, value);
736   }
737   else if (setup_file_list->next == NULL)
738     setup_file_list->next = newSetupFileList(token, value);
739   else
740     setTokenValue(setup_file_list->next, token, value);
741 }
742
743 #ifdef DEBUG
744 static void printSetupFileList(struct SetupFileList *setup_file_list)
745 {
746   if (!setup_file_list)
747     return;
748
749   printf("token: '%s'\n", setup_file_list->token);
750   printf("value: '%s'\n", setup_file_list->value);
751
752   printSetupFileList(setup_file_list->next);
753 }
754 #endif
755
756 static struct SetupFileList *loadSetupFileList(char *filename)
757 {
758   int line_len;
759   char line[MAX_LINE_LEN];
760   char *token, *value, *line_ptr;
761   struct SetupFileList *setup_file_list = newSetupFileList("", "");
762   struct SetupFileList *first_valid_list_entry;
763
764   FILE *file;
765
766   if (!(file = fopen(filename, "r")))
767   {
768     Error(ERR_WARN, "cannot open setup/info file '%s'", filename);
769     return NULL;
770   }
771
772   while(!feof(file))
773   {
774     /* read next line of input file */
775     if (!fgets(line, MAX_LINE_LEN, file))
776       break;
777
778     /* cut trailing comment or whitespace from input line */
779     for (line_ptr = line; *line_ptr; line_ptr++)
780     {
781       if (*line_ptr == '#' || *line_ptr == '\n')
782       {
783         *line_ptr = '\0';
784         break;
785       }
786     }
787
788     /* cut trailing whitespaces from input line */
789     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
790       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
791         *line_ptr = '\0';
792
793     /* ignore empty lines */
794     if (*line == '\0')
795       continue;
796
797     line_len = strlen(line);
798
799     /* cut leading whitespaces from token */
800     for (token = line; *token; token++)
801       if (*token != ' ' && *token != '\t')
802         break;
803
804     /* find end of token */
805     for (line_ptr = token; *line_ptr; line_ptr++)
806     {
807       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
808       {
809         *line_ptr = '\0';
810         break;
811       }
812     }
813
814     if (line_ptr < line + line_len)
815       value = line_ptr + 1;
816     else
817       value = "\0";
818
819     /* cut leading whitespaces from value */
820     for (; *value; value++)
821       if (*value != ' ' && *value != '\t')
822         break;
823
824     if (*token && *value)
825       setTokenValue(setup_file_list, token, value);
826   }
827
828   fclose(file);
829
830   first_valid_list_entry = setup_file_list->next;
831
832   /* free empty list header */
833   setup_file_list->next = NULL;
834   freeSetupFileList(setup_file_list);
835
836   if (first_valid_list_entry == NULL)
837     Error(ERR_WARN, "setup/info file '%s' is empty", filename);
838
839   return first_valid_list_entry;
840 }
841
842 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
843                                          char *identifier)
844 {
845   if (!setup_file_list)
846     return;
847
848   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
849   {
850     if (strcmp(setup_file_list->value, identifier) != 0)
851     {
852       Error(ERR_WARN, "setup/info file has wrong version");
853       return;
854     }
855     else
856       return;
857   }
858
859   if (setup_file_list->next)
860     checkSetupFileListIdentifier(setup_file_list->next, identifier);
861   else
862   {
863     Error(ERR_WARN, "setup/info file has no version information");
864     return;
865   }
866 }
867
868 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
869 {
870   ldi->name = getStringCopy("non-existing");
871   ldi->levels = 0;
872   ldi->sort_priority = 999;     /* default: least priority */
873   ldi->readonly = TRUE;
874 }
875
876 static void setSetupInfoToDefaults(struct SetupInfo *si)
877 {
878   int i;
879
880   si->player_name = getStringCopy(getLoginName());
881
882   si->sound = TRUE;
883   si->sound_loops = FALSE;
884   si->sound_music = FALSE;
885   si->sound_simple = FALSE;
886   si->toons = TRUE;
887   si->double_buffering = TRUE;
888   si->direct_draw = !si->double_buffering;
889   si->scroll_delay = FALSE;
890   si->soft_scrolling = TRUE;
891   si->fading = FALSE;
892   si->autorecord = FALSE;
893   si->quick_doors = FALSE;
894
895   for (i=0; i<MAX_PLAYERS; i++)
896   {
897     si->input[i].use_joystick = FALSE;
898     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
899     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
900     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
901     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
902     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
903     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
904     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
905     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
906     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
907     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
908     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
909     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
910     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
911     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
912     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
913   }
914 }
915
916 static void setSetupInfo(int token_nr, char *token_value)
917 {
918   int token_type = token_info[token_nr].type;
919   void *setup_value = token_info[token_nr].value;
920
921   if (token_value == NULL)
922     return;
923
924   /* set setup field to corresponding token value */
925   switch (token_type)
926   {
927     case TYPE_BOOLEAN:
928     case TYPE_SWITCH:
929       *(boolean *)setup_value = get_string_boolean_value(token_value);
930       break;
931
932     case TYPE_KEYSYM:
933       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
934       break;
935
936     case TYPE_INTEGER:
937       *(int *)setup_value = get_string_integer_value(token_value);
938       break;
939
940     case TYPE_STRING:
941       if (*(char **)setup_value != NULL)
942         free(*(char **)setup_value);
943       *(char **)setup_value = getStringCopy(token_value);
944       break;
945
946     default:
947       break;
948   }
949 }
950
951 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
952 {
953   int i, pnr;
954
955   if (!setup_file_list)
956     return;
957
958   /* handle global setup values */
959   si = setup;
960   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
961     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
962   setup = si;
963
964   /* handle player specific setup values */
965   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
966   {
967     char prefix[30];
968
969     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
970
971     sii = setup.input[pnr];
972     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
973     {
974       char full_token[100];
975
976       sprintf(full_token, "%s%s", prefix, token_info[i].text);
977       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
978     }
979     setup.input[pnr] = sii;
980   }
981 }
982
983 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
984 {
985   int i;
986
987   if (!level_series_name)
988     return 0;
989
990   for (i=0; i<num_leveldirs; i++)
991     if (strcmp(level_series_name, leveldir[i].filename) == 0)
992       return i;
993
994   return 0;
995 }
996
997 int getLastPlayedLevelOfLevelSeries(char *level_series_name)
998 {
999   char *token_value;
1000   int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
1001   int last_level_nr = 0;
1002
1003   if (!level_series_name)
1004     return 0;
1005
1006   token_value = getTokenValue(level_setup_list, level_series_name);
1007
1008   if (token_value)
1009   {
1010     int highest_level_nr = leveldir[level_series_nr].levels - 1;
1011
1012     last_level_nr = atoi(token_value);
1013
1014     if (last_level_nr < 0)
1015       last_level_nr = 0;
1016     if (last_level_nr > highest_level_nr)
1017       last_level_nr = highest_level_nr;
1018   }
1019
1020   return last_level_nr;
1021 }
1022
1023 void LoadLevelInfo()
1024 {
1025   DIR *dir;
1026   struct stat file_status;
1027   char *level_directory = options.level_directory;
1028   char *directory = NULL;
1029   char *filename = NULL;
1030   struct SetupFileList *setup_file_list = NULL;
1031   struct dirent *dir_entry;
1032   int i, num_entries = 0;
1033
1034   if ((dir = opendir(level_directory)) == NULL)
1035     Error(ERR_EXIT, "cannot read level directory '%s'", level_directory);
1036
1037   while (num_entries < MAX_LEVDIR_ENTRIES)
1038   {
1039     if ((dir_entry = readdir(dir)) == NULL)     /* last directory entry */
1040       break;
1041
1042     /* skip entries for current and parent directory */
1043     if (strcmp(dir_entry->d_name, ".")  == 0 ||
1044         strcmp(dir_entry->d_name, "..") == 0)
1045       continue;
1046
1047     /* find out if directory entry is itself a directory */
1048     directory = getPath2(level_directory, dir_entry->d_name);
1049     if (stat(directory, &file_status) != 0 ||           /* cannot stat file */
1050         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
1051     {
1052       free(directory);
1053       continue;
1054     }
1055
1056     if (strlen(dir_entry->d_name) >= MAX_LEVDIR_FILENAME)
1057     {
1058       Error(ERR_WARN, "filename of level directory '%s' too long -- ignoring",
1059             dir_entry->d_name);
1060       continue;
1061     }
1062
1063     filename = getPath2(directory, LEVELINFO_FILENAME);
1064     setup_file_list = loadSetupFileList(filename);
1065
1066     if (setup_file_list)
1067     {
1068       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1069       setLevelDirInfoToDefaults(&leveldir[num_entries]);
1070
1071       ldi = leveldir[num_entries];
1072       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1073         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1074       leveldir[num_entries] = ldi;
1075
1076       leveldir[num_entries].filename = getStringCopy(dir_entry->d_name);
1077
1078       freeSetupFileList(setup_file_list);
1079       num_entries++;
1080     }
1081     else
1082       Error(ERR_WARN, "ignoring level directory '%s'", directory);
1083
1084     free(directory);
1085     free(filename);
1086   }
1087
1088   if (num_entries == MAX_LEVDIR_ENTRIES)
1089     Error(ERR_WARN, "using %d level directories -- ignoring the rest",
1090           num_entries);
1091
1092   closedir(dir);
1093
1094   num_leveldirs = num_entries;
1095   leveldir_nr = 0;
1096
1097   if (!num_leveldirs)
1098     Error(ERR_EXIT, "cannot find any valid level series in directory '%s'",
1099           level_directory);
1100 }
1101
1102 void LoadSetup()
1103 {
1104   char filename[MAX_FILENAME_LEN];
1105   struct SetupFileList *setup_file_list = NULL;
1106
1107   /* always start with reliable default setup values */
1108   setSetupInfoToDefaults(&setup);
1109
1110   sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1111
1112   setup_file_list = loadSetupFileList(filename);
1113
1114   if (setup_file_list)
1115   {
1116     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1117     decodeSetupFileList(setup_file_list);
1118
1119     setup.direct_draw = !setup.double_buffering;
1120
1121     freeSetupFileList(setup_file_list);
1122
1123     /* needed to work around problems with fixed length strings */
1124     if (strlen(setup.player_name) >= MAX_NAMELEN)
1125       setup.player_name[MAX_NAMELEN - 1] = '\0';
1126     else if (strlen(setup.player_name) < MAX_NAMELEN - 1)
1127     {
1128       char *new_name = checked_malloc(MAX_NAMELEN);
1129
1130       strcpy(new_name, setup.player_name);
1131       free(setup.player_name);
1132       setup.player_name = new_name;
1133     }
1134   }
1135   else
1136     Error(ERR_WARN, "using default setup values");
1137 }
1138
1139 static char *getSetupLine(char *prefix, int token_nr)
1140 {
1141   int i;
1142   static char entry[MAX_LINE_LEN];
1143   int token_type = token_info[token_nr].type;
1144   void *setup_value = token_info[token_nr].value;
1145   char *token_text = token_info[token_nr].text;
1146
1147   /* start with the prefix, token and some spaces to format output line */
1148   sprintf(entry, "%s%s:", prefix, token_text);
1149   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1150     strcat(entry, " ");
1151
1152   /* continue with the token's value (which can have different types) */
1153   switch (token_type)
1154   {
1155     case TYPE_BOOLEAN:
1156       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1157       break;
1158
1159     case TYPE_SWITCH:
1160       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1161       break;
1162
1163     case TYPE_KEYSYM:
1164       {
1165         KeySym keysym = *(KeySym *)setup_value;
1166         char *keyname = getKeyNameFromKeySym(keysym);
1167
1168         strcat(entry, getX11KeyNameFromKeySym(keysym));
1169         for (i=strlen(entry); i<50; i++)
1170           strcat(entry, " ");
1171
1172         /* add comment, if useful */
1173         if (strcmp(keyname, "(undefined)") != 0 &&
1174             strcmp(keyname, "(unknown)") != 0)
1175         {
1176           strcat(entry, "# ");
1177           strcat(entry, keyname);
1178         }
1179       }
1180       break;
1181
1182     case TYPE_INTEGER:
1183       {
1184         char buffer[MAX_LINE_LEN];
1185
1186         sprintf(buffer, "%d", *(int *)setup_value);
1187         strcat(entry, buffer);
1188       }
1189       break;
1190
1191     case TYPE_STRING:
1192       strcat(entry, *(char **)setup_value);
1193       break;
1194
1195     default:
1196       break;
1197   }
1198
1199   return entry;
1200 }
1201
1202 void SaveSetup()
1203 {
1204   int i, pnr;
1205   char filename[MAX_FILENAME_LEN];
1206   FILE *file;
1207
1208   sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1209
1210   InitUserDataDirectory();
1211
1212   if (!(file = fopen(filename, "w")))
1213   {
1214     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1215     return;
1216   }
1217
1218   fprintf(file, "%s\n",
1219           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1220   fprintf(file, "\n");
1221
1222   /* handle global setup values */
1223   si = setup;
1224   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1225   {
1226     fprintf(file, "%s\n", getSetupLine("", i));
1227
1228     /* just to make things nicer :) */
1229     if (i == SETUP_TOKEN_PLAYER_NAME)
1230       fprintf(file, "\n");
1231   }
1232
1233   /* handle player specific setup values */
1234   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1235   {
1236     char prefix[30];
1237
1238     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1239     fprintf(file, "\n");
1240
1241     sii = setup.input[pnr];
1242     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1243       fprintf(file, "%s\n", getSetupLine(prefix, i));
1244   }
1245
1246   fclose(file);
1247
1248   chmod(filename, SETUP_PERMS);
1249 }
1250
1251 void LoadLevelSetup()
1252 {
1253   char filename[MAX_FILENAME_LEN];
1254
1255   /* always start with reliable default setup values */
1256
1257   leveldir_nr = 0;
1258   level_nr = 0;
1259
1260   sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1261
1262   if (level_setup_list)
1263     freeSetupFileList(level_setup_list);
1264
1265   level_setup_list = loadSetupFileList(filename);
1266
1267   if (level_setup_list)
1268   {
1269     char *last_level_series =
1270       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1271
1272     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1273     level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
1274
1275     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1276   }
1277   else
1278   {
1279     level_setup_list = newSetupFileList(TOKEN_STR_FILE_IDENTIFIER,
1280                                         LEVELSETUP_COOKIE);
1281     Error(ERR_WARN, "using default setup values");
1282   }
1283 }
1284
1285 void SaveLevelSetup()
1286 {
1287   char filename[MAX_FILENAME_LEN];
1288   struct SetupFileList *list_entry = level_setup_list;
1289   FILE *file;
1290
1291   setTokenValue(level_setup_list,
1292                 TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
1293
1294   setTokenValue(level_setup_list,
1295                 leveldir[leveldir_nr].filename, int2str(level_nr, 0));
1296
1297   sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1298
1299   InitUserDataDirectory();
1300
1301   if (!(file = fopen(filename, "w")))
1302   {
1303     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1304     return;
1305   }
1306
1307   fprintf(file, "%s:              %s\n\n",
1308           TOKEN_STR_FILE_IDENTIFIER, LEVELSETUP_COOKIE);
1309
1310   while (list_entry)
1311   {
1312     if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1313       fprintf(file, "%s\n",
1314               getFormattedSetupEntry(list_entry->token, list_entry->value));
1315
1316     /* just to make things nicer :) */
1317     if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1318       fprintf(file, "\n");
1319
1320     list_entry = list_entry->next;
1321   }
1322
1323   fclose(file);
1324
1325   chmod(filename, SETUP_PERMS);
1326 }