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