rnd-19981026-2
[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 void LoadJoystickData()
471 {
472   int i;
473   char cookie[256];
474   FILE *file;
475
476   if (joystick_status==JOYSTICK_OFF)
477     return;
478
479 #ifndef MSDOS
480   if (!(file=fopen(JOYDAT_FILE,"r")))
481     return;
482
483   fscanf(file,"%s",cookie);
484   if (strcmp(cookie,JOYSTICK_COOKIE))   /* ungültiges Format? */
485   {
486     Error(ERR_WARN, "wrong format of joystick file '%s'", JOYDAT_FILE);
487     fclose(file);
488     return;
489   }
490
491   for(i=0;i<2;i++)
492   {
493     fscanf(file,"%s",cookie);
494     fscanf(file, "%d %d %d \n",
495            &joystick[i].xleft, &joystick[i].xmiddle, &joystick[i].xright);
496     fscanf(file, "%d %d %d \n",
497            &joystick[i].yupper, &joystick[i].ymiddle, &joystick[i].ylower);
498   }
499   fclose(file);
500
501   CheckJoystickData();
502 #else
503   load_joystick_data(JOYDAT_FILE);
504 #endif
505 }
506
507 void SaveJoystickData()
508 {
509   int i;
510   FILE *file;
511
512   if (joystick_status==JOYSTICK_OFF)
513     return;
514
515 #ifndef MSDOS
516   CheckJoystickData();
517
518   if (!(file=fopen(JOYDAT_FILE,"w")))
519   {
520     Error(ERR_WARN, "cannot save joystick calibration data to file '%s'",
521           JOYDAT_FILE);
522     return;
523   }
524
525   fprintf(file,"%s\n",JOYSTICK_COOKIE); /* Formatkennung */
526   for(i=0;i<2;i++)
527   {
528     fprintf(file,"JOYSTICK_%d_DATA\n",i);
529     fprintf(file, "%d %d %d \n",
530             joystick[i].xleft, joystick[i].xmiddle, joystick[i].xright);
531     fprintf(file, "%d %d %d \n",
532             joystick[i].yupper, joystick[i].ymiddle, joystick[i].ylower);
533   }
534   fclose(file);
535
536   chmod(JOYDAT_FILE, JOYDAT_PERMS);
537 #else
538   save_joystick_data(JOYDAT_FILE);
539 #endif
540
541 }
542
543 /* ------------------------------------------------------------------------- */
544 /* new setup functions                                                       */
545 /* ------------------------------------------------------------------------- */
546
547 #define MAX_LINE_LEN                    1000
548 #define MAX_SETUP_TOKEN_LEN             100
549 #define MAX_SETUP_VALUE_LEN             100
550
551 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
552 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
553 #define TOKEN_STR_ALIAS_NAME            "alias_name"
554
555 #define TOKEN_STR_PLAYER_PREFIX         "player_"
556 #define TOKEN_VALUE_POSITION            30
557 #define TOKEN_INVALID                   -1
558 #define TOKEN_IGNORE                    -99
559
560 #define SETUP_TOKEN_ALIAS_NAME          100
561
562 #define SETUP_TOKEN_SOUND               0
563 #define SETUP_TOKEN_SOUND_LOOPS         1
564 #define SETUP_TOKEN_SOUND_MUSIC         2
565 #define SETUP_TOKEN_SOUND_SIMPLE        3
566 #define SETUP_TOKEN_TOONS               4
567 #define SETUP_TOKEN_DIRECT_DRAW         5
568 #define SETUP_TOKEN_SCROLL_DELAY        6
569 #define SETUP_TOKEN_SOFT_SCROLLING      7
570 #define SETUP_TOKEN_FADING              8
571 #define SETUP_TOKEN_AUTORECORD          9
572 #define SETUP_TOKEN_QUICK_DOORS         10
573 #define SETUP_TOKEN_USE_JOYSTICK        11
574 #define SETUP_TOKEN_JOYSTICK_NR         12
575 #define SETUP_TOKEN_JOY_SNAP            13
576 #define SETUP_TOKEN_JOY_BOMB            14
577 #define SETUP_TOKEN_KEY_LEFT            15
578 #define SETUP_TOKEN_KEY_RIGHT           16
579 #define SETUP_TOKEN_KEY_UP              17
580 #define SETUP_TOKEN_KEY_DOWN            18
581 #define SETUP_TOKEN_KEY_SNAP            19
582 #define SETUP_TOKEN_KEY_BOMB            20
583
584 #define NUM_SETUP_TOKENS                21
585
586 static struct
587 {
588   char *token, *value_true, *value_false;
589 } setup_info[] =
590 {
591   { "sound",                    "on", "off" },
592   { "repeating_sound_loops",    "on", "off" },
593   { "background_music",         "on", "off" },
594   { "simple_sound_effects",     "on", "off" },
595   { "toons",                    "on", "off" },
596   { "double_buffering",         "off", "on" },
597   { "scroll_delay",             "on", "off" },
598   { "soft_scrolling",           "on", "off" },
599   { "screen_fading",            "on", "off" },
600   { "automatic_tape_recording", "on", "off" },
601   { "quick_doors",              "on", "off" },
602
603   /* for each player: */
604   { ".use_joystick",            "true", "false" },
605   { ".joystick_device",         "second", "first" },
606   { ".joy.snap_field",          "", "" },
607   { ".joy.place_bomb",          "", "" },
608   { ".key.move_left",           "", "" },
609   { ".key.move_right",          "", "" },
610   { ".key.move_up",             "", "" },
611   { ".key.move_down",           "", "" },
612   { ".key.snap_field",          "", "" },
613   { ".key.place_bomb",          "", "" }
614 };
615
616 static char *string_tolower(char *s)
617 {
618   static char s_lower[100];
619   int i;
620
621   if (strlen(s) >= 100)
622     return s;
623
624   strcpy(s_lower, s);
625
626   for (i=0; i<strlen(s_lower); i++)
627     s_lower[i] = tolower(s_lower[i]);
628
629   return s_lower;
630 }
631
632 static int get_string_integer_value(char *s)
633 {
634   static char *number_text[][3] =
635   {
636     { "0", "zero", "null", },
637     { "1", "one", "first" },
638     { "2", "two", "second" },
639     { "3", "three", "third" },
640     { "4", "four", "fourth" },
641     { "5", "five", "fifth" },
642     { "6", "six", "sixth" },
643     { "7", "seven", "seventh" },
644     { "8", "eight", "eighth" },
645     { "9", "nine", "ninth" },
646     { "10", "ten", "tenth" },
647     { "11", "eleven", "eleventh" },
648     { "12", "twelve", "twelfth" },
649   };
650
651   int i, j;
652
653   for (i=0; i<13; i++)
654     for (j=0; j<3; j++)
655       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
656         return i;
657
658   return -1;
659 }
660
661 static boolean get_string_boolean_value(char *s)
662 {
663   if (strcmp(string_tolower(s), "true") == 0 ||
664       strcmp(string_tolower(s), "yes") == 0 ||
665       strcmp(string_tolower(s), "on") == 0 ||
666       get_string_integer_value(s) == 1)
667     return TRUE;
668   else
669     return FALSE;
670 }
671
672 static char *getSetupToken(int token_nr)
673 {
674   return setup_info[token_nr].token;
675 }
676
677 static char *getSetupValue(int token_nr, boolean token_value)
678 {
679   if (token_value == TRUE)
680     return setup_info[token_nr].value_true;
681   else
682     return setup_info[token_nr].value_false;
683 }
684
685 static char *getFormattedSetupEntry(char *token, char *value)
686 {
687   int i;
688   static char entry[MAX_LINE_LEN];
689
690   sprintf(entry, "%s:", token);
691   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
692     entry[i] = ' ';
693   entry[i] = '\0';
694
695   strcat(entry, value);
696
697   return entry;
698 }
699
700 static char *getSetupEntry(char *prefix, int token_nr, int token_value)
701 {
702   int i;
703   static char entry[MAX_LINE_LEN];
704
705   sprintf(entry, "%s%s:", prefix, getSetupToken(token_nr));
706   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
707     entry[i] = ' ';
708   entry[i] = '\0';
709
710   strcat(entry, getSetupValue(token_nr, token_value));
711
712   return entry;
713 }
714
715 static char *getSetupEntryWithComment(char *prefix,int token_nr, KeySym keysym)
716 {
717   int i;
718   static char entry[MAX_LINE_LEN];
719   char *keyname = getKeyNameFromKeySym(keysym);
720
721   sprintf(entry, "%s%s:", prefix, getSetupToken(token_nr));
722   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
723     entry[i] = ' ';
724   entry[i] = '\0';
725
726   strcat(entry, getX11KeyNameFromKeySym(keysym));
727   for (i=strlen(entry); i<50; i++)
728     entry[i] = ' ';
729   entry[i] = '\0';
730
731   /* add comment, if useful */
732   if (strcmp(keyname, "(undefined)") != 0 &&
733       strcmp(keyname, "(unknown)") != 0)
734   {
735     strcat(entry, "# ");
736     strcat(entry, keyname);
737   }
738
739   return entry;
740 }
741
742 static void freeSetupFileList(struct SetupFileList *setup_file_list)
743 {
744   if (!setup_file_list)
745     return;
746
747   if (setup_file_list->token)
748     free(setup_file_list->token);
749   if (setup_file_list->value)
750     free(setup_file_list->value);
751   if (setup_file_list->next)
752     freeSetupFileList(setup_file_list->next);
753   free(setup_file_list);
754 }
755
756 static struct SetupFileList *newSetupFileList(char *token, char *value)
757 {
758   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
759
760   new->token = checked_malloc(strlen(token) + 1);
761   strcpy(new->token, token);
762
763   new->value = checked_malloc(strlen(value) + 1);
764   strcpy(new->value, value);
765
766   new->next = NULL;
767
768   return new;
769 }
770
771 static char *getSetupFileListEntry(struct SetupFileList *setup_file_list,
772                                    char *token)
773 {
774   if (!setup_file_list)
775     return NULL;
776
777   if (strcmp(setup_file_list->token, token) == 0)
778     return setup_file_list->value;
779   else
780     return getSetupFileListEntry(setup_file_list->next, token);
781 }
782
783 boolean setSetupFileListEntry(struct SetupFileList *setup_file_list,
784                               char *token, char *value)
785 {
786   if (!setup_file_list)
787     return FALSE;
788
789   if (strcmp(setup_file_list->token, token) == 0)
790   {
791     free(setup_file_list->value);
792     setup_file_list->value = checked_malloc(strlen(value) + 1);
793     strcpy(setup_file_list->value, value);
794
795     return TRUE;
796   }
797   else
798     return setSetupFileListEntry(setup_file_list->next, token, value);
799 }
800
801 void updateSetupFileListEntry(struct SetupFileList *setup_file_list,
802                               char *token, char *value)
803 {
804   if (!setup_file_list)
805     return;
806
807   if (getSetupFileListEntry(setup_file_list, token) != NULL)
808     setSetupFileListEntry(setup_file_list, token, value);
809   else
810   {
811     struct SetupFileList *list_entry = setup_file_list;
812
813     while (list_entry->next)
814       list_entry = list_entry->next;
815
816     list_entry->next = newSetupFileList(token, value);
817   }
818 }
819
820 #ifdef DEBUG
821 static void printSetupFileList(struct SetupFileList *setup_file_list)
822 {
823   if (!setup_file_list)
824     return;
825
826   printf("token: '%s'\n", setup_file_list->token);
827   printf("value: '%s'\n", setup_file_list->value);
828
829   printSetupFileList(setup_file_list->next);
830 }
831 #endif
832
833 static struct SetupFileList *loadSetupFileList(char *filename)
834 {
835   int line_len;
836   char line[MAX_LINE_LEN];
837   char *token, *value, *line_ptr;
838   struct SetupFileList *setup_file_list = newSetupFileList("", "");
839   struct SetupFileList *first_valid_list_entry;
840
841
842
843   /*
844   struct SetupFileList **next_entry = &setup_file_list;
845   */
846
847
848   FILE *file;
849
850   if (!(file = fopen(filename, "r")))
851   {
852     Error(ERR_WARN, "cannot open setup file '%s'", filename);
853     return NULL;
854   }
855
856   while(!feof(file))
857   {
858     /* read next line of input file */
859     if (!fgets(line, MAX_LINE_LEN, file))
860       break;
861
862     /* cut trailing comment or whitespace from input line */
863     for (line_ptr = line; *line_ptr; line_ptr++)
864     {
865       if (*line_ptr == '#' || *line_ptr == '\n')
866       {
867         *line_ptr = '\0';
868         break;
869       }
870     }
871
872     /* cut trailing whitespaces from input line */
873     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
874       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
875         *line_ptr = '\0';
876
877     /* ignore empty lines */
878     if (*line == '\0')
879       continue;
880
881     line_len = strlen(line);
882
883     /* cut leading whitespaces from token */
884     for (token = line; *token; token++)
885       if (*token != ' ' && *token != '\t')
886         break;
887
888     /* find end of token */
889     for (line_ptr = token; *line_ptr; line_ptr++)
890     {
891       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
892       {
893         *line_ptr = '\0';
894         break;
895       }
896     }
897
898     if (line_ptr < line + line_len)
899       value = line_ptr + 1;
900     else
901       value = "\0";
902
903     /* cut leading whitespaces from value */
904     for (; *value; value++)
905       if (*value != ' ' && *value != '\t')
906         break;
907
908     if (*token && *value)
909       updateSetupFileListEntry(setup_file_list, token, value);
910
911
912 #if 0
913     {
914       /* allocate new token/value pair */
915
916       *next_entry = newSetupFileList(token, value);
917       next_entry = &((*next_entry)->next);
918     }
919 #endif
920
921
922
923   }
924
925   fclose(file);
926
927   first_valid_list_entry = setup_file_list->next;
928
929   /* free empty list header */
930   setup_file_list->next = NULL;
931   freeSetupFileList(setup_file_list);
932
933   if (!first_valid_list_entry)
934     Error(ERR_WARN, "setup file is empty");
935
936   return first_valid_list_entry;
937 }
938
939 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
940                                          char *identifier)
941 {
942   if (!setup_file_list)
943     return;
944
945   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
946   {
947     if (strcmp(setup_file_list->value, identifier) != 0)
948     {
949       Error(ERR_WARN, "setup file has wrong version");
950       return;
951     }
952     else
953       return;
954   }
955
956   if (setup_file_list->next)
957     checkSetupFileListIdentifier(setup_file_list->next, identifier);
958   else
959   {
960     Error(ERR_WARN, "setup file has no version information");
961     return;
962   }
963 }
964
965 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
966 {
967   int i;
968   int token_nr = TOKEN_INVALID;
969   int player_nr = 0;
970   char *token;
971   char *token_value;
972   int token_integer_value;
973   boolean token_boolean_value;
974   int token_player_prefix_len;
975
976   if (!setup_file_list)
977     return;
978
979   token = setup_file_list->token;
980   token_value = setup_file_list->value;
981   token_integer_value = get_string_integer_value(token_value);
982   token_boolean_value = get_string_boolean_value(token_value);
983
984   token_player_prefix_len = strlen(TOKEN_STR_PLAYER_PREFIX);
985
986   if (strncmp(token, TOKEN_STR_PLAYER_PREFIX,
987               token_player_prefix_len) == 0)
988   {
989     token += token_player_prefix_len;
990
991     if (*token >= '0' && *token <= '9')
992     {
993       player_nr = ((int)(*token - '0') - 1 + MAX_PLAYERS) % MAX_PLAYERS;
994       token++;
995     }
996   }
997
998   for (i=0; i<NUM_SETUP_TOKENS; i++)
999   {
1000     if (strcmp(token, setup_info[i].token) == 0)
1001     {
1002       token_nr = i;
1003       break;
1004     }
1005   }
1006
1007   if (strcmp(token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1008     token_nr = TOKEN_IGNORE;
1009   else if (strcmp(token, TOKEN_STR_ALIAS_NAME) == 0)
1010     token_nr = SETUP_TOKEN_ALIAS_NAME;
1011
1012   switch (token_nr)
1013   {
1014     case SETUP_TOKEN_SOUND:
1015       setup.sound_on = token_boolean_value;
1016       break;
1017     case SETUP_TOKEN_SOUND_LOOPS:
1018       setup.sound_loops_on = token_boolean_value;
1019       break;
1020     case SETUP_TOKEN_SOUND_MUSIC:
1021       setup.sound_music_on = token_boolean_value;
1022       break;
1023     case SETUP_TOKEN_SOUND_SIMPLE:
1024       setup.sound_simple_on = token_boolean_value;
1025       break;
1026     case SETUP_TOKEN_TOONS:
1027       setup.toons_on = token_boolean_value;
1028       break;
1029     case SETUP_TOKEN_DIRECT_DRAW:
1030       setup.direct_draw_on = !token_boolean_value;
1031       break;
1032     case SETUP_TOKEN_SCROLL_DELAY:
1033       setup.scroll_delay_on = token_boolean_value;
1034       break;
1035     case SETUP_TOKEN_SOFT_SCROLLING:
1036       setup.soft_scrolling_on = token_boolean_value;
1037       break;
1038     case SETUP_TOKEN_FADING:
1039       setup.fading_on = token_boolean_value;
1040       break;
1041     case SETUP_TOKEN_AUTORECORD:
1042       setup.autorecord_on = token_boolean_value;
1043       break;
1044     case SETUP_TOKEN_QUICK_DOORS:
1045       setup.quick_doors = token_boolean_value;
1046       break;
1047
1048     case SETUP_TOKEN_USE_JOYSTICK:
1049       setup.input[player_nr].use_joystick = token_boolean_value;
1050       break;
1051     case SETUP_TOKEN_JOYSTICK_NR:
1052       if (token_integer_value < 0 || token_integer_value > 1)
1053         token_integer_value = 1;
1054       setup.input[player_nr].joystick_nr = token_integer_value - 1;
1055       break;
1056     case SETUP_TOKEN_JOY_SNAP:
1057       setup.input[player_nr].joy.snap = getJoySymbolFromJoyName(token_value);
1058       break;
1059     case SETUP_TOKEN_JOY_BOMB    :
1060       setup.input[player_nr].joy.bomb = getJoySymbolFromJoyName(token_value);
1061       break;
1062     case SETUP_TOKEN_KEY_LEFT:
1063       setup.input[player_nr].key.left = getKeySymFromX11KeyName(token_value);
1064       break;
1065     case SETUP_TOKEN_KEY_RIGHT:
1066       setup.input[player_nr].key.right = getKeySymFromX11KeyName(token_value);
1067       break;
1068     case SETUP_TOKEN_KEY_UP:
1069       setup.input[player_nr].key.up = getKeySymFromX11KeyName(token_value);
1070       break;
1071     case SETUP_TOKEN_KEY_DOWN:
1072       setup.input[player_nr].key.down = getKeySymFromX11KeyName(token_value);
1073       break;
1074     case SETUP_TOKEN_KEY_SNAP:
1075       setup.input[player_nr].key.snap = getKeySymFromX11KeyName(token_value);
1076       break;
1077     case SETUP_TOKEN_KEY_BOMB:
1078       setup.input[player_nr].key.bomb = getKeySymFromX11KeyName(token_value);
1079       break;
1080
1081     case SETUP_TOKEN_ALIAS_NAME:
1082       strncpy(local_player->alias_name, token_value, MAX_NAMELEN-1);
1083       local_player->alias_name[MAX_NAMELEN-1] = '\0';
1084       break;
1085
1086     case TOKEN_INVALID:
1087       Error(ERR_WARN, "unknown token '%s' not recognized", token);
1088       break;
1089
1090     case TOKEN_IGNORE:
1091     default:
1092       break;
1093   }
1094
1095   decodeSetupFileList(setup_file_list->next);
1096 }
1097
1098 void LoadSetup()
1099 {
1100   int i;
1101   char filename[MAX_FILENAME_LEN];
1102   struct SetupFileList *setup_file_list = NULL;
1103
1104   /* always start with reliable default setup values */
1105
1106   strncpy(local_player->login_name, GetLoginName(), MAX_NAMELEN-1);
1107   local_player->login_name[MAX_NAMELEN-1] = '\0';
1108
1109   strncpy(local_player->alias_name, GetLoginName(), MAX_NAMELEN-1);
1110   local_player->alias_name[MAX_NAMELEN-1] = '\0';
1111
1112   setup.sound_on = TRUE;
1113   setup.sound_loops_on = FALSE;
1114   setup.sound_music_on = FALSE;
1115   setup.sound_simple_on = FALSE;
1116   setup.toons_on = TRUE;
1117   setup.direct_draw_on = FALSE;
1118   setup.scroll_delay_on = FALSE;
1119   setup.soft_scrolling_on = TRUE;
1120   setup.fading_on = FALSE;
1121   setup.autorecord_on = FALSE;
1122   setup.quick_doors = FALSE;
1123
1124   for (i=0; i<MAX_PLAYERS; i++)
1125   {
1126     setup.input[i].use_joystick = FALSE;
1127     setup.input[i].joystick_nr = 0;
1128     setup.input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1129     setup.input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1130     setup.input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
1131     setup.input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1132     setup.input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
1133     setup.input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
1134     setup.input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
1135     setup.input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
1136   }
1137
1138   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1139
1140   setup_file_list = loadSetupFileList(filename);
1141
1142   if (setup_file_list)
1143   {
1144
1145 #if 0
1146     printSetupFileList(setup_file_list);
1147 #endif
1148
1149     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1150     decodeSetupFileList(setup_file_list);
1151
1152     freeSetupFileList(setup_file_list);
1153   }
1154   else
1155     Error(ERR_RETURN, "using default setup values");
1156 }
1157
1158 void LoadLevelSetup()
1159 {
1160   char filename[MAX_FILENAME_LEN];
1161
1162   /* always start with reliable default setup values */
1163
1164   leveldir_nr = 0;
1165   level_nr = 0;
1166
1167   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1168
1169   if (level_setup_list)
1170     freeSetupFileList(level_setup_list);
1171
1172   level_setup_list = loadSetupFileList(filename);
1173
1174   if (level_setup_list)
1175   {
1176     int i;
1177
1178     char *last_level_series =
1179       getSetupFileListEntry(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1180
1181     if (last_level_series)
1182     {
1183       for (i=0; i<num_leveldirs; i++)
1184       {
1185         if (strcmp(last_level_series, leveldir[i].filename) == 0)
1186         {
1187           char *token_value =
1188             getSetupFileListEntry(level_setup_list, last_level_series);
1189
1190           leveldir_nr = i;
1191
1192           if (token_value)
1193           {
1194             level_nr = atoi(token_value);
1195
1196             if (level_nr < 0)
1197               level_nr = 0;
1198             if (level_nr > leveldir[leveldir_nr].levels - 1)
1199               level_nr = leveldir[leveldir_nr].levels - 1;
1200           }
1201
1202           break;
1203         }
1204       }
1205     }
1206
1207
1208 #if 0
1209     printSetupFileList(level_setup_list);
1210 #endif
1211
1212     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1213   }
1214   else
1215     Error(ERR_RETURN, "using default setup values");
1216 }
1217
1218 void SaveSetup()
1219 {
1220   int i;
1221   char filename[MAX_FILENAME_LEN];
1222   FILE *file;
1223
1224   sprintf(filename, "%s/%s", SETUP_PATH, SETUP_FILENAME);
1225
1226   if (!(file = fopen(filename, "w")))
1227   {
1228     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1229     return;
1230   }
1231
1232   fprintf(file, "%s:              %s\n",
1233           TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE);
1234
1235   fprintf(file, "\n");
1236
1237   fprintf(file, "%s\n",
1238           getSetupEntry("", SETUP_TOKEN_SOUND,
1239                         setup.sound_on));
1240   fprintf(file, "%s\n",
1241           getSetupEntry("", SETUP_TOKEN_SOUND_LOOPS,
1242                         setup.sound_loops_on));
1243   fprintf(file, "%s\n",
1244           getSetupEntry("", SETUP_TOKEN_SOUND_MUSIC,
1245                         setup.sound_music_on));
1246   fprintf(file, "%s\n",
1247           getSetupEntry("", SETUP_TOKEN_SOUND_SIMPLE,
1248                         setup.sound_simple_on));
1249   fprintf(file, "%s\n",
1250           getSetupEntry("", SETUP_TOKEN_TOONS,
1251                         setup.toons_on));
1252   fprintf(file, "%s\n",
1253           getSetupEntry("", SETUP_TOKEN_DIRECT_DRAW,
1254                         setup.direct_draw_on));
1255   fprintf(file, "%s\n",
1256           getSetupEntry("", SETUP_TOKEN_SCROLL_DELAY,
1257                         setup.scroll_delay_on));
1258   fprintf(file, "%s\n",
1259           getSetupEntry("", SETUP_TOKEN_SOFT_SCROLLING,
1260                         setup.soft_scrolling_on));
1261   fprintf(file, "%s\n",
1262           getSetupEntry("", SETUP_TOKEN_FADING,
1263                         setup.fading_on));
1264   fprintf(file, "%s\n",
1265           getSetupEntry("", SETUP_TOKEN_AUTORECORD,
1266                         setup.autorecord_on));
1267   fprintf(file, "%s\n",
1268           getSetupEntry("", SETUP_TOKEN_QUICK_DOORS,
1269                         setup.quick_doors));
1270
1271   fprintf(file, "\n");
1272
1273   fprintf(file, "%s\n",
1274           getFormattedSetupEntry(TOKEN_STR_ALIAS_NAME,
1275                                  local_player->alias_name));
1276
1277   for (i=0; i<MAX_PLAYERS; i++)
1278   {
1279     char prefix[30];
1280
1281     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, i + 1);
1282
1283     fprintf(file, "\n");
1284
1285     fprintf(file, "%s\n",
1286             getSetupEntry(prefix, SETUP_TOKEN_USE_JOYSTICK,
1287                           setup.input[i].use_joystick));
1288     fprintf(file, "%s\n",
1289             getSetupEntry(prefix, SETUP_TOKEN_JOYSTICK_NR,
1290                           setup.input[i].joystick_nr));
1291
1292     fprintf(file, "%s%s:      %s\n", prefix,
1293             getSetupToken(SETUP_TOKEN_JOY_SNAP),
1294             getJoyNameFromJoySymbol(setup.input[i].joy.snap));
1295     fprintf(file, "%s%s:      %s\n", prefix,
1296             getSetupToken(SETUP_TOKEN_JOY_BOMB),
1297             getJoyNameFromJoySymbol(setup.input[i].joy.bomb));
1298
1299     fprintf(file, "%s\n",
1300             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_LEFT,
1301                                      setup.input[i].key.left));
1302     fprintf(file, "%s\n",
1303             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_RIGHT,
1304                                      setup.input[i].key.right));
1305     fprintf(file, "%s\n",
1306             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_UP,
1307                                      setup.input[i].key.up));
1308     fprintf(file, "%s\n",
1309             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_DOWN,
1310                                      setup.input[i].key.down));
1311     fprintf(file, "%s\n",
1312             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_SNAP,
1313                                      setup.input[i].key.snap));
1314     fprintf(file, "%s\n",
1315             getSetupEntryWithComment(prefix, SETUP_TOKEN_KEY_BOMB,
1316                                      setup.input[i].key.bomb));
1317   }
1318
1319   fclose(file);
1320
1321   chmod(filename, SETUP_PERMS);
1322 }
1323
1324 void SaveLevelSetup()
1325 {
1326   char filename[MAX_FILENAME_LEN];
1327   struct SetupFileList *list_entry = level_setup_list;
1328   FILE *file;
1329
1330   updateSetupFileListEntry(level_setup_list,
1331                            TOKEN_STR_LAST_LEVEL_SERIES,
1332                            leveldir[leveldir_nr].filename);
1333
1334   updateSetupFileListEntry(level_setup_list,
1335                            leveldir[leveldir_nr].filename,
1336                            int2str(level_nr, 0));
1337
1338   sprintf(filename, "%s/%s", SETUP_PATH, LEVELSETUP_FILENAME);
1339
1340   if (!(file = fopen(filename, "w")))
1341   {
1342     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1343     return;
1344   }
1345
1346   fprintf(file, "%s:              %s\n\n",
1347           TOKEN_STR_FILE_IDENTIFIER, LEVELSETUP_COOKIE);
1348
1349   while (list_entry)
1350   {
1351     if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1352       fprintf(file, "%s\n",
1353               getFormattedSetupEntry(list_entry->token, list_entry->value));
1354
1355     /* just to make things nicer :) */
1356     if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1357       fprintf(file, "\n");
1358
1359     list_entry = list_entry->next;
1360   }
1361
1362   fclose(file);
1363
1364   chmod(filename, SETUP_PERMS);
1365 }