rnd-19981105-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 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 (levelrec_10)
276     {
277       /* eliminate possible diagonal moves in old tapes */
278       /* this is only for backward compatibility */
279
280       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
281       byte action = tape.pos[i].action[0];
282       int k, num_moves = 0;
283
284       for (k=0; k<4; k++)
285       {
286         if (action & joy_dir[k])
287         {
288           tape.pos[i + num_moves].action[0] = joy_dir[k];
289           if (num_moves > 0)
290             tape.pos[i + num_moves].delay = 0;
291           num_moves++;
292         }
293       }
294
295       if (num_moves > 1)
296       {
297         num_moves--;
298         i += num_moves;
299         tape.length += num_moves;
300       }
301     }
302
303     if (feof(file))
304       break;
305   }
306
307   fclose(file);
308
309   if (i != tape.length)
310     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
311
312   tape.length_seconds = GetTapeLength();
313 }
314
315 void SaveLevelTape(int level_nr)
316 {
317   int i;
318   char filename[MAX_FILENAME_LEN];
319   FILE *file;
320   boolean new_tape = TRUE;
321
322 #ifndef MSDOS
323   sprintf(filename,"%s/%s/%d.tape",
324           level_directory,leveldir[leveldir_nr].filename,level_nr);
325 #else
326   sprintf(filename,"%s/%s/%d.tap",
327           level_directory,leveldir[leveldir_nr].filename,level_nr);
328 #endif
329
330   /* Testen, ob bereits eine Aufnahme existiert */
331   if ((file=fopen(filename,"r")))
332   {
333     new_tape = FALSE;
334     fclose(file);
335
336     if (!Request("Replace old tape ?",REQ_ASK))
337       return;
338   }
339
340   if (!(file=fopen(filename,"w")))
341   {
342     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
343     return;
344   }
345
346   fputs(LEVELREC_COOKIE,file);          /* Formatkennung */
347   fputc(0x0a,file);
348
349   fputc((tape.random_seed >> 24) & 0xff,file);
350   fputc((tape.random_seed >> 16) & 0xff,file);
351   fputc((tape.random_seed >>  8) & 0xff,file);
352   fputc((tape.random_seed >>  0) & 0xff,file);
353
354   fputc((tape.date >>  24) & 0xff,file);
355   fputc((tape.date >>  16) & 0xff,file);
356   fputc((tape.date >>   8) & 0xff,file);
357   fputc((tape.date >>   0) & 0xff,file);
358
359   fputc((tape.length >>  24) & 0xff,file);
360   fputc((tape.length >>  16) & 0xff,file);
361   fputc((tape.length >>   8) & 0xff,file);
362   fputc((tape.length >>   0) & 0xff,file);
363
364   for(i=0;i<tape.length;i++)
365   {
366     int j;
367
368     for(j=0; j<MAX_PLAYERS; j++)
369       fputc(tape.pos[i].action[j],file);
370
371     fputc(tape.pos[i].delay,file);
372   }
373
374   fclose(file);
375
376   chmod(filename, LEVREC_PERMS);
377
378   tape.changed = FALSE;
379
380   if (new_tape)
381     Request("tape saved !",REQ_CONFIRM);
382 }
383
384 boolean CreateNewScoreFile()
385 {
386   int i,j,k;
387   char filename[MAX_FILENAME_LEN];
388   char empty_alias[MAX_NAMELEN];
389   FILE *file;
390
391   sprintf(filename,"%s/%s/%s",
392           level_directory,leveldir[leveldir_nr].filename,SCORE_FILENAME);
393
394   if (!(file=fopen(filename,"w")))
395     return(FALSE);
396
397   for(i=0;i<MAX_NAMELEN;i++)
398     empty_alias[i] = 0;
399   strncpy(empty_alias,EMPTY_ALIAS,MAX_NAMELEN-1);
400
401   fputs(SCORE_COOKIE,file);             /* Formatkennung */
402   for(i=0;i<leveldir[leveldir_nr].levels;i++)
403   {
404     for(j=0;j<MAX_SCORE_ENTRIES;j++)
405     {
406       for(k=0;k<MAX_NAMELEN;k++)
407         fputc(empty_alias[k],file);
408       fputc(0,file);
409       fputc(0,file);
410     }
411   }
412   fclose(file);
413
414   chmod(filename, SCORE_PERMS);
415   return(TRUE);
416 }
417
418 void LoadScore(int level_nr)
419 {
420   int i,j;
421   char filename[MAX_FILENAME_LEN];
422   char cookie[MAX_FILENAME_LEN];
423   FILE *file;
424
425   sprintf(filename,"%s/%s/%s",
426           level_directory,leveldir[leveldir_nr].filename,SCORE_FILENAME);
427
428   if (!(file = fopen(filename,"r")))
429   {
430     if (!CreateNewScoreFile())
431       Error(ERR_WARN, "cannot create score file '%s'", filename);
432     else if (!(file = fopen(filename,"r"))) 
433       Error(ERR_WARN, "cannot read score for level %d", level_nr);
434   }
435
436   if (file)
437   {
438     fgets(cookie,SCORE_COOKIE_LEN,file);
439     if (strcmp(cookie,SCORE_COOKIE))    /* ungültiges Format? */
440     {
441       Error(ERR_WARN, "wrong format of score file '%s'", filename);
442       fclose(file);
443       file = NULL;
444     }
445   }
446
447   if (file)
448   {
449     fseek(file,
450           SCORE_COOKIE_LEN-1+level_nr*(MAX_SCORE_ENTRIES*(MAX_NAMELEN+2)),
451           SEEK_SET);
452     for(i=0;i<MAX_SCORE_ENTRIES;i++)
453     {
454       for(j=0;j<MAX_NAMELEN;j++)
455         highscore[i].Name[j] = fgetc(file);
456       highscore[i].Score = (fgetc(file)<<8) | fgetc(file);
457     }
458     fclose(file);
459   }
460   else
461   {
462     for(i=0;i<MAX_SCORE_ENTRIES;i++)
463     {
464       strcpy(highscore[i].Name,EMPTY_ALIAS);
465       highscore[i].Score = 0;
466     }
467   }
468 }
469
470 void SaveScore(int level_nr)
471 {
472   int i,j;
473   char filename[MAX_FILENAME_LEN];
474   FILE *file;
475
476   sprintf(filename,"%s/%s/%s",
477           level_directory,leveldir[leveldir_nr].filename,SCORE_FILENAME);
478
479   if (!(file=fopen(filename,"r+")))
480   {
481     Error(ERR_WARN, "cannot save score for level %d", level_nr);
482     return;
483   }
484
485   fseek(file,
486         SCORE_COOKIE_LEN-1+level_nr*(MAX_SCORE_ENTRIES*(MAX_NAMELEN+2)),
487         SEEK_SET);
488   for(i=0;i<MAX_SCORE_ENTRIES;i++)
489   {
490     for(j=0;j<MAX_NAMELEN;j++)
491       fputc(highscore[i].Name[j],file);
492     fputc(highscore[i].Score / 256,file);
493     fputc(highscore[i].Score % 256,file);
494   }
495   fclose(file);
496 }
497
498 #define MAX_LINE_LEN                    1000
499
500 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
501 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
502 #define TOKEN_STR_PLAYER_PREFIX         "player_"
503
504 #define TOKEN_VALUE_POSITION            30
505
506 #define SETUP_TOKEN_SOUND               0
507 #define SETUP_TOKEN_SOUND_LOOPS         1
508 #define SETUP_TOKEN_SOUND_MUSIC         2
509 #define SETUP_TOKEN_SOUND_SIMPLE        3
510 #define SETUP_TOKEN_TOONS               4
511 #define SETUP_TOKEN_DOUBLE_BUFFERING    5
512 #define SETUP_TOKEN_SCROLL_DELAY        6
513 #define SETUP_TOKEN_SOFT_SCROLLING      7
514 #define SETUP_TOKEN_FADING              8
515 #define SETUP_TOKEN_AUTORECORD          9
516 #define SETUP_TOKEN_QUICK_DOORS         10
517 #define SETUP_TOKEN_TEAM_MODE           11
518 #define SETUP_TOKEN_ALIAS_NAME          12
519
520 #define SETUP_TOKEN_USE_JOYSTICK        13
521 #define SETUP_TOKEN_JOY_DEVICE_NAME     14
522 #define SETUP_TOKEN_JOY_XLEFT           15
523 #define SETUP_TOKEN_JOY_XMIDDLE         16
524 #define SETUP_TOKEN_JOY_XRIGHT          17
525 #define SETUP_TOKEN_JOY_YUPPER          18
526 #define SETUP_TOKEN_JOY_YMIDDLE         19
527 #define SETUP_TOKEN_JOY_YLOWER          20
528 #define SETUP_TOKEN_JOY_SNAP            21
529 #define SETUP_TOKEN_JOY_BOMB            22
530 #define SETUP_TOKEN_KEY_LEFT            23
531 #define SETUP_TOKEN_KEY_RIGHT           24
532 #define SETUP_TOKEN_KEY_UP              25
533 #define SETUP_TOKEN_KEY_DOWN            26
534 #define SETUP_TOKEN_KEY_SNAP            27
535 #define SETUP_TOKEN_KEY_BOMB            28
536
537 #define NUM_SETUP_TOKENS                29
538
539 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_SOUND
540 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_ALIAS_NAME
541
542 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
543 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
544
545 #define TYPE_BOOLEAN                    1
546 #define TYPE_SWITCH                     2
547 #define TYPE_KEYSYM                     3
548 #define TYPE_INTEGER                    4
549 #define TYPE_STRING                     5
550
551 static struct SetupInfo si;
552 static struct SetupInputInfo sii;
553 static struct
554 {
555   int type;
556   void *value;
557   char *text;
558 } token_info[] =
559 {
560   { TYPE_SWITCH,  &si.sound,            "sound"                         },
561   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
562   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
563   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
564   { TYPE_SWITCH,  &si.toons,            "toons"                         },
565   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
566   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
567   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
568   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
569   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
570   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
571   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
572   { TYPE_STRING,  &si.alias_name,       "alias_name"                    },
573
574   /* for each player: */
575   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
576   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
577   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
578   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
579   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
580   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
581   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
582   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
583   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
584   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
585   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
586   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
587   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
588   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
589   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
590   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               }
591 };
592
593 static char *string_tolower(char *s)
594 {
595   static char s_lower[100];
596   int i;
597
598   if (strlen(s) >= 100)
599     return s;
600
601   strcpy(s_lower, s);
602
603   for (i=0; i<strlen(s_lower); i++)
604     s_lower[i] = tolower(s_lower[i]);
605
606   return s_lower;
607 }
608
609 static int get_string_integer_value(char *s)
610 {
611   static char *number_text[][3] =
612   {
613     { "0", "zero", "null", },
614     { "1", "one", "first" },
615     { "2", "two", "second" },
616     { "3", "three", "third" },
617     { "4", "four", "fourth" },
618     { "5", "five", "fifth" },
619     { "6", "six", "sixth" },
620     { "7", "seven", "seventh" },
621     { "8", "eight", "eighth" },
622     { "9", "nine", "ninth" },
623     { "10", "ten", "tenth" },
624     { "11", "eleven", "eleventh" },
625     { "12", "twelve", "twelfth" },
626   };
627
628   int i, j;
629
630   for (i=0; i<13; i++)
631     for (j=0; j<3; j++)
632       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
633         return i;
634
635   return atoi(s);
636 }
637
638 static boolean get_string_boolean_value(char *s)
639 {
640   if (strcmp(string_tolower(s), "true") == 0 ||
641       strcmp(string_tolower(s), "yes") == 0 ||
642       strcmp(string_tolower(s), "on") == 0 ||
643       get_string_integer_value(s) == 1)
644     return TRUE;
645   else
646     return FALSE;
647 }
648
649 static char *getFormattedSetupEntry(char *token, char *value)
650 {
651   int i;
652   static char entry[MAX_LINE_LEN];
653
654   sprintf(entry, "%s:", token);
655   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
656     entry[i] = ' ';
657   entry[i] = '\0';
658
659   strcat(entry, value);
660
661   return entry;
662 }
663
664 static void freeSetupFileList(struct SetupFileList *setup_file_list)
665 {
666   if (!setup_file_list)
667     return;
668
669   if (setup_file_list->token)
670     free(setup_file_list->token);
671   if (setup_file_list->value)
672     free(setup_file_list->value);
673   if (setup_file_list->next)
674     freeSetupFileList(setup_file_list->next);
675   free(setup_file_list);
676 }
677
678 static struct SetupFileList *newSetupFileList(char *token, char *value)
679 {
680   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
681
682   new->token = checked_malloc(strlen(token) + 1);
683   strcpy(new->token, token);
684
685   new->value = checked_malloc(strlen(value) + 1);
686   strcpy(new->value, value);
687
688   new->next = NULL;
689
690   return new;
691 }
692
693 static char *getTokenValue(struct SetupFileList *setup_file_list,
694                            char *token)
695 {
696   if (!setup_file_list)
697     return NULL;
698
699   if (strcmp(setup_file_list->token, token) == 0)
700     return setup_file_list->value;
701   else
702     return getTokenValue(setup_file_list->next, token);
703 }
704
705 static void setTokenValue(struct SetupFileList *setup_file_list,
706                           char *token, char *value)
707 {
708   if (!setup_file_list)
709     return;
710
711   if (strcmp(setup_file_list->token, token) == 0)
712   {
713     free(setup_file_list->value);
714     setup_file_list->value = checked_malloc(strlen(value) + 1);
715     strcpy(setup_file_list->value, value);
716   }
717   else if (setup_file_list->next == NULL)
718     setup_file_list->next = newSetupFileList(token, value);
719   else
720     setTokenValue(setup_file_list->next, token, value);
721 }
722
723 #ifdef DEBUG
724 static void printSetupFileList(struct SetupFileList *setup_file_list)
725 {
726   if (!setup_file_list)
727     return;
728
729   printf("token: '%s'\n", setup_file_list->token);
730   printf("value: '%s'\n", setup_file_list->value);
731
732   printSetupFileList(setup_file_list->next);
733 }
734 #endif
735
736 static struct SetupFileList *loadSetupFileList(char *filename)
737 {
738   int line_len;
739   char line[MAX_LINE_LEN];
740   char *token, *value, *line_ptr;
741   struct SetupFileList *setup_file_list = newSetupFileList("", "");
742   struct SetupFileList *first_valid_list_entry;
743
744   FILE *file;
745
746   if (!(file = fopen(filename, "r")))
747   {
748     Error(ERR_WARN, "cannot open setup file '%s'", filename);
749     return NULL;
750   }
751
752   while(!feof(file))
753   {
754     /* read next line of input file */
755     if (!fgets(line, MAX_LINE_LEN, file))
756       break;
757
758     /* cut trailing comment or whitespace from input line */
759     for (line_ptr = line; *line_ptr; line_ptr++)
760     {
761       if (*line_ptr == '#' || *line_ptr == '\n')
762       {
763         *line_ptr = '\0';
764         break;
765       }
766     }
767
768     /* cut trailing whitespaces from input line */
769     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
770       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
771         *line_ptr = '\0';
772
773     /* ignore empty lines */
774     if (*line == '\0')
775       continue;
776
777     line_len = strlen(line);
778
779     /* cut leading whitespaces from token */
780     for (token = line; *token; token++)
781       if (*token != ' ' && *token != '\t')
782         break;
783
784     /* find end of token */
785     for (line_ptr = token; *line_ptr; line_ptr++)
786     {
787       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
788       {
789         *line_ptr = '\0';
790         break;
791       }
792     }
793
794     if (line_ptr < line + line_len)
795       value = line_ptr + 1;
796     else
797       value = "\0";
798
799     /* cut leading whitespaces from value */
800     for (; *value; value++)
801       if (*value != ' ' && *value != '\t')
802         break;
803
804     if (*token && *value)
805       setTokenValue(setup_file_list, token, value);
806   }
807
808   fclose(file);
809
810   first_valid_list_entry = setup_file_list->next;
811
812   /* free empty list header */
813   setup_file_list->next = NULL;
814   freeSetupFileList(setup_file_list);
815
816   if (!first_valid_list_entry)
817     Error(ERR_WARN, "setup file is empty");
818
819   return first_valid_list_entry;
820 }
821
822 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
823                                          char *identifier)
824 {
825   if (!setup_file_list)
826     return;
827
828   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
829   {
830     if (strcmp(setup_file_list->value, identifier) != 0)
831     {
832       Error(ERR_WARN, "setup file has wrong version");
833       return;
834     }
835     else
836       return;
837   }
838
839   if (setup_file_list->next)
840     checkSetupFileListIdentifier(setup_file_list->next, identifier);
841   else
842   {
843     Error(ERR_WARN, "setup file has no version information");
844     return;
845   }
846 }
847
848 static void setSetupInfoToDefaults(struct SetupInfo *si)
849 {
850   int i;
851
852   si->sound = TRUE;
853   si->sound_loops = FALSE;
854   si->sound_music = FALSE;
855   si->sound_simple = FALSE;
856   si->toons = TRUE;
857   si->double_buffering = TRUE;
858   si->direct_draw = !si->double_buffering;
859   si->scroll_delay = FALSE;
860   si->soft_scrolling = TRUE;
861   si->fading = FALSE;
862   si->autorecord = FALSE;
863   si->quick_doors = FALSE;
864
865   strncpy(si->login_name, GetLoginName(), MAX_NAMELEN-1);
866   si->login_name[MAX_NAMELEN-1] = '\0';
867   strncpy(si->alias_name, GetLoginName(), MAX_NAMELEN-1);
868   si->alias_name[MAX_NAMELEN-1] = '\0';
869
870   for (i=0; i<MAX_PLAYERS; i++)
871   {
872     si->input[i].use_joystick = FALSE;
873     strcpy(si->input[i].joy.device_name, joystick_device_name[i]);
874     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
875     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
876     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
877     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
878     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
879     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
880     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
881     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
882     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
883     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
884     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
885     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
886     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
887     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
888   }
889 }
890
891 static void setSetupInfo(int token_nr, char *token_value)
892 {
893   int token_type = token_info[token_nr].type;
894   void *setup_value = token_info[token_nr].value;
895
896   if (token_value == NULL)
897     return;
898
899   /* set setup field to corresponding token value */
900   switch (token_type)
901   {
902     case TYPE_BOOLEAN:
903     case TYPE_SWITCH:
904       *(boolean *)setup_value = get_string_boolean_value(token_value);
905       break;
906
907     case TYPE_KEYSYM:
908       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
909       break;
910
911     case TYPE_INTEGER:
912       *(int *)setup_value = get_string_integer_value(token_value);
913       break;
914
915     case TYPE_STRING:
916       strcpy((char *)setup_value, token_value);
917       break;
918
919     default:
920       break;
921   }
922 }
923
924 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
925 {
926   int i, pnr;
927
928   if (!setup_file_list)
929     return;
930
931   /* handle global setup values */
932   si = setup;
933   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
934     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
935   setup = si;
936
937   /* handle player specific setup values */
938   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
939   {
940     char prefix[30];
941
942     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
943
944     sii = setup.input[pnr];
945     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
946     {
947       char full_token[100];
948
949       sprintf(full_token, "%s%s", prefix, token_info[i].text);
950       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
951     }
952     setup.input[pnr] = sii;
953   }
954 }
955
956 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
957 {
958   int i;
959
960   if (!level_series_name)
961     return 0;
962
963   for (i=0; i<num_leveldirs; i++)
964     if (strcmp(level_series_name, leveldir[i].filename) == 0)
965       return i;
966
967   return 0;
968 }
969
970 int getLastPlayedLevelOfLevelSeries(char *level_series_name)
971 {
972   char *token_value;
973   int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
974   int last_level_nr = 0;
975
976   if (!level_series_name)
977     return 0;
978
979   token_value = getTokenValue(level_setup_list, level_series_name);
980
981   if (token_value)
982   {
983     int highest_level_nr = leveldir[level_series_nr].levels - 1;
984
985     last_level_nr = atoi(token_value);
986
987     if (last_level_nr < 0)
988       last_level_nr = 0;
989     if (last_level_nr > highest_level_nr)
990       last_level_nr = highest_level_nr;
991   }
992
993   return last_level_nr;
994 }
995
996 void LoadSetup()
997 {
998   char filename[MAX_FILENAME_LEN];
999   struct SetupFileList *setup_file_list = NULL;
1000
1001   /* always start with reliable default setup values */
1002   setSetupInfoToDefaults(&setup);
1003
1004   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1005
1006   setup_file_list = loadSetupFileList(filename);
1007
1008   if (setup_file_list)
1009   {
1010     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1011     decodeSetupFileList(setup_file_list);
1012
1013     setup.direct_draw = !setup.double_buffering;
1014
1015     freeSetupFileList(setup_file_list);
1016   }
1017   else
1018     Error(ERR_WARN, "using default setup values");
1019 }
1020
1021 static char *getSetupLine(char *prefix, int token_nr)
1022 {
1023   int i;
1024   static char entry[MAX_LINE_LEN];
1025   int token_type = token_info[token_nr].type;
1026   void *setup_value = token_info[token_nr].value;
1027   char *token_text = token_info[token_nr].text;
1028
1029   /* start with the prefix, token and some spaces to format output line */
1030   sprintf(entry, "%s%s:", prefix, token_text);
1031   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1032     strcat(entry, " ");
1033
1034   /* continue with the token's value (which can have different types) */
1035   switch (token_type)
1036   {
1037     case TYPE_BOOLEAN:
1038       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1039       break;
1040
1041     case TYPE_SWITCH:
1042       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1043       break;
1044
1045     case TYPE_KEYSYM:
1046       {
1047         KeySym keysym = *(KeySym *)setup_value;
1048         char *keyname = getKeyNameFromKeySym(keysym);
1049
1050         strcat(entry, getX11KeyNameFromKeySym(keysym));
1051         for (i=strlen(entry); i<50; i++)
1052           strcat(entry, " ");
1053
1054         /* add comment, if useful */
1055         if (strcmp(keyname, "(undefined)") != 0 &&
1056             strcmp(keyname, "(unknown)") != 0)
1057         {
1058           strcat(entry, "# ");
1059           strcat(entry, keyname);
1060         }
1061       }
1062       break;
1063
1064     case TYPE_INTEGER:
1065       {
1066         char buffer[MAX_LINE_LEN];
1067
1068         sprintf(buffer, "%d", *(int *)setup_value);
1069         strcat(entry, buffer);
1070       }
1071       break;
1072
1073     case TYPE_STRING:
1074       strcat(entry, (char *)setup_value);
1075       break;
1076
1077     default:
1078       break;
1079   }
1080
1081   return entry;
1082 }
1083
1084 void SaveSetup()
1085 {
1086   int i, pnr;
1087   char filename[MAX_FILENAME_LEN];
1088   FILE *file;
1089
1090   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1091
1092   if (!(file = fopen(filename, "w")))
1093   {
1094     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1095     return;
1096   }
1097
1098   fprintf(file, "%s\n",
1099           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1100   fprintf(file, "\n");
1101
1102   /* handle global setup values */
1103   si = setup;
1104   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1105   {
1106     /* just to make things nicer :) */
1107     if (i == SETUP_TOKEN_ALIAS_NAME)
1108       fprintf(file, "\n");
1109
1110     fprintf(file, "%s\n", getSetupLine("", i));
1111   }
1112
1113   /* handle player specific setup values */
1114   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1115   {
1116     char prefix[30];
1117
1118     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1119     fprintf(file, "\n");
1120
1121     sii = setup.input[pnr];
1122     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1123       fprintf(file, "%s\n", getSetupLine(prefix, i));
1124   }
1125
1126   fclose(file);
1127
1128   chmod(filename, SETUP_PERMS);
1129 }
1130
1131 void LoadLevelSetup()
1132 {
1133   char filename[MAX_FILENAME_LEN];
1134
1135   /* always start with reliable default setup values */
1136
1137   leveldir_nr = 0;
1138   level_nr = 0;
1139
1140   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1141
1142   if (level_setup_list)
1143     freeSetupFileList(level_setup_list);
1144
1145   level_setup_list = loadSetupFileList(filename);
1146
1147   if (level_setup_list)
1148   {
1149     char *last_level_series =
1150       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1151
1152     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1153     level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
1154
1155     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1156   }
1157   else
1158     Error(ERR_WARN, "using default setup values");
1159 }
1160
1161 void SaveLevelSetup()
1162 {
1163   char filename[MAX_FILENAME_LEN];
1164   struct SetupFileList *list_entry = level_setup_list;
1165   FILE *file;
1166
1167   setTokenValue(level_setup_list,
1168                 TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
1169
1170   setTokenValue(level_setup_list,
1171                 leveldir[leveldir_nr].filename, int2str(level_nr, 0));
1172
1173   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1174
1175   if (!(file = fopen(filename, "w")))
1176   {
1177     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1178     return;
1179   }
1180
1181   fprintf(file, "%s:              %s\n\n",
1182           TOKEN_STR_FILE_IDENTIFIER, LEVELSETUP_COOKIE);
1183
1184   while (list_entry)
1185   {
1186     if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1187       fprintf(file, "%s\n",
1188               getFormattedSetupEntry(list_entry->token, list_entry->value));
1189
1190     /* just to make things nicer :) */
1191     if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1192       fprintf(file, "\n");
1193
1194     list_entry = list_entry->next;
1195   }
1196
1197   fclose(file);
1198
1199   chmod(filename, SETUP_PERMS);
1200 }