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