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