rnd-19981101-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_ALIAS_NAME          11
518
519 #define SETUP_TOKEN_USE_JOYSTICK        12
520 #define SETUP_TOKEN_JOY_DEVICE_NAME     13
521 #define SETUP_TOKEN_JOY_XLEFT           14
522 #define SETUP_TOKEN_JOY_XMIDDLE         15
523 #define SETUP_TOKEN_JOY_XRIGHT          16
524 #define SETUP_TOKEN_JOY_YUPPER          17
525 #define SETUP_TOKEN_JOY_YMIDDLE         18
526 #define SETUP_TOKEN_JOY_YLOWER          19
527 #define SETUP_TOKEN_JOY_SNAP            20
528 #define SETUP_TOKEN_JOY_BOMB            21
529 #define SETUP_TOKEN_KEY_LEFT            22
530 #define SETUP_TOKEN_KEY_RIGHT           23
531 #define SETUP_TOKEN_KEY_UP              24
532 #define SETUP_TOKEN_KEY_DOWN            25
533 #define SETUP_TOKEN_KEY_SNAP            26
534 #define SETUP_TOKEN_KEY_BOMB            27
535
536 #define NUM_SETUP_TOKENS                28
537
538 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_SOUND
539 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_ALIAS_NAME
540
541 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
542 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
543
544 #define TYPE_BOOLEAN                    1
545 #define TYPE_SWITCH                     2
546 #define TYPE_KEYSYM                     3
547 #define TYPE_INTEGER                    4
548 #define TYPE_STRING                     5
549
550 static struct SetupInfo si;
551 static struct SetupInputInfo sii;
552 static struct
553 {
554   int type;
555   void *value;
556   char *text;
557 } token_info[] =
558 {
559   { TYPE_SWITCH,  &si.sound,            "sound"                         },
560   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
561   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
562   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
563   { TYPE_SWITCH,  &si.toons,            "toons"                         },
564   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
565   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
566   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
567   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
568   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
569   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
570   { TYPE_STRING,  &si.alias_name,       "alias_name"                    },
571
572   /* for each player: */
573   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
574   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
575   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
576   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
577   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
578   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
579   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
580   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
581   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
582   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
583   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
584   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
585   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
586   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
587   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
588   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               }
589 };
590
591 static char *string_tolower(char *s)
592 {
593   static char s_lower[100];
594   int i;
595
596   if (strlen(s) >= 100)
597     return s;
598
599   strcpy(s_lower, s);
600
601   for (i=0; i<strlen(s_lower); i++)
602     s_lower[i] = tolower(s_lower[i]);
603
604   return s_lower;
605 }
606
607 static int get_string_integer_value(char *s)
608 {
609   static char *number_text[][3] =
610   {
611     { "0", "zero", "null", },
612     { "1", "one", "first" },
613     { "2", "two", "second" },
614     { "3", "three", "third" },
615     { "4", "four", "fourth" },
616     { "5", "five", "fifth" },
617     { "6", "six", "sixth" },
618     { "7", "seven", "seventh" },
619     { "8", "eight", "eighth" },
620     { "9", "nine", "ninth" },
621     { "10", "ten", "tenth" },
622     { "11", "eleven", "eleventh" },
623     { "12", "twelve", "twelfth" },
624   };
625
626   int i, j;
627
628   for (i=0; i<13; i++)
629     for (j=0; j<3; j++)
630       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
631         return i;
632
633   return atoi(s);
634 }
635
636 static boolean get_string_boolean_value(char *s)
637 {
638   if (strcmp(string_tolower(s), "true") == 0 ||
639       strcmp(string_tolower(s), "yes") == 0 ||
640       strcmp(string_tolower(s), "on") == 0 ||
641       get_string_integer_value(s) == 1)
642     return TRUE;
643   else
644     return FALSE;
645 }
646
647 static char *getFormattedSetupEntry(char *token, char *value)
648 {
649   int i;
650   static char entry[MAX_LINE_LEN];
651
652   sprintf(entry, "%s:", token);
653   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
654     entry[i] = ' ';
655   entry[i] = '\0';
656
657   strcat(entry, value);
658
659   return entry;
660 }
661
662 static void freeSetupFileList(struct SetupFileList *setup_file_list)
663 {
664   if (!setup_file_list)
665     return;
666
667   if (setup_file_list->token)
668     free(setup_file_list->token);
669   if (setup_file_list->value)
670     free(setup_file_list->value);
671   if (setup_file_list->next)
672     freeSetupFileList(setup_file_list->next);
673   free(setup_file_list);
674 }
675
676 static struct SetupFileList *newSetupFileList(char *token, char *value)
677 {
678   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
679
680   new->token = checked_malloc(strlen(token) + 1);
681   strcpy(new->token, token);
682
683   new->value = checked_malloc(strlen(value) + 1);
684   strcpy(new->value, value);
685
686   new->next = NULL;
687
688   return new;
689 }
690
691 static char *getTokenValue(struct SetupFileList *setup_file_list,
692                            char *token)
693 {
694   if (!setup_file_list)
695     return NULL;
696
697   if (strcmp(setup_file_list->token, token) == 0)
698     return setup_file_list->value;
699   else
700     return getTokenValue(setup_file_list->next, token);
701 }
702
703 static void setTokenValue(struct SetupFileList *setup_file_list,
704                           char *token, char *value)
705 {
706   if (!setup_file_list)
707     return;
708
709   if (strcmp(setup_file_list->token, token) == 0)
710   {
711     free(setup_file_list->value);
712     setup_file_list->value = checked_malloc(strlen(value) + 1);
713     strcpy(setup_file_list->value, value);
714   }
715   else if (setup_file_list->next == NULL)
716     setup_file_list->next = newSetupFileList(token, value);
717   else
718     setTokenValue(setup_file_list->next, token, value);
719 }
720
721 #ifdef DEBUG
722 static void printSetupFileList(struct SetupFileList *setup_file_list)
723 {
724   if (!setup_file_list)
725     return;
726
727   printf("token: '%s'\n", setup_file_list->token);
728   printf("value: '%s'\n", setup_file_list->value);
729
730   printSetupFileList(setup_file_list->next);
731 }
732 #endif
733
734 static struct SetupFileList *loadSetupFileList(char *filename)
735 {
736   int line_len;
737   char line[MAX_LINE_LEN];
738   char *token, *value, *line_ptr;
739   struct SetupFileList *setup_file_list = newSetupFileList("", "");
740   struct SetupFileList *first_valid_list_entry;
741
742   FILE *file;
743
744   if (!(file = fopen(filename, "r")))
745   {
746     Error(ERR_WARN, "cannot open setup file '%s'", filename);
747     return NULL;
748   }
749
750   while(!feof(file))
751   {
752     /* read next line of input file */
753     if (!fgets(line, MAX_LINE_LEN, file))
754       break;
755
756     /* cut trailing comment or whitespace from input line */
757     for (line_ptr = line; *line_ptr; line_ptr++)
758     {
759       if (*line_ptr == '#' || *line_ptr == '\n')
760       {
761         *line_ptr = '\0';
762         break;
763       }
764     }
765
766     /* cut trailing whitespaces from input line */
767     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
768       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
769         *line_ptr = '\0';
770
771     /* ignore empty lines */
772     if (*line == '\0')
773       continue;
774
775     line_len = strlen(line);
776
777     /* cut leading whitespaces from token */
778     for (token = line; *token; token++)
779       if (*token != ' ' && *token != '\t')
780         break;
781
782     /* find end of token */
783     for (line_ptr = token; *line_ptr; line_ptr++)
784     {
785       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
786       {
787         *line_ptr = '\0';
788         break;
789       }
790     }
791
792     if (line_ptr < line + line_len)
793       value = line_ptr + 1;
794     else
795       value = "\0";
796
797     /* cut leading whitespaces from value */
798     for (; *value; value++)
799       if (*value != ' ' && *value != '\t')
800         break;
801
802     if (*token && *value)
803       setTokenValue(setup_file_list, token, value);
804   }
805
806   fclose(file);
807
808   first_valid_list_entry = setup_file_list->next;
809
810   /* free empty list header */
811   setup_file_list->next = NULL;
812   freeSetupFileList(setup_file_list);
813
814   if (!first_valid_list_entry)
815     Error(ERR_WARN, "setup file is empty");
816
817   return first_valid_list_entry;
818 }
819
820 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
821                                          char *identifier)
822 {
823   if (!setup_file_list)
824     return;
825
826   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
827   {
828     if (strcmp(setup_file_list->value, identifier) != 0)
829     {
830       Error(ERR_WARN, "setup file has wrong version");
831       return;
832     }
833     else
834       return;
835   }
836
837   if (setup_file_list->next)
838     checkSetupFileListIdentifier(setup_file_list->next, identifier);
839   else
840   {
841     Error(ERR_WARN, "setup file has no version information");
842     return;
843   }
844 }
845
846 static void setSetupInfoToDefaults(struct SetupInfo *si)
847 {
848   int i;
849
850   si->sound = TRUE;
851   si->sound_loops = FALSE;
852   si->sound_music = FALSE;
853   si->sound_simple = FALSE;
854   si->toons = TRUE;
855   si->double_buffering = TRUE;
856   si->direct_draw = !si->double_buffering;
857   si->scroll_delay = FALSE;
858   si->soft_scrolling = TRUE;
859   si->fading = FALSE;
860   si->autorecord = FALSE;
861   si->quick_doors = FALSE;
862
863   strncpy(si->login_name, GetLoginName(), MAX_NAMELEN-1);
864   si->login_name[MAX_NAMELEN-1] = '\0';
865   strncpy(si->alias_name, GetLoginName(), MAX_NAMELEN-1);
866   si->alias_name[MAX_NAMELEN-1] = '\0';
867
868   for (i=0; i<MAX_PLAYERS; i++)
869   {
870     si->input[i].use_joystick = FALSE;
871     strcpy(si->input[i].joy.device_name, joystick_device_name[i]);
872     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
873     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
874     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
875     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
876     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
877     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
878     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
879     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
880     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
881     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
882     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
883     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
884     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
885     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
886   }
887 }
888
889 static void setSetupInfo(int token_nr, char *token_value)
890 {
891   int token_type = token_info[token_nr].type;
892   void *setup_value = token_info[token_nr].value;
893
894   if (token_value == NULL)
895     return;
896
897   /* set setup field to corresponding token value */
898   switch (token_type)
899   {
900     case TYPE_BOOLEAN:
901     case TYPE_SWITCH:
902       *(boolean *)setup_value = get_string_boolean_value(token_value);
903       break;
904
905     case TYPE_KEYSYM:
906       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
907       break;
908
909     case TYPE_INTEGER:
910       *(int *)setup_value = get_string_integer_value(token_value);
911       break;
912
913     case TYPE_STRING:
914       strcpy((char *)setup_value, token_value);
915       break;
916
917     default:
918       break;
919   }
920 }
921
922 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
923 {
924   int i, pnr;
925
926   if (!setup_file_list)
927     return;
928
929   /* handle global setup values */
930   si = setup;
931   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
932     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
933   setup = si;
934
935   /* handle player specific setup values */
936   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
937   {
938     char prefix[30];
939
940     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
941
942     sii = setup.input[pnr];
943     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
944     {
945       char full_token[100];
946
947       sprintf(full_token, "%s%s", prefix, token_info[i].text);
948       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
949     }
950     setup.input[pnr] = sii;
951   }
952 }
953
954 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
955 {
956   int i;
957
958   if (!level_series_name)
959     return 0;
960
961   for (i=0; i<num_leveldirs; i++)
962     if (strcmp(level_series_name, leveldir[i].filename) == 0)
963       return i;
964
965   return 0;
966 }
967
968 int getLastPlayedLevelOfLevelSeries(char *level_series_name)
969 {
970   char *token_value;
971   int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
972   int last_level_nr = 0;
973
974   if (!level_series_name)
975     return 0;
976
977   token_value = getTokenValue(level_setup_list, level_series_name);
978
979   if (token_value)
980   {
981     int highest_level_nr = leveldir[level_series_nr].levels - 1;
982
983     last_level_nr = atoi(token_value);
984
985     if (last_level_nr < 0)
986       last_level_nr = 0;
987     if (last_level_nr > highest_level_nr)
988       last_level_nr = highest_level_nr;
989   }
990
991   return last_level_nr;
992 }
993
994 void LoadSetup()
995 {
996   char filename[MAX_FILENAME_LEN];
997   struct SetupFileList *setup_file_list = NULL;
998
999   /* always start with reliable default setup values */
1000   setSetupInfoToDefaults(&setup);
1001
1002   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1003
1004   setup_file_list = loadSetupFileList(filename);
1005
1006   if (setup_file_list)
1007   {
1008     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1009     decodeSetupFileList(setup_file_list);
1010
1011     setup.direct_draw = !setup.double_buffering;
1012
1013     freeSetupFileList(setup_file_list);
1014   }
1015   else
1016     Error(ERR_WARN, "using default setup values");
1017 }
1018
1019 static char *getSetupLine(char *prefix, int token_nr)
1020 {
1021   int i;
1022   static char entry[MAX_LINE_LEN];
1023   int token_type = token_info[token_nr].type;
1024   void *setup_value = token_info[token_nr].value;
1025   char *token_text = token_info[token_nr].text;
1026
1027   /* start with the prefix, token and some spaces to format output line */
1028   sprintf(entry, "%s%s:", prefix, token_text);
1029   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1030     strcat(entry, " ");
1031
1032   /* continue with the token's value (which can have different types) */
1033   switch (token_type)
1034   {
1035     case TYPE_BOOLEAN:
1036       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1037       break;
1038
1039     case TYPE_SWITCH:
1040       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1041       break;
1042
1043     case TYPE_KEYSYM:
1044       {
1045         KeySym keysym = *(KeySym *)setup_value;
1046         char *keyname = getKeyNameFromKeySym(keysym);
1047
1048         strcat(entry, getX11KeyNameFromKeySym(keysym));
1049         for (i=strlen(entry); i<50; i++)
1050           strcat(entry, " ");
1051
1052         /* add comment, if useful */
1053         if (strcmp(keyname, "(undefined)") != 0 &&
1054             strcmp(keyname, "(unknown)") != 0)
1055         {
1056           strcat(entry, "# ");
1057           strcat(entry, keyname);
1058         }
1059       }
1060       break;
1061
1062     case TYPE_INTEGER:
1063       {
1064         char buffer[MAX_LINE_LEN];
1065
1066         sprintf(buffer, "%d", *(int *)setup_value);
1067         strcat(entry, buffer);
1068       }
1069       break;
1070
1071     case TYPE_STRING:
1072       strcat(entry, (char *)setup_value);
1073       break;
1074
1075     default:
1076       break;
1077   }
1078
1079   return entry;
1080 }
1081
1082 void SaveSetup()
1083 {
1084   int i, pnr;
1085   char filename[MAX_FILENAME_LEN];
1086   FILE *file;
1087
1088   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1089
1090   if (!(file = fopen(filename, "w")))
1091   {
1092     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1093     return;
1094   }
1095
1096   fprintf(file, "%s\n",
1097           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1098   fprintf(file, "\n");
1099
1100   /* handle global setup values */
1101   si = setup;
1102   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1103   {
1104     /* just to make things nicer :) */
1105     if (i == SETUP_TOKEN_ALIAS_NAME)
1106       fprintf(file, "\n");
1107
1108     fprintf(file, "%s\n", getSetupLine("", i));
1109   }
1110
1111   /* handle player specific setup values */
1112   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1113   {
1114     char prefix[30];
1115
1116     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1117     fprintf(file, "\n");
1118
1119     sii = setup.input[pnr];
1120     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1121       fprintf(file, "%s\n", getSetupLine(prefix, i));
1122   }
1123
1124   fclose(file);
1125
1126   chmod(filename, SETUP_PERMS);
1127 }
1128
1129 void LoadLevelSetup()
1130 {
1131   char filename[MAX_FILENAME_LEN];
1132
1133   /* always start with reliable default setup values */
1134
1135   leveldir_nr = 0;
1136   level_nr = 0;
1137
1138   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1139
1140   if (level_setup_list)
1141     freeSetupFileList(level_setup_list);
1142
1143   level_setup_list = loadSetupFileList(filename);
1144
1145   if (level_setup_list)
1146   {
1147     char *last_level_series =
1148       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1149
1150     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1151     level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
1152
1153     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1154   }
1155   else
1156     Error(ERR_WARN, "using default setup values");
1157 }
1158
1159 void SaveLevelSetup()
1160 {
1161   char filename[MAX_FILENAME_LEN];
1162   struct SetupFileList *list_entry = level_setup_list;
1163   FILE *file;
1164
1165   setTokenValue(level_setup_list,
1166                 TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
1167
1168   setTokenValue(level_setup_list,
1169                 leveldir[leveldir_nr].filename, int2str(level_nr, 0));
1170
1171   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1172
1173   if (!(file = fopen(filename, "w")))
1174   {
1175     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1176     return;
1177   }
1178
1179   fprintf(file, "%s:              %s\n\n",
1180           TOKEN_STR_FILE_IDENTIFIER, LEVELSETUP_COOKIE);
1181
1182   while (list_entry)
1183   {
1184     if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1185       fprintf(file, "%s\n",
1186               getFormattedSetupEntry(list_entry->token, list_entry->value));
1187
1188     /* just to make things nicer :) */
1189     if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1190       fprintf(file, "\n");
1191
1192     list_entry = list_entry->next;
1193   }
1194
1195   fclose(file);
1196
1197   chmod(filename, SETUP_PERMS);
1198 }