cb1199d340bb58212659431caadeb900800719ca
[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 #include <dirent.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 #include "files.h"
20 #include "tools.h"
21 #include "misc.h"
22 #include "tape.h"
23 #include "joystick.h"
24
25 #define MAX_LINE_LEN                    1000    /* file input line length */
26 #define CHUNK_ID_LEN                    4       /* IFF style chunk id length */
27 #define LEVEL_HEADER_SIZE               80      /* size of level file header */
28 #define LEVEL_HEADER_UNUSED             18      /* unused level header bytes */
29 #define TAPE_HEADER_SIZE                20      /* size of tape file header */
30 #define TAPE_HEADER_UNUSED              7       /* unused tape header bytes */
31 #define FILE_VERSION_1_0                10      /* old 1.0 file version */
32 #define FILE_VERSION_1_2                12      /* actual file version */
33
34 static void SaveUserLevelInfo();                /* for 'InitUserLevelDir()' */
35 static char *getSetupLine(char *, int);         /* for 'SaveUserLevelInfo()' */
36
37 static char *getGlobalDataDir()
38 {
39   return GAME_DIR;
40 }
41
42 static char *getUserDataDir()
43 {
44   static char *userdata_dir = NULL;
45
46   if (!userdata_dir)
47   {
48     char *home_dir = getHomeDir();
49     char *data_dir = USERDATA_DIRECTORY;
50
51     userdata_dir = checked_malloc(strlen(home_dir) + strlen(data_dir) + 2);
52     sprintf(userdata_dir, "%s/%s", home_dir, data_dir);
53   }
54
55   return userdata_dir;
56 }
57
58 static char *getSetupDir()
59 {
60   return getUserDataDir();
61 }
62
63 static char *getUserLevelDir(char *level_subdir)
64 {
65   static char *userlevel_dir = NULL;
66   char *data_dir = getUserDataDir();
67   char *userlevel_subdir = LEVELS_DIRECTORY;
68
69   if (userlevel_dir)
70     free(userlevel_dir);
71
72   userlevel_dir = checked_malloc(strlen(data_dir) + strlen(userlevel_subdir) +
73                                  strlen(level_subdir) + 3);
74   sprintf(userlevel_dir, "%s/%s%s%s", data_dir, userlevel_subdir,
75           (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
76
77   return userlevel_dir;
78 }
79
80 static char *getTapeDir(char *level_subdir)
81 {
82   static char *tape_dir = NULL;
83   char *data_dir = getUserDataDir();
84   char *tape_subdir = TAPES_DIRECTORY;
85
86   if (tape_dir)
87     free(tape_dir);
88
89   tape_dir = checked_malloc(strlen(data_dir) + strlen(tape_subdir) +
90                             strlen(level_subdir) + 3);
91   sprintf(tape_dir, "%s/%s%s%s", data_dir, tape_subdir,
92           (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
93
94   return tape_dir;
95 }
96
97 static char *getScoreDir(char *level_subdir)
98 {
99   static char *score_dir = NULL;
100   char *data_dir = getGlobalDataDir();
101   char *score_subdir = SCORES_DIRECTORY;
102
103   if (score_dir)
104     free(score_dir);
105
106   score_dir = checked_malloc(strlen(data_dir) + strlen(score_subdir) +
107                              strlen(level_subdir) + 3);
108   sprintf(score_dir, "%s/%s%s%s", data_dir, score_subdir,
109           (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
110
111   return score_dir;
112 }
113
114 static void createDirectory(char *dir, char *text)
115 {
116   if (access(dir, F_OK) != 0)
117     if (mkdir(dir, USERDATA_DIR_MODE) != 0)
118       Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
119 }
120
121 static void InitUserDataDirectory()
122 {
123   createDirectory(getUserDataDir(), "user data");
124 }
125
126 static void InitTapeDirectory(char *level_subdir)
127 {
128   createDirectory(getTapeDir(""), "main tape");
129   createDirectory(getTapeDir(level_subdir), "level tape");
130 }
131
132 static void InitScoreDirectory(char *level_subdir)
133 {
134   createDirectory(getScoreDir(""), "main score");
135   createDirectory(getScoreDir(level_subdir), "level score");
136 }
137
138 static void InitUserLevelDirectory(char *level_subdir)
139 {
140   if (access(getUserLevelDir(level_subdir), F_OK) != 0)
141   {
142     createDirectory(getUserLevelDir(""), "main user level");
143     createDirectory(getUserLevelDir(level_subdir), "user level");
144
145     SaveUserLevelInfo();
146   }
147 }
148
149 static void setLevelInfoToDefaults()
150 {
151   int i, x, y;
152
153   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
154   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
155
156   for(x=0; x<MAX_LEV_FIELDX; x++) 
157     for(y=0; y<MAX_LEV_FIELDY; y++) 
158       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
159
160   level.time = 100;
161   level.edelsteine = 0;
162   level.tempo_amoebe = 10;
163   level.dauer_sieb = 10;
164   level.dauer_ablenk = 10;
165   level.amoebe_inhalt = EL_DIAMANT;
166
167   strcpy(level.name, "Nameless Level");
168
169   for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
170     level.score[i] = 10;
171
172   for(i=0; i<4; i++)
173     for(x=0; x<3; x++)
174       for(y=0; y<3; y++)
175         level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
176
177   Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
178   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
179     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
180 }
181
182 void LoadLevel(int level_nr)
183 {
184   int i, x, y;
185   char filename[MAX_FILENAME_LEN];
186   char cookie[MAX_LINE_LEN];
187   char chunk[CHUNK_ID_LEN + 1];
188   int file_version = FILE_VERSION_1_2;  /* last version of level files */
189   int chunk_length;
190   FILE *file;
191
192   /* always start with reliable default values */
193   setLevelInfoToDefaults();
194
195   if (leveldir[leveldir_nr].user_defined)
196     sprintf(filename, "%s/%s/%d",
197             getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
198   else
199     sprintf(filename, "%s/%s/%d",
200             options.level_directory, leveldir[leveldir_nr].filename, level_nr);
201
202   if (!(file = fopen(filename, "r")))
203   {
204     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
205     return;
206   }
207
208   /* check file identifier */
209   fgets(cookie, MAX_LINE_LEN, file);
210   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
211     cookie[strlen(cookie) - 1] = '\0';
212
213   if (strcmp(cookie, LEVEL_COOKIE_10) == 0)     /* old 1.0 level format */
214     file_version = FILE_VERSION_1_0;
215   else if (strcmp(cookie, LEVEL_COOKIE) != 0)   /* unknown level format */
216   {
217     Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
218     fclose(file);
219     return;
220   }
221
222   /* read chunk "HEAD" */
223   if (file_version >= FILE_VERSION_1_2)
224   {
225     /* first check header chunk identifier and chunk length */
226     fgets(chunk, CHUNK_ID_LEN + 1, file);
227     chunk_length =
228       (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
229     if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
230     {
231       Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
232       fclose(file);
233       return;
234     }
235   }
236
237   lev_fieldx = level.fieldx = fgetc(file);
238   lev_fieldy = level.fieldy = fgetc(file);
239
240   level.time            = (fgetc(file)<<8) | fgetc(file);
241   level.edelsteine      = (fgetc(file)<<8) | fgetc(file);
242
243   for(i=0; i<MAX_LEVNAMLEN; i++)
244     level.name[i]       = fgetc(file);
245   level.name[MAX_LEVNAMLEN - 1] = 0;
246
247   for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
248     level.score[i]      = fgetc(file);
249
250   for(i=0; i<4; i++)
251     for(y=0; y<3; y++)
252       for(x=0; x<3; x++)
253         level.mampfer_inhalt[i][x][y] = fgetc(file);
254
255   level.tempo_amoebe    = fgetc(file);
256   level.dauer_sieb      = fgetc(file);
257   level.dauer_ablenk    = fgetc(file);
258   level.amoebe_inhalt = fgetc(file);
259
260   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* skip unused header bytes */
261     fgetc(file);
262
263   /* read chunk "BODY" */
264   if (file_version >= FILE_VERSION_1_2)
265   {
266     /* next check body chunk identifier and chunk length */
267     fgets(chunk, CHUNK_ID_LEN + 1, file);
268     chunk_length =
269       (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
270     if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
271     {
272       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
273       fclose(file);
274       return;
275     }
276   }
277
278   for(y=0; y<lev_fieldy; y++) 
279     for(x=0; x<lev_fieldx; x++) 
280       Feld[x][y] = Ur[x][y] = fgetc(file);
281
282   fclose(file);
283
284   if (level.time <= 10)         /* minimum playing time of each level */
285     level.time = 10;
286 }
287
288 void SaveLevel(int level_nr)
289 {
290   int i, x, y;
291   char filename[MAX_FILENAME_LEN];
292   FILE *file;
293   int chunk_length;
294
295   if (leveldir[leveldir_nr].user_defined)
296     sprintf(filename, "%s/%s/%d",
297             getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
298   else
299     sprintf(filename, "%s/%s/%d",
300             options.level_directory, leveldir[leveldir_nr].filename, level_nr);
301
302   if (!(file = fopen(filename, "w")))
303   {
304     Error(ERR_WARN, "cannot save level file '%s'", filename);
305     return;
306   }
307
308   fputs(LEVEL_COOKIE, file);            /* file identifier */
309   fputc('\n', file);
310
311   fputs("HEAD", file);                  /* chunk identifier for file header */
312
313   chunk_length = LEVEL_HEADER_SIZE;
314
315   fputc((chunk_length >>  24) & 0xff, file);
316   fputc((chunk_length >>  16) & 0xff, file);
317   fputc((chunk_length >>   8) & 0xff, file);
318   fputc((chunk_length >>   0) & 0xff, file);
319
320   fputc(level.fieldx, file);
321   fputc(level.fieldy, file);
322   fputc(level.time / 256, file);
323   fputc(level.time % 256, file);
324   fputc(level.edelsteine / 256, file);
325   fputc(level.edelsteine % 256, file);
326
327   for(i=0; i<MAX_LEVNAMLEN; i++)
328     fputc(level.name[i], file);
329   for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
330     fputc(level.score[i], file);
331   for(i=0; i<4; i++)
332     for(y=0; y<3; y++)
333       for(x=0; x<3; x++)
334         fputc(level.mampfer_inhalt[i][x][y], file);
335   fputc(level.tempo_amoebe, file);
336   fputc(level.dauer_sieb, file);
337   fputc(level.dauer_ablenk, file);
338   fputc(level.amoebe_inhalt, file);
339
340   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* set unused header bytes to zero */
341     fputc(0, file);
342
343   fputs("BODY", file);                  /* chunk identifier for file body */
344   chunk_length = lev_fieldx * lev_fieldy;
345
346   fputc((chunk_length >>  24) & 0xff, file);
347   fputc((chunk_length >>  16) & 0xff, file);
348   fputc((chunk_length >>   8) & 0xff, file);
349   fputc((chunk_length >>   0) & 0xff, file);
350
351   for(y=0; y<lev_fieldy; y++) 
352     for(x=0; x<lev_fieldx; x++) 
353       fputc(Ur[x][y], file);
354
355   fclose(file);
356
357   chmod(filename, LEVEL_PERMS);
358 }
359
360 void LoadTape(int level_nr)
361 {
362   int i, j;
363   char filename[MAX_FILENAME_LEN];
364   char cookie[MAX_LINE_LEN];
365   char chunk[CHUNK_ID_LEN + 1];
366   FILE *file;
367   boolean player_participates[MAX_PLAYERS];
368   int num_participating_players;
369   int file_version = FILE_VERSION_1_2;  /* last version of tape files */
370   int chunk_length;
371
372   sprintf(filename, "%s/%d.%s",
373           getTapeDir(leveldir[leveldir_nr].filename),
374           level_nr, TAPEFILE_EXTENSION);
375
376   if (!(file = fopen(filename, "r")))
377     return;
378
379   /* check file identifier */
380   fgets(cookie, MAX_LINE_LEN, file);
381   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
382     cookie[strlen(cookie) - 1] = '\0';
383
384   if (strcmp(cookie, TAPE_COOKIE_10) == 0)      /* old 1.0 tape format */
385     file_version = FILE_VERSION_1_0;
386   else if (strcmp(cookie, TAPE_COOKIE) != 0)    /* unknown tape format */
387   {
388     Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
389     fclose(file);
390     return;
391   }
392
393   /* read chunk "HEAD" */
394   if (file_version >= FILE_VERSION_1_2)
395   {
396     /* first check header chunk identifier and chunk length */
397     fgets(chunk, CHUNK_ID_LEN + 1, file);
398     chunk_length =
399       (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
400
401     if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
402     {
403       Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
404       fclose(file);
405       return;
406     }
407   }
408
409   tape.random_seed =
410     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
411   tape.date =
412     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
413   tape.length =
414     (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
415
416   /* read header fields that are new since version 1.2 */
417   if (file_version >= FILE_VERSION_1_2)
418   {
419     byte store_participating_players = fgetc(file);
420
421     for(i=0; i<TAPE_HEADER_UNUSED; i++)         /* skip unused header bytes */
422       fgetc(file);
423
424     /* check which players participate in this tape recording */
425     num_participating_players = 0;
426     for(i=0; i<MAX_PLAYERS; i++)
427     {
428       player_participates[i] = FALSE;
429
430       if (store_participating_players & (1 << i))
431       {
432         player_participates[i] = TRUE;
433         num_participating_players++;
434       }
435     }
436   }
437
438   tape.level_nr = level_nr;
439   tape.counter = 0;
440   tape.changed = FALSE;
441
442   tape.recording = FALSE;
443   tape.playing = FALSE;
444   tape.pausing = FALSE;
445
446   /* read chunk "BODY" */
447   if (file_version >= FILE_VERSION_1_2)
448   {
449     /* next check body chunk identifier and chunk length */
450     fgets(chunk, CHUNK_ID_LEN + 1, file);
451     chunk_length =
452       (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
453     if (strcmp(chunk, "BODY") ||
454         chunk_length != (num_participating_players + 1) * tape.length)
455     {
456       Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
457       fclose(file);
458       return;
459     }
460   }
461
462   for(i=0; i<tape.length; i++)
463   {
464     if (i >= MAX_TAPELEN)
465       break;
466
467     for(j=0; j<MAX_PLAYERS; j++)
468     {
469       tape.pos[i].action[j] = MV_NO_MOVING;
470
471       /* pre-1.2 tapes store data for only one player */
472       if (file_version == FILE_VERSION_1_0 && j > 0)
473         continue;
474
475       if (player_participates[j])
476         tape.pos[i].action[j] = fgetc(file);
477     }
478
479     tape.pos[i].delay = fgetc(file);
480
481     if (file_version == FILE_VERSION_1_0)
482     {
483       /* eliminate possible diagonal moves in old tapes */
484       /* this is only for backward compatibility */
485
486       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
487       byte action = tape.pos[i].action[0];
488       int k, num_moves = 0;
489
490       for (k=0; k<4; k++)
491       {
492         if (action & joy_dir[k])
493         {
494           tape.pos[i + num_moves].action[0] = joy_dir[k];
495           if (num_moves > 0)
496             tape.pos[i + num_moves].delay = 0;
497           num_moves++;
498         }
499       }
500
501       if (num_moves > 1)
502       {
503         num_moves--;
504         i += num_moves;
505         tape.length += num_moves;
506       }
507     }
508
509     if (feof(file))
510       break;
511   }
512
513   fclose(file);
514
515   if (i != tape.length)
516     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
517
518   tape.length_seconds = GetTapeLength();
519 }
520
521 void SaveTape(int level_nr)
522 {
523   int i, j;
524   char filename[MAX_FILENAME_LEN];
525   FILE *file;
526   boolean new_tape = TRUE;
527   boolean player_participates[MAX_PLAYERS];
528   byte store_participating_players;
529   int num_participating_players;
530   int chunk_length;
531
532   InitTapeDirectory(leveldir[leveldir_nr].filename);
533
534   sprintf(filename, "%s/%d.%s",
535           getTapeDir(leveldir[leveldir_nr].filename),
536           level_nr, TAPEFILE_EXTENSION);
537
538   /* if a tape still exists, ask to overwrite it */
539   if ((file = fopen(filename, "r")))
540   {
541     new_tape = FALSE;
542     fclose(file);
543
544     if (!Request("Replace old tape ?", REQ_ASK))
545       return;
546   }
547
548   for(i=0; i<MAX_PLAYERS; i++)
549     player_participates[i] = FALSE;
550
551   /* check which players participate in this tape recording */
552   for(i=0; i<tape.length; i++)
553     for(j=0; j<MAX_PLAYERS; j++)
554       if (tape.pos[i].action[j] != 0)
555         player_participates[j] = TRUE;
556
557   /* count number of players and set corresponding bits for compact storage */
558   store_participating_players = 0;
559   num_participating_players = 0;
560   for(i=0; i<MAX_PLAYERS; i++)
561   {
562     if (player_participates[i])
563     {
564       num_participating_players++;
565       store_participating_players |= (1 << i);
566     }
567   }
568
569   if (!(file = fopen(filename, "w")))
570   {
571     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
572     return;
573   }
574
575   fputs(TAPE_COOKIE, file);             /* file identifier */
576   fputc('\n', file);
577
578   fputs("HEAD", file);                  /* chunk identifier for file header */
579
580   chunk_length = TAPE_HEADER_SIZE;
581
582   fputc((chunk_length >>  24) & 0xff, file);
583   fputc((chunk_length >>  16) & 0xff, file);
584   fputc((chunk_length >>   8) & 0xff, file);
585   fputc((chunk_length >>   0) & 0xff, file);
586
587   fputc((tape.random_seed >> 24) & 0xff, file);
588   fputc((tape.random_seed >> 16) & 0xff, file);
589   fputc((tape.random_seed >>  8) & 0xff, file);
590   fputc((tape.random_seed >>  0) & 0xff, file);
591
592   fputc((tape.date >>  24) & 0xff, file);
593   fputc((tape.date >>  16) & 0xff, file);
594   fputc((tape.date >>   8) & 0xff, file);
595   fputc((tape.date >>   0) & 0xff, file);
596
597   fputc((tape.length >>  24) & 0xff, file);
598   fputc((tape.length >>  16) & 0xff, file);
599   fputc((tape.length >>   8) & 0xff, file);
600   fputc((tape.length >>   0) & 0xff, file);
601
602   fputc(store_participating_players, file);
603
604   for(i=0; i<TAPE_HEADER_UNUSED; i++)   /* set unused header bytes to zero */
605     fputc(0, file);
606
607   fputs("BODY", file);                  /* chunk identifier for file body */
608   chunk_length = (num_participating_players + 1) * tape.length;
609
610   fputc((chunk_length >>  24) & 0xff, file);
611   fputc((chunk_length >>  16) & 0xff, file);
612   fputc((chunk_length >>   8) & 0xff, file);
613   fputc((chunk_length >>   0) & 0xff, file);
614
615   for(i=0; i<tape.length; i++)
616   {
617     int j;
618
619     for(j=0; j<MAX_PLAYERS; j++)
620       if (player_participates[j])
621         fputc(tape.pos[i].action[j], file);
622
623     fputc(tape.pos[i].delay, file);
624   }
625
626   fclose(file);
627
628   chmod(filename, LEVREC_PERMS);
629
630   tape.changed = FALSE;
631
632   if (new_tape)
633     Request("tape saved !", REQ_CONFIRM);
634 }
635
636 void LoadScore(int level_nr)
637 {
638   int i;
639   char filename[MAX_FILENAME_LEN];
640   char cookie[MAX_LINE_LEN];
641   char line[MAX_LINE_LEN];
642   char *line_ptr;
643   FILE *file;
644
645   /* start with empty score table */
646   for(i=0; i<MAX_SCORE_ENTRIES; i++)
647   {
648     strcpy(highscore[i].Name, EMPTY_ALIAS);
649     highscore[i].Score = 0;
650   }
651
652   sprintf(filename, "%s/%d.%s",
653           getScoreDir(leveldir[leveldir_nr].filename),
654           level_nr, SCOREFILE_EXTENSION);
655
656   if (!(file = fopen(filename, "r")))
657     return;
658
659   /* check file identifier */
660   fgets(cookie, MAX_LINE_LEN, file);
661
662   if (strcmp(cookie, SCORE_COOKIE) != 0)
663   {
664     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
665     fclose(file);
666     return;
667   }
668
669   for(i=0; i<MAX_SCORE_ENTRIES; i++)
670   {
671     fscanf(file, "%d", &highscore[i].Score);
672     fgets(line, MAX_LINE_LEN, file);
673
674     if (line[strlen(line)-1] == '\n')
675       line[strlen(line)-1] = '\0';
676
677     for (line_ptr = line; *line_ptr; line_ptr++)
678     {
679       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
680       {
681         strncpy(highscore[i].Name, line_ptr, MAX_NAMELEN - 1);
682         highscore[i].Name[MAX_NAMELEN - 1] = '\0';
683         break;
684       }
685     }
686   }
687
688   fclose(file);
689 }
690
691 void SaveScore(int level_nr)
692 {
693   int i;
694   char filename[MAX_FILENAME_LEN];
695   FILE *file;
696
697   InitScoreDirectory(leveldir[leveldir_nr].filename);
698
699   sprintf(filename, "%s/%d.%s",
700           getScoreDir(leveldir[leveldir_nr].filename),
701           level_nr, SCOREFILE_EXTENSION);
702
703   if (!(file = fopen(filename, "w")))
704   {
705     Error(ERR_WARN, "cannot save score for level %d", level_nr);
706     return;
707   }
708
709   fprintf(file, "%s\n\n", SCORE_COOKIE);
710
711   for(i=0; i<MAX_SCORE_ENTRIES; i++)
712     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
713
714   fclose(file);
715
716   chmod(filename, SCORE_PERMS);
717 }
718
719 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
720 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
721 #define TOKEN_STR_PLAYER_PREFIX         "player_"
722
723 #define TOKEN_VALUE_POSITION            30
724
725 /* global setup */
726 #define SETUP_TOKEN_PLAYER_NAME         0
727 #define SETUP_TOKEN_SOUND               1
728 #define SETUP_TOKEN_SOUND_LOOPS         2
729 #define SETUP_TOKEN_SOUND_MUSIC         3
730 #define SETUP_TOKEN_SOUND_SIMPLE        4
731 #define SETUP_TOKEN_TOONS               5
732 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
733 #define SETUP_TOKEN_SCROLL_DELAY        7
734 #define SETUP_TOKEN_SOFT_SCROLLING      8
735 #define SETUP_TOKEN_FADING              9
736 #define SETUP_TOKEN_AUTORECORD          10
737 #define SETUP_TOKEN_QUICK_DOORS         11
738 #define SETUP_TOKEN_TEAM_MODE           12
739
740 /* player setup */
741 #define SETUP_TOKEN_USE_JOYSTICK        13
742 #define SETUP_TOKEN_JOY_DEVICE_NAME     14
743 #define SETUP_TOKEN_JOY_XLEFT           15
744 #define SETUP_TOKEN_JOY_XMIDDLE         16
745 #define SETUP_TOKEN_JOY_XRIGHT          17
746 #define SETUP_TOKEN_JOY_YUPPER          18
747 #define SETUP_TOKEN_JOY_YMIDDLE         19
748 #define SETUP_TOKEN_JOY_YLOWER          20
749 #define SETUP_TOKEN_JOY_SNAP            21
750 #define SETUP_TOKEN_JOY_BOMB            22
751 #define SETUP_TOKEN_KEY_LEFT            23
752 #define SETUP_TOKEN_KEY_RIGHT           24
753 #define SETUP_TOKEN_KEY_UP              25
754 #define SETUP_TOKEN_KEY_DOWN            26
755 #define SETUP_TOKEN_KEY_SNAP            27
756 #define SETUP_TOKEN_KEY_BOMB            28
757
758 /* level directory info */
759 #define LEVELINFO_TOKEN_NAME            29
760 #define LEVELINFO_TOKEN_LEVELS          30
761 #define LEVELINFO_TOKEN_SORT_PRIORITY   31
762 #define LEVELINFO_TOKEN_READONLY        32
763
764 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
765 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_TEAM_MODE
766
767 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
768 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
769
770 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
771 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
772
773 #define TYPE_BOOLEAN                    1
774 #define TYPE_SWITCH                     2
775 #define TYPE_KEYSYM                     3
776 #define TYPE_INTEGER                    4
777 #define TYPE_STRING                     5
778
779 static struct SetupInfo si;
780 static struct SetupInputInfo sii;
781 static struct LevelDirInfo ldi;
782 static struct
783 {
784   int type;
785   void *value;
786   char *text;
787 } token_info[] =
788 {
789   /* global setup */
790   { TYPE_STRING,  &si.player_name,      "player_name"                   },
791   { TYPE_SWITCH,  &si.sound,            "sound"                         },
792   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
793   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
794   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
795   { TYPE_SWITCH,  &si.toons,            "toons"                         },
796   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
797   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
798   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
799   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
800   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
801   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
802   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
803
804   /* player setup */
805   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
806   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
807   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
808   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
809   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
810   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
811   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
812   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
813   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
814   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
815   { TYPE_KEYSYM,  &sii.key.left,        ".key.move_left"                },
816   { TYPE_KEYSYM,  &sii.key.right,       ".key.move_right"               },
817   { TYPE_KEYSYM,  &sii.key.up,          ".key.move_up"                  },
818   { TYPE_KEYSYM,  &sii.key.down,        ".key.move_down"                },
819   { TYPE_KEYSYM,  &sii.key.snap,        ".key.snap_field"               },
820   { TYPE_KEYSYM,  &sii.key.bomb,        ".key.place_bomb"               },
821
822   /* level directory info */
823   { TYPE_STRING,  &ldi.name,            "name"                          },
824   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
825   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
826   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
827 };
828
829 static char *string_tolower(char *s)
830 {
831   static char s_lower[100];
832   int i;
833
834   if (strlen(s) >= 100)
835     return s;
836
837   strcpy(s_lower, s);
838
839   for (i=0; i<strlen(s_lower); i++)
840     s_lower[i] = tolower(s_lower[i]);
841
842   return s_lower;
843 }
844
845 static int get_string_integer_value(char *s)
846 {
847   static char *number_text[][3] =
848   {
849     { "0", "zero", "null", },
850     { "1", "one", "first" },
851     { "2", "two", "second" },
852     { "3", "three", "third" },
853     { "4", "four", "fourth" },
854     { "5", "five", "fifth" },
855     { "6", "six", "sixth" },
856     { "7", "seven", "seventh" },
857     { "8", "eight", "eighth" },
858     { "9", "nine", "ninth" },
859     { "10", "ten", "tenth" },
860     { "11", "eleven", "eleventh" },
861     { "12", "twelve", "twelfth" },
862   };
863
864   int i, j;
865
866   for (i=0; i<13; i++)
867     for (j=0; j<3; j++)
868       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
869         return i;
870
871   return atoi(s);
872 }
873
874 static boolean get_string_boolean_value(char *s)
875 {
876   if (strcmp(string_tolower(s), "true") == 0 ||
877       strcmp(string_tolower(s), "yes") == 0 ||
878       strcmp(string_tolower(s), "on") == 0 ||
879       get_string_integer_value(s) == 1)
880     return TRUE;
881   else
882     return FALSE;
883 }
884
885 static char *getFormattedSetupEntry(char *token, char *value)
886 {
887   int i;
888   static char entry[MAX_LINE_LEN];
889
890   sprintf(entry, "%s:", token);
891   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
892     entry[i] = ' ';
893   entry[i] = '\0';
894
895   strcat(entry, value);
896
897   return entry;
898 }
899
900 static void freeSetupFileList(struct SetupFileList *setup_file_list)
901 {
902   if (!setup_file_list)
903     return;
904
905   if (setup_file_list->token)
906     free(setup_file_list->token);
907   if (setup_file_list->value)
908     free(setup_file_list->value);
909   if (setup_file_list->next)
910     freeSetupFileList(setup_file_list->next);
911   free(setup_file_list);
912 }
913
914 static struct SetupFileList *newSetupFileList(char *token, char *value)
915 {
916   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
917
918   new->token = checked_malloc(strlen(token) + 1);
919   strcpy(new->token, token);
920
921   new->value = checked_malloc(strlen(value) + 1);
922   strcpy(new->value, value);
923
924   new->next = NULL;
925
926   return new;
927 }
928
929 static char *getTokenValue(struct SetupFileList *setup_file_list,
930                            char *token)
931 {
932   if (!setup_file_list)
933     return NULL;
934
935   if (strcmp(setup_file_list->token, token) == 0)
936     return setup_file_list->value;
937   else
938     return getTokenValue(setup_file_list->next, token);
939 }
940
941 static void setTokenValue(struct SetupFileList *setup_file_list,
942                           char *token, char *value)
943 {
944   if (!setup_file_list)
945     return;
946
947   if (strcmp(setup_file_list->token, token) == 0)
948   {
949     free(setup_file_list->value);
950     setup_file_list->value = checked_malloc(strlen(value) + 1);
951     strcpy(setup_file_list->value, value);
952   }
953   else if (setup_file_list->next == NULL)
954     setup_file_list->next = newSetupFileList(token, value);
955   else
956     setTokenValue(setup_file_list->next, token, value);
957 }
958
959 #ifdef DEBUG
960 static void printSetupFileList(struct SetupFileList *setup_file_list)
961 {
962   if (!setup_file_list)
963     return;
964
965   printf("token: '%s'\n", setup_file_list->token);
966   printf("value: '%s'\n", setup_file_list->value);
967
968   printSetupFileList(setup_file_list->next);
969 }
970 #endif
971
972 static struct SetupFileList *loadSetupFileList(char *filename)
973 {
974   int line_len;
975   char line[MAX_LINE_LEN];
976   char *token, *value, *line_ptr;
977   struct SetupFileList *setup_file_list = newSetupFileList("", "");
978   struct SetupFileList *first_valid_list_entry;
979
980   FILE *file;
981
982   if (!(file = fopen(filename, "r")))
983   {
984     Error(ERR_WARN, "cannot open setup/info file '%s'", filename);
985     return NULL;
986   }
987
988   while(!feof(file))
989   {
990     /* read next line of input file */
991     if (!fgets(line, MAX_LINE_LEN, file))
992       break;
993
994     /* cut trailing comment or whitespace from input line */
995     for (line_ptr = line; *line_ptr; line_ptr++)
996     {
997       if (*line_ptr == '#' || *line_ptr == '\n')
998       {
999         *line_ptr = '\0';
1000         break;
1001       }
1002     }
1003
1004     /* cut trailing whitespaces from input line */
1005     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1006       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1007         *line_ptr = '\0';
1008
1009     /* ignore empty lines */
1010     if (*line == '\0')
1011       continue;
1012
1013     line_len = strlen(line);
1014
1015     /* cut leading whitespaces from token */
1016     for (token = line; *token; token++)
1017       if (*token != ' ' && *token != '\t')
1018         break;
1019
1020     /* find end of token */
1021     for (line_ptr = token; *line_ptr; line_ptr++)
1022     {
1023       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1024       {
1025         *line_ptr = '\0';
1026         break;
1027       }
1028     }
1029
1030     if (line_ptr < line + line_len)
1031       value = line_ptr + 1;
1032     else
1033       value = "\0";
1034
1035     /* cut leading whitespaces from value */
1036     for (; *value; value++)
1037       if (*value != ' ' && *value != '\t')
1038         break;
1039
1040     if (*token && *value)
1041       setTokenValue(setup_file_list, token, value);
1042   }
1043
1044   fclose(file);
1045
1046   first_valid_list_entry = setup_file_list->next;
1047
1048   /* free empty list header */
1049   setup_file_list->next = NULL;
1050   freeSetupFileList(setup_file_list);
1051
1052   if (first_valid_list_entry == NULL)
1053     Error(ERR_WARN, "setup/info file '%s' is empty", filename);
1054
1055   return first_valid_list_entry;
1056 }
1057
1058 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1059                                          char *identifier)
1060 {
1061   if (!setup_file_list)
1062     return;
1063
1064   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1065   {
1066     if (strcmp(setup_file_list->value, identifier) != 0)
1067     {
1068       Error(ERR_WARN, "setup/info file has wrong version");
1069       return;
1070     }
1071     else
1072       return;
1073   }
1074
1075   if (setup_file_list->next)
1076     checkSetupFileListIdentifier(setup_file_list->next, identifier);
1077   else
1078   {
1079     Error(ERR_WARN, "setup/info file has no version information");
1080     return;
1081   }
1082 }
1083
1084 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1085 {
1086   ldi->name = getStringCopy("non-existing");
1087   ldi->levels = 0;
1088   ldi->sort_priority = 999;     /* default: least priority */
1089   ldi->readonly = TRUE;
1090 }
1091
1092 static void setSetupInfoToDefaults(struct SetupInfo *si)
1093 {
1094   int i;
1095
1096   si->player_name = getStringCopy(getLoginName());
1097
1098   si->sound = TRUE;
1099   si->sound_loops = FALSE;
1100   si->sound_music = FALSE;
1101   si->sound_simple = FALSE;
1102   si->toons = TRUE;
1103   si->double_buffering = TRUE;
1104   si->direct_draw = !si->double_buffering;
1105   si->scroll_delay = FALSE;
1106   si->soft_scrolling = TRUE;
1107   si->fading = FALSE;
1108   si->autorecord = FALSE;
1109   si->quick_doors = FALSE;
1110
1111   for (i=0; i<MAX_PLAYERS; i++)
1112   {
1113     si->input[i].use_joystick = FALSE;
1114     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1115     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1116     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1117     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1118     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1119     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1120     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1121     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1122     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1123     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
1124     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1125     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
1126     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
1127     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
1128     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
1129   }
1130 }
1131
1132 static void setSetupInfo(int token_nr, char *token_value)
1133 {
1134   int token_type = token_info[token_nr].type;
1135   void *setup_value = token_info[token_nr].value;
1136
1137   if (token_value == NULL)
1138     return;
1139
1140   /* set setup field to corresponding token value */
1141   switch (token_type)
1142   {
1143     case TYPE_BOOLEAN:
1144     case TYPE_SWITCH:
1145       *(boolean *)setup_value = get_string_boolean_value(token_value);
1146       break;
1147
1148     case TYPE_KEYSYM:
1149       *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
1150       break;
1151
1152     case TYPE_INTEGER:
1153       *(int *)setup_value = get_string_integer_value(token_value);
1154       break;
1155
1156     case TYPE_STRING:
1157       if (*(char **)setup_value != NULL)
1158         free(*(char **)setup_value);
1159       *(char **)setup_value = getStringCopy(token_value);
1160       break;
1161
1162     default:
1163       break;
1164   }
1165 }
1166
1167 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1168 {
1169   int i, pnr;
1170
1171   if (!setup_file_list)
1172     return;
1173
1174   /* handle global setup values */
1175   si = setup;
1176   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1177     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1178   setup = si;
1179
1180   /* handle player specific setup values */
1181   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1182   {
1183     char prefix[30];
1184
1185     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1186
1187     sii = setup.input[pnr];
1188     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1189     {
1190       char full_token[100];
1191
1192       sprintf(full_token, "%s%s", prefix, token_info[i].text);
1193       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1194     }
1195     setup.input[pnr] = sii;
1196   }
1197 }
1198
1199 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
1200 {
1201   int i;
1202
1203   if (!level_series_name)
1204     return 0;
1205
1206   for (i=0; i<num_leveldirs; i++)
1207     if (strcmp(level_series_name, leveldir[i].filename) == 0)
1208       return i;
1209
1210   return 0;
1211 }
1212
1213 int getLastPlayedLevelOfLevelSeries(char *level_series_name)
1214 {
1215   char *token_value;
1216   int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
1217   int last_level_nr = 0;
1218
1219   if (!level_series_name)
1220     return 0;
1221
1222   token_value = getTokenValue(level_setup_list, level_series_name);
1223
1224   if (token_value)
1225   {
1226     int highest_level_nr = leveldir[level_series_nr].levels - 1;
1227
1228     last_level_nr = atoi(token_value);
1229
1230     if (last_level_nr < 0)
1231       last_level_nr = 0;
1232     if (last_level_nr > highest_level_nr)
1233       last_level_nr = highest_level_nr;
1234   }
1235
1236   return last_level_nr;
1237 }
1238
1239 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1240 {
1241   const struct LevelDirInfo *entry1 = object1;
1242   const struct LevelDirInfo *entry2 = object2;
1243   int compare_result;
1244
1245   if (entry1->sort_priority != entry2->sort_priority)
1246     compare_result = entry1->sort_priority - entry2->sort_priority;
1247   else
1248   {
1249     char *name1 = getStringToLower(entry1->name);
1250     char *name2 = getStringToLower(entry2->name);
1251
1252     compare_result = strcmp(name1, name2);
1253
1254     free(name1);
1255     free(name2);
1256   }
1257
1258   return compare_result;
1259 }
1260
1261 static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
1262 {
1263   DIR *dir;
1264   struct stat file_status;
1265   char *directory = NULL;
1266   char *filename = NULL;
1267   struct SetupFileList *setup_file_list = NULL;
1268   struct dirent *dir_entry;
1269   int i, current_entry = start_entry;
1270
1271   if ((dir = opendir(level_directory)) == NULL)
1272     Error(ERR_EXIT, "cannot read level directory '%s'", level_directory);
1273
1274   while (current_entry < MAX_LEVDIR_ENTRIES)
1275   {
1276     if ((dir_entry = readdir(dir)) == NULL)     /* last directory entry */
1277       break;
1278
1279     /* skip entries for current and parent directory */
1280     if (strcmp(dir_entry->d_name, ".")  == 0 ||
1281         strcmp(dir_entry->d_name, "..") == 0)
1282       continue;
1283
1284     /* find out if directory entry is itself a directory */
1285     directory = getPath2(level_directory, dir_entry->d_name);
1286     if (stat(directory, &file_status) != 0 ||           /* cannot stat file */
1287         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
1288     {
1289       free(directory);
1290       continue;
1291     }
1292
1293     if (strlen(dir_entry->d_name) >= MAX_LEVDIR_FILENAME)
1294     {
1295       Error(ERR_WARN, "filename of level directory '%s' too long -- ignoring",
1296             dir_entry->d_name);
1297       continue;
1298     }
1299
1300     filename = getPath2(directory, LEVELINFO_FILENAME);
1301     setup_file_list = loadSetupFileList(filename);
1302
1303     if (setup_file_list)
1304     {
1305       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1306       setLevelDirInfoToDefaults(&leveldir[current_entry]);
1307
1308       ldi = leveldir[current_entry];
1309       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1310         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1311       leveldir[current_entry] = ldi;
1312
1313       leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
1314       leveldir[current_entry].user_defined =
1315         (level_directory == options.level_directory ? FALSE : TRUE);
1316
1317       freeSetupFileList(setup_file_list);
1318       current_entry++;
1319     }
1320     else
1321       Error(ERR_WARN, "ignoring level directory '%s'", directory);
1322
1323     free(directory);
1324     free(filename);
1325   }
1326
1327   if (current_entry == MAX_LEVDIR_ENTRIES)
1328     Error(ERR_WARN, "using %d level directories -- ignoring the rest",
1329           current_entry);
1330
1331   closedir(dir);
1332
1333   if (current_entry == start_entry && start_entry != -1)
1334     Error(ERR_EXIT, "cannot find any valid level series in directory '%s'",
1335           level_directory);
1336
1337   return current_entry;
1338 }
1339
1340 void LoadLevelInfo()
1341 {
1342   InitUserLevelDirectory(getLoginName());
1343
1344   num_leveldirs = 0;
1345   leveldir_nr = 0;
1346
1347   num_leveldirs = LoadLevelInfoFromLevelDir(options.level_directory,
1348                                             num_leveldirs);
1349   num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
1350                                             num_leveldirs);
1351   if (num_leveldirs > 1)
1352     qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
1353           compareLevelDirInfoEntries);
1354 }
1355
1356 static void SaveUserLevelInfo()
1357 {
1358   char filename[MAX_FILENAME_LEN];
1359   FILE *file;
1360   int i;
1361
1362   sprintf(filename, "%s/%s",
1363           getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1364
1365   if (!(file = fopen(filename, "w")))
1366   {
1367     Error(ERR_WARN, "cannot write level info file '%s'", filename);
1368     return;
1369   }
1370
1371   ldi.name = getLoginName();
1372   ldi.levels = 100;
1373   ldi.sort_priority = 300;
1374   ldi.readonly = FALSE;
1375
1376   fprintf(file, "%s\n\n",
1377           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1378
1379   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1380     fprintf(file, "%s\n", getSetupLine("", i));
1381
1382   fclose(file);
1383
1384   chmod(filename, SETUP_PERMS);
1385 }
1386
1387 void LoadSetup()
1388 {
1389   char filename[MAX_FILENAME_LEN];
1390   struct SetupFileList *setup_file_list = NULL;
1391
1392   /* always start with reliable default values */
1393   setSetupInfoToDefaults(&setup);
1394
1395   sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1396
1397   setup_file_list = loadSetupFileList(filename);
1398
1399   if (setup_file_list)
1400   {
1401     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1402     decodeSetupFileList(setup_file_list);
1403
1404     setup.direct_draw = !setup.double_buffering;
1405
1406     freeSetupFileList(setup_file_list);
1407
1408     /* needed to work around problems with fixed length strings */
1409     if (strlen(setup.player_name) >= MAX_NAMELEN)
1410       setup.player_name[MAX_NAMELEN - 1] = '\0';
1411     else if (strlen(setup.player_name) < MAX_NAMELEN - 1)
1412     {
1413       char *new_name = checked_malloc(MAX_NAMELEN);
1414
1415       strcpy(new_name, setup.player_name);
1416       free(setup.player_name);
1417       setup.player_name = new_name;
1418     }
1419   }
1420   else
1421     Error(ERR_WARN, "using default setup values");
1422 }
1423
1424 static char *getSetupLine(char *prefix, int token_nr)
1425 {
1426   int i;
1427   static char entry[MAX_LINE_LEN];
1428   int token_type = token_info[token_nr].type;
1429   void *setup_value = token_info[token_nr].value;
1430   char *token_text = token_info[token_nr].text;
1431
1432   /* start with the prefix, token and some spaces to format output line */
1433   sprintf(entry, "%s%s:", prefix, token_text);
1434   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1435     strcat(entry, " ");
1436
1437   /* continue with the token's value (which can have different types) */
1438   switch (token_type)
1439   {
1440     case TYPE_BOOLEAN:
1441       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1442       break;
1443
1444     case TYPE_SWITCH:
1445       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1446       break;
1447
1448     case TYPE_KEYSYM:
1449       {
1450         KeySym keysym = *(KeySym *)setup_value;
1451         char *keyname = getKeyNameFromKeySym(keysym);
1452
1453         strcat(entry, getX11KeyNameFromKeySym(keysym));
1454         for (i=strlen(entry); i<50; i++)
1455           strcat(entry, " ");
1456
1457         /* add comment, if useful */
1458         if (strcmp(keyname, "(undefined)") != 0 &&
1459             strcmp(keyname, "(unknown)") != 0)
1460         {
1461           strcat(entry, "# ");
1462           strcat(entry, keyname);
1463         }
1464       }
1465       break;
1466
1467     case TYPE_INTEGER:
1468       {
1469         char buffer[MAX_LINE_LEN];
1470
1471         sprintf(buffer, "%d", *(int *)setup_value);
1472         strcat(entry, buffer);
1473       }
1474       break;
1475
1476     case TYPE_STRING:
1477       strcat(entry, *(char **)setup_value);
1478       break;
1479
1480     default:
1481       break;
1482   }
1483
1484   return entry;
1485 }
1486
1487 void SaveSetup()
1488 {
1489   int i, pnr;
1490   char filename[MAX_FILENAME_LEN];
1491   FILE *file;
1492
1493   InitUserDataDirectory();
1494
1495   sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1496
1497   if (!(file = fopen(filename, "w")))
1498   {
1499     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1500     return;
1501   }
1502
1503   fprintf(file, "%s\n",
1504           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1505   fprintf(file, "\n");
1506
1507   /* handle global setup values */
1508   si = setup;
1509   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1510   {
1511     fprintf(file, "%s\n", getSetupLine("", i));
1512
1513     /* just to make things nicer :) */
1514     if (i == SETUP_TOKEN_PLAYER_NAME)
1515       fprintf(file, "\n");
1516   }
1517
1518   /* handle player specific setup values */
1519   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1520   {
1521     char prefix[30];
1522
1523     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1524     fprintf(file, "\n");
1525
1526     sii = setup.input[pnr];
1527     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1528       fprintf(file, "%s\n", getSetupLine(prefix, i));
1529   }
1530
1531   fclose(file);
1532
1533   chmod(filename, SETUP_PERMS);
1534 }
1535
1536 void LoadLevelSetup()
1537 {
1538   char filename[MAX_FILENAME_LEN];
1539
1540   /* always start with reliable default values */
1541   leveldir_nr = 0;
1542   level_nr = 0;
1543
1544   sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1545
1546   if (level_setup_list)
1547     freeSetupFileList(level_setup_list);
1548
1549   level_setup_list = loadSetupFileList(filename);
1550
1551   if (level_setup_list)
1552   {
1553     char *last_level_series =
1554       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1555
1556     leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1557     level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
1558
1559     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1560   }
1561   else
1562   {
1563     level_setup_list = newSetupFileList(TOKEN_STR_FILE_IDENTIFIER,
1564                                         LEVELSETUP_COOKIE);
1565     Error(ERR_WARN, "using default setup values");
1566   }
1567 }
1568
1569 void SaveLevelSetup()
1570 {
1571   char filename[MAX_FILENAME_LEN];
1572   struct SetupFileList *list_entry = level_setup_list;
1573   FILE *file;
1574
1575   InitUserDataDirectory();
1576
1577   setTokenValue(level_setup_list,
1578                 TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
1579
1580   setTokenValue(level_setup_list,
1581                 leveldir[leveldir_nr].filename, int2str(level_nr, 0));
1582
1583   sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1584
1585   if (!(file = fopen(filename, "w")))
1586   {
1587     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1588     return;
1589   }
1590
1591   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1592                                                  LEVELSETUP_COOKIE));
1593   while (list_entry)
1594   {
1595     if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1596       fprintf(file, "%s\n",
1597               getFormattedSetupEntry(list_entry->token, list_entry->value));
1598
1599     /* just to make things nicer :) */
1600     if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1601       fprintf(file, "\n");
1602
1603     list_entry = list_entry->next;
1604   }
1605
1606   fclose(file);
1607
1608   chmod(filename, SETUP_PERMS);
1609 }