8ccb2466eca853ac23df9f22c2b7f09a9a3dc68d
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <dirent.h>
16 #include <sys/stat.h>
17
18 #include "libgame/libgame.h"
19
20 #include "files.h"
21 #include "tools.h"
22 #include "tape.h"
23 #include "joystick.h"
24
25 #define MAX_FILENAME_LEN        256     /* maximal filename length   */
26 #define MAX_LINE_LEN            1000    /* maximal input line length */
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header */
29 #define LEVEL_HEADER_UNUSED     15      /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk  */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes   */
32 #define TAPE_HEADER_SIZE        20      /* size of tape file header  */
33 #define TAPE_HEADER_UNUSED      7       /* unused tape header bytes  */
34
35 /* file identifier strings */
36 #define LEVEL_COOKIE            "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
37 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE             "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
39 #define SETUP_COOKIE            "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE       "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE        "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define LEVEL_COOKIE_14         "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
46 #define TAPE_COOKIE_10          "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47
48 /* file names and filename extensions */
49 #if !defined(PLATFORM_MSDOS)
50 #define LEVELSETUP_DIRECTORY    "levelsetup"
51 #define SETUP_FILENAME          "setup.conf"
52 #define LEVELSETUP_FILENAME     "levelsetup.conf"
53 #define LEVELINFO_FILENAME      "levelinfo.conf"
54 #define LEVELFILE_EXTENSION     "level"
55 #define TAPEFILE_EXTENSION      "tape"
56 #define SCOREFILE_EXTENSION     "score"
57 #else
58 #define LEVELSETUP_DIRECTORY    "lvlsetup"
59 #define SETUP_FILENAME          "setup.cnf"
60 #define LEVELSETUP_FILENAME     "lvlsetup.cnf"
61 #define LEVELINFO_FILENAME      "lvlinfo.cnf"
62 #define LEVELFILE_EXTENSION     "lvl"
63 #define TAPEFILE_EXTENSION      "tap"
64 #define SCOREFILE_EXTENSION     "sco"
65 #endif
66
67 #if defined(PLATFORM_WIN32)
68 #ifndef S_IRGRP
69 #define S_IRGRP S_IRUSR
70 #endif
71 #ifndef S_IROTH
72 #define S_IROTH S_IRUSR
73 #endif
74 #ifndef S_IWGRP
75 #define S_IWGRP S_IWUSR
76 #endif
77 #ifndef S_IWOTH
78 #define S_IWOTH S_IWUSR
79 #endif
80 #ifndef S_IXGRP
81 #define S_IXGRP S_IXUSR
82 #endif
83 #ifndef S_IXOTH
84 #define S_IXOTH S_IXUSR
85 #endif
86 #endif  /* PLATFORM_WIN32 */
87
88 /* file permissions for newly written files */
89 #define MODE_R_ALL              (S_IRUSR | S_IRGRP | S_IROTH)
90 #define MODE_W_ALL              (S_IWUSR | S_IWGRP | S_IWOTH)
91 #define MODE_X_ALL              (S_IXUSR | S_IXGRP | S_IXOTH)
92 #define LEVEL_PERMS             (MODE_R_ALL | MODE_W_ALL)
93 #define SCORE_PERMS             LEVEL_PERMS
94 #define TAPE_PERMS              LEVEL_PERMS
95 #define SETUP_PERMS             LEVEL_PERMS
96
97 /* sort priorities of level series (also used as level series classes) */
98 #define LEVELCLASS_TUTORIAL_START       10
99 #define LEVELCLASS_TUTORIAL_END         99
100 #define LEVELCLASS_CLASSICS_START       100
101 #define LEVELCLASS_CLASSICS_END         199
102 #define LEVELCLASS_CONTRIBUTION_START   200
103 #define LEVELCLASS_CONTRIBUTION_END     299
104 #define LEVELCLASS_USER_START           300
105 #define LEVELCLASS_USER_END             399
106 #define LEVELCLASS_BD_START             400
107 #define LEVELCLASS_BD_END               499
108 #define LEVELCLASS_EM_START             500
109 #define LEVELCLASS_EM_END               599
110 #define LEVELCLASS_SP_START             600
111 #define LEVELCLASS_SP_END               699
112 #define LEVELCLASS_DX_START             700
113 #define LEVELCLASS_DX_END               799
114
115 #define LEVELCLASS_TUTORIAL             LEVELCLASS_TUTORIAL_START
116 #define LEVELCLASS_CLASSICS             LEVELCLASS_CLASSICS_START
117 #define LEVELCLASS_CONTRIBUTION         LEVELCLASS_CONTRIBUTION_START
118 #define LEVELCLASS_USER                 LEVELCLASS_USER_START
119 #define LEVELCLASS_BD                   LEVELCLASS_BD_START
120 #define LEVELCLASS_EM                   LEVELCLASS_EM_START
121 #define LEVELCLASS_SP                   LEVELCLASS_SP_START
122 #define LEVELCLASS_DX                   LEVELCLASS_DX_START
123
124 #define LEVELCLASS_UNDEFINED            999
125
126 #define NUM_LEVELCLASS_DESC     8
127 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
128 {
129   "Tutorial Levels",
130   "Classic Originals",
131   "Contributions",
132   "Private Levels",
133   "Boulderdash",
134   "Emerald Mine",
135   "Supaplex",
136   "DX Boulderdash"
137 };
138
139 #define IS_LEVELCLASS_TUTORIAL(p) \
140         ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
141          (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
142 #define IS_LEVELCLASS_CLASSICS(p) \
143         ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
144          (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
145 #define IS_LEVELCLASS_CONTRIBUTION(p) \
146         ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
147          (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
148 #define IS_LEVELCLASS_USER(p) \
149         ((p)->sort_priority >= LEVELCLASS_USER_START && \
150          (p)->sort_priority <= LEVELCLASS_USER_END)
151 #define IS_LEVELCLASS_BD(p) \
152         ((p)->sort_priority >= LEVELCLASS_BD_START && \
153          (p)->sort_priority <= LEVELCLASS_BD_END)
154 #define IS_LEVELCLASS_EM(p) \
155         ((p)->sort_priority >= LEVELCLASS_EM_START && \
156          (p)->sort_priority <= LEVELCLASS_EM_END)
157 #define IS_LEVELCLASS_SP(p) \
158         ((p)->sort_priority >= LEVELCLASS_SP_START && \
159          (p)->sort_priority <= LEVELCLASS_SP_END)
160 #define IS_LEVELCLASS_DX(p) \
161         ((p)->sort_priority >= LEVELCLASS_DX_START && \
162          (p)->sort_priority <= LEVELCLASS_DX_END)
163
164 #define LEVELCLASS(n)   (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
165                          IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
166                          IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
167                          IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
168                          IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
169                          IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
170                          IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
171                          IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
172                          LEVELCLASS_UNDEFINED)
173
174 #define LEVELCOLOR(n)   (IS_LEVELCLASS_TUTORIAL(n) ?            FC_BLUE : \
175                          IS_LEVELCLASS_CLASSICS(n) ?            FC_RED : \
176                          IS_LEVELCLASS_BD(n) ?                  FC_GREEN : \
177                          IS_LEVELCLASS_EM(n) ?                  FC_YELLOW : \
178                          IS_LEVELCLASS_SP(n) ?                  FC_GREEN : \
179                          IS_LEVELCLASS_DX(n) ?                  FC_YELLOW : \
180                          IS_LEVELCLASS_CONTRIBUTION(n) ?        FC_GREEN : \
181                          IS_LEVELCLASS_USER(n) ?                FC_RED : \
182                          FC_BLUE)
183
184 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ?            0 : \
185                          IS_LEVELCLASS_CLASSICS(n) ?            1 : \
186                          IS_LEVELCLASS_BD(n) ?                  2 : \
187                          IS_LEVELCLASS_EM(n) ?                  3 : \
188                          IS_LEVELCLASS_SP(n) ?                  4 : \
189                          IS_LEVELCLASS_DX(n) ?                  5 : \
190                          IS_LEVELCLASS_CONTRIBUTION(n) ?        6 : \
191                          IS_LEVELCLASS_USER(n) ?                7 : \
192                          9)
193
194 static int getFileVersionFromCookieString(const char *cookie)
195 {
196   const char *ptr_cookie1, *ptr_cookie2;
197   const char *pattern1 = "_FILE_VERSION_";
198   const char *pattern2 = "?.?";
199   const int len_cookie = strlen(cookie);
200   const int len_pattern1 = strlen(pattern1);
201   const int len_pattern2 = strlen(pattern2);
202   const int len_pattern = len_pattern1 + len_pattern2;
203   int version_major, version_minor;
204
205   if (len_cookie <= len_pattern)
206     return -1;
207
208   ptr_cookie1 = &cookie[len_cookie - len_pattern];
209   ptr_cookie2 = &cookie[len_cookie - len_pattern2];
210
211   if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
212     return -1;
213
214   if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
215       ptr_cookie2[1] != '.' ||
216       ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
217     return -1;
218
219   version_major = ptr_cookie2[0] - '0';
220   version_minor = ptr_cookie2[2] - '0';
221
222   return (version_major * 10 + version_minor);
223 }
224
225 boolean checkCookieString(const char *cookie, const char *template)
226 {
227   const char *pattern = "_FILE_VERSION_?.?";
228   const int len_cookie = strlen(cookie);
229   const int len_template = strlen(template);
230   const int len_pattern = strlen(pattern);
231
232   if (len_cookie != len_template)
233     return FALSE;
234
235   if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
236     return FALSE;
237
238   return TRUE;
239 }
240
241 char *getLevelClassDescription(struct LevelDirInfo *ldi)
242 {
243   int position = ldi->sort_priority / 100;
244
245   if (position >= 0 && position < NUM_LEVELCLASS_DESC)
246     return levelclass_desc[position];
247   else
248     return "Unknown Level Class";
249 }
250
251 static void SaveUserLevelInfo();                /* for 'InitUserLevelDir()' */
252 static char *getSetupLine(char *, int);         /* for 'SaveUserLevelInfo()' */
253
254 static char *getSetupDir()
255 {
256   return getUserDataDir();
257 }
258
259 static char *getUserLevelDir(char *level_subdir)
260 {
261   static char *userlevel_dir = NULL;
262   char *data_dir = getUserDataDir();
263   char *userlevel_subdir = LEVELS_DIRECTORY;
264
265   if (userlevel_dir)
266     free(userlevel_dir);
267
268   if (strlen(level_subdir) > 0)
269     userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
270   else
271     userlevel_dir = getPath2(data_dir, userlevel_subdir);
272
273   return userlevel_dir;
274 }
275
276 static char *getTapeDir(char *level_subdir)
277 {
278   static char *tape_dir = NULL;
279   char *data_dir = getUserDataDir();
280   char *tape_subdir = TAPES_DIRECTORY;
281
282   if (tape_dir)
283     free(tape_dir);
284
285   if (strlen(level_subdir) > 0)
286     tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
287   else
288     tape_dir = getPath2(data_dir, tape_subdir);
289
290   return tape_dir;
291 }
292
293 static char *getScoreDir(char *level_subdir)
294 {
295   static char *score_dir = NULL;
296   char *data_dir = options.rw_base_directory;
297   char *score_subdir = SCORES_DIRECTORY;
298
299   if (score_dir)
300     free(score_dir);
301
302   if (strlen(level_subdir) > 0)
303     score_dir = getPath3(data_dir, score_subdir, level_subdir);
304   else
305     score_dir = getPath2(data_dir, score_subdir);
306
307   return score_dir;
308 }
309
310 static char *getLevelSetupDir(char *level_subdir)
311 {
312   static char *levelsetup_dir = NULL;
313   char *data_dir = getUserDataDir();
314   char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
315
316   if (levelsetup_dir)
317     free(levelsetup_dir);
318
319   if (strlen(level_subdir) > 0)
320     levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
321   else
322     levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
323
324   return levelsetup_dir;
325 }
326
327 static char *getLevelFilename(int nr)
328 {
329   static char *filename = NULL;
330   char basename[MAX_FILENAME_LEN];
331
332   if (filename != NULL)
333     free(filename);
334
335   sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
336   filename = getPath3((leveldir_current->user_defined ?
337                        getUserLevelDir("") :
338                        options.level_directory),
339                       leveldir_current->fullpath,
340                       basename);
341
342   return filename;
343 }
344
345 static char *getTapeFilename(int nr)
346 {
347   static char *filename = NULL;
348   char basename[MAX_FILENAME_LEN];
349
350   if (filename != NULL)
351     free(filename);
352
353   sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
354   filename = getPath2(getTapeDir(leveldir_current->filename), basename);
355
356   return filename;
357 }
358
359 static char *getScoreFilename(int nr)
360 {
361   static char *filename = NULL;
362   char basename[MAX_FILENAME_LEN];
363
364   if (filename != NULL)
365     free(filename);
366
367   sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
368   filename = getPath2(getScoreDir(leveldir_current->filename), basename);
369
370   return filename;
371 }
372
373 static void InitTapeDirectory(char *level_subdir)
374 {
375   createDirectory(getUserDataDir(), "user data");
376   createDirectory(getTapeDir(""), "main tape");
377   createDirectory(getTapeDir(level_subdir), "level tape");
378 }
379
380 static void InitScoreDirectory(char *level_subdir)
381 {
382   createDirectory(getScoreDir(""), "main score");
383   createDirectory(getScoreDir(level_subdir), "level score");
384 }
385
386 static void InitUserLevelDirectory(char *level_subdir)
387 {
388   if (access(getUserLevelDir(level_subdir), F_OK) != 0)
389   {
390     createDirectory(getUserDataDir(), "user data");
391     createDirectory(getUserLevelDir(""), "main user level");
392     createDirectory(getUserLevelDir(level_subdir), "user level");
393
394     SaveUserLevelInfo();
395   }
396 }
397
398 static void InitLevelSetupDirectory(char *level_subdir)
399 {
400   createDirectory(getUserDataDir(), "user data");
401   createDirectory(getLevelSetupDir(""), "main level setup");
402   createDirectory(getLevelSetupDir(level_subdir), "level setup");
403 }
404
405 static void setLevelInfoToDefaults()
406 {
407   int i, x, y;
408
409   level.file_version = FILE_VERSION_ACTUAL;
410   level.game_version = GAME_VERSION_ACTUAL;
411
412   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
413   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
414   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
415
416   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
417   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
418
419   for(x=0; x<MAX_LEV_FIELDX; x++)
420     for(y=0; y<MAX_LEV_FIELDY; y++)
421       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
422
423   level.time = 100;
424   level.gems_needed = 0;
425   level.amoeba_speed = 10;
426   level.time_magic_wall = 10;
427   level.time_wheel = 10;
428   level.time_light = 10;
429   level.time_timegate = 10;
430   level.amoeba_content = EL_DIAMANT;
431   level.double_speed = FALSE;
432   level.gravity = FALSE;
433
434   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
435     level.name[i] = '\0';
436   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
437     level.author[i] = '\0';
438
439   strcpy(level.name, NAMELESS_LEVEL_NAME);
440   strcpy(level.author, ANONYMOUS_NAME);
441
442   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
443     level.score[i] = 10;
444
445   level.num_yam_contents = STD_ELEMENT_CONTENTS;
446   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
447     for(x=0; x<3; x++)
448       for(y=0; y<3; y++)
449         level.yam_content[i][x][y] =
450           (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
451
452   Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
453   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
454     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
455
456   BorderElement = EL_BETON;
457
458   /* try to determine better author name than 'anonymous' */
459   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
460   {
461     strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
462     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
463   }
464   else
465   {
466     switch (LEVELCLASS(leveldir_current))
467     {
468       case LEVELCLASS_TUTORIAL:
469         strcpy(level.author, PROGRAM_AUTHOR_STRING);
470         break;
471
472       case LEVELCLASS_CONTRIBUTION:
473         strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
474         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
475         break;
476
477       case LEVELCLASS_USER:
478         strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
479         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
480         break;
481
482       default:
483         /* keep default value */
484         break;
485     }
486   }
487 }
488
489 static int checkLevelElement(int element)
490 {
491   if (element >= EL_FIRST_RUNTIME_EL)
492   {
493     Error(ERR_WARN, "invalid level element %d", element);
494     element = EL_CHAR_FRAGE;
495   }
496
497   return element;
498 }
499
500 void OLD_LoadLevel(int level_nr)
501 {
502   int i, x, y;
503   char *filename = getLevelFilename(level_nr);
504   char cookie[MAX_LINE_LEN];
505   char chunk_name[CHUNK_ID_LEN + 1];
506   boolean encoding_16bit = FALSE;       /* default: maximal 256 elements */
507   int file_version = FILE_VERSION_ACTUAL;
508   int chunk_size;
509   FILE *file;
510
511   /* always start with reliable default values */
512   setLevelInfoToDefaults();
513
514   if (!(file = fopen(filename, MODE_READ)))
515   {
516     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
517     return;
518   }
519
520   /* check file identifier */
521   fgets(cookie, MAX_LINE_LEN, file);
522   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
523     cookie[strlen(cookie) - 1] = '\0';
524
525 #if 0
526   if (strcmp(cookie, LEVEL_COOKIE_10) == 0)     /* old 1.0 level format */
527     file_version = FILE_VERSION_1_0;
528   else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
529     file_version = FILE_VERSION_1_2;
530   else if (strcmp(cookie, LEVEL_COOKIE) != 0)   /* unknown level format */
531   {
532     Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
533     fclose(file);
534     return;
535   }
536 #else
537   if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
538   {
539     Error(ERR_WARN, "unknown format of level file '%s'", filename);
540     fclose(file);
541     return;
542   }
543
544   file_version = getFileVersionFromCookieString(cookie);
545 #endif
546
547   level.file_version = file_version;
548
549   /* read chunk "HEAD" */
550   if (file_version >= FILE_VERSION_1_2)
551   {
552     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
553     if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
554     {
555       Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
556       fclose(file);
557       return;
558     }
559   }
560
561   lev_fieldx = level.fieldx = fgetc(file);
562   lev_fieldy = level.fieldy = fgetc(file);
563
564   level.time        = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
565   level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
566
567   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
568     level.name[i] = fgetc(file);
569   level.name[MAX_LEVEL_NAME_LEN] = 0;
570
571   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
572     level.score[i] = fgetc(file);
573
574   level.num_yam_contents = STD_ELEMENT_CONTENTS;
575   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
576   {
577     for(y=0; y<3; y++)
578     {
579       for(x=0; x<3; x++)
580       {
581         if (i < STD_ELEMENT_CONTENTS)
582           level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
583         else
584           level.yam_content[i][x][y] = EL_LEERRAUM;
585       }
586     }
587   }
588
589   level.amoeba_speed    = fgetc(file);
590   level.time_magic_wall = fgetc(file);
591   level.time_wheel      = fgetc(file);
592   level.amoeba_content  = checkLevelElement(fgetc(file));
593   level.double_speed    = (fgetc(file) == 1 ? TRUE : FALSE);
594   level.gravity         = (fgetc(file) == 1 ? TRUE : FALSE);
595
596   encoding_16bit        = (fgetc(file) == 1 ? TRUE : FALSE);
597
598   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* skip unused header bytes */
599     fgetc(file);
600
601   if (file_version >= FILE_VERSION_1_2)
602   {
603     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
604
605     /* look for optional author chunk */
606     if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
607     {
608       for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
609         level.author[i] = fgetc(file);
610       level.author[MAX_LEVEL_NAME_LEN] = 0;
611
612       getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
613     }
614
615     /* look for optional content chunk */
616     if (strcmp(chunk_name, "CONT") == 0 &&
617         chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
618     {
619       fgetc(file);
620       level.num_yam_contents = fgetc(file);
621       fgetc(file);
622       fgetc(file);
623
624       if (level.num_yam_contents < 1 ||
625           level.num_yam_contents > MAX_ELEMENT_CONTENTS)
626       {
627 #if DEBUG
628         printf("WARNING: num_yam_contents == %d (corrected)\n",
629                level.num_yam_contents);
630 #endif
631         level.num_yam_contents = STD_ELEMENT_CONTENTS;
632       }
633
634       for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
635         for(y=0; y<3; y++)
636           for(x=0; x<3; x++)
637             level.yam_content[i][x][y] =
638               checkLevelElement(encoding_16bit ?
639                                 getFile16BitInteger(file,
640                                                     BYTE_ORDER_BIG_ENDIAN) :
641                                 fgetc(file));
642
643       getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
644     }
645
646     /* next check body chunk identifier and chunk size */
647     if (strcmp(chunk_name, "BODY") != 0 ||
648         chunk_size != lev_fieldx * lev_fieldy)
649     {
650       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
651       fclose(file);
652       return;
653     }
654   }
655
656   /* clear all other level fields (needed if resized in level editor later) */
657   for(x=0; x<MAX_LEV_FIELDX; x++)
658     for(y=0; y<MAX_LEV_FIELDY; y++)
659       Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
660
661   /* now read in the valid level fields from level file */
662   for(y=0; y<lev_fieldy; y++)
663     for(x=0; x<lev_fieldx; x++)
664       Feld[x][y] = Ur[x][y] =
665         checkLevelElement(encoding_16bit ?
666                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
667                           fgetc(file));
668
669   fclose(file);
670
671   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
672       IS_LEVELCLASS_USER(leveldir_current))
673   {
674     /* for user contributed and private levels, use the version of
675        the game engine the levels were created for */
676     level.game_version = file_version;
677
678     /* player was faster than monsters in pre-1.0 levels */
679     if (file_version == FILE_VERSION_1_0)
680     {
681       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
682       Error(ERR_WARN, "using high speed movement for player");
683       level.double_speed = TRUE;
684     }
685   }
686   else
687   {
688     /* always use the latest version of the game engine for all but
689        user contributed and private levels */
690     level.game_version = GAME_VERSION_ACTUAL;
691   }
692
693   /* determine border element for this level */
694   SetBorderElement();
695 }
696
697 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
698 {
699   while (bytes--)
700     fgetc(file);
701 }
702
703 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
704 {
705   while (bytes--)
706     fputc(0, file);
707 }
708
709 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
710 {
711   int i, x, y;
712
713   lev_fieldx = level->fieldx = fgetc(file);
714   lev_fieldy = level->fieldy = fgetc(file);
715
716   level->time           = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
717   level->gems_needed    = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
718
719   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
720     level->name[i] = fgetc(file);
721   level->name[MAX_LEVEL_NAME_LEN] = 0;
722
723   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
724     level->score[i] = fgetc(file);
725
726   level->num_yam_contents = STD_ELEMENT_CONTENTS;
727   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
728     for(y=0; y<3; y++)
729       for(x=0; x<3; x++)
730         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
731
732   level->amoeba_speed           = fgetc(file);
733   level->time_magic_wall        = fgetc(file);
734   level->time_wheel             = fgetc(file);
735   level->amoeba_content         = checkLevelElement(fgetc(file));
736   level->double_speed           = (fgetc(file) == 1 ? TRUE : FALSE);
737   level->gravity                = (fgetc(file) == 1 ? TRUE : FALSE);
738
739   level->encoding_16bit_field   = (fgetc(file) == 1 ? TRUE : FALSE);
740
741   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
742
743   return chunk_size;
744 }
745
746 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
747 {
748   int i;
749
750   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
751     level->author[i] = fgetc(file);
752   level->author[MAX_LEVEL_NAME_LEN] = 0;
753
754   return chunk_size;
755 }
756
757 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
758 {
759   int i, x, y;
760   int header_size = 4;
761   int content_size_type1 = MAX_ELEMENT_CONTENTS * 3 * 3;
762
763   int chunk_size_expected = header_size + content_size_type1;
764
765   /* Note: "chunk_size" was wrong before version 2.0 when elements are
766      stored with 16-bit encoding (and should be twice as big then).
767      Even worse, playfield data was stored 16-bit when only yamyam content
768      contained 16-bit elements and vice versa. */
769
770   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771     chunk_size_expected += content_size_type1;
772
773   if (chunk_size_expected != chunk_size)
774   {
775     ReadUnusedBytesFromFile(file, chunk_size);
776     return chunk_size_expected;
777   }
778
779   fgetc(file);
780   level->num_yam_contents = fgetc(file);
781   fgetc(file);
782   fgetc(file);
783
784   /* correct invalid number of content fields -- should never happen */
785   if (level->num_yam_contents < 1 ||
786       level->num_yam_contents > MAX_ELEMENT_CONTENTS)
787     level->num_yam_contents = STD_ELEMENT_CONTENTS;
788
789   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
790     for(y=0; y<3; y++)
791       for(x=0; x<3; x++)
792         level->yam_content[i][x][y] =
793           checkLevelElement(level->encoding_16bit_field ?
794                             getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
795                             fgetc(file));
796   return chunk_size;
797 }
798
799 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
800 {
801   int x, y;
802   int chunk_size_expected = level->fieldx * level->fieldy;
803
804   /* Note: "chunk_size" was wrong before version 2.0 when elements are
805      stored with 16-bit encoding (and should be twice as big then).
806      Even worse, playfield data was stored 16-bit when only yamyam content
807      contained 16-bit elements and vice versa. */
808
809   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
810     chunk_size_expected *= 2;
811
812   if (chunk_size_expected != chunk_size)
813   {
814     ReadUnusedBytesFromFile(file, chunk_size);
815     return chunk_size_expected;
816   }
817
818   for(y=0; y<level->fieldy; y++)
819     for(x=0; x<level->fieldx; x++)
820       Feld[x][y] = Ur[x][y] =
821         checkLevelElement(level->encoding_16bit_field ?
822                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
823                           fgetc(file));
824   return chunk_size;
825 }
826
827 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
828 {
829   int i, x, y;
830   int element;
831   int num_contents, content_xsize, content_ysize;
832   int content_array[MAX_ELEMENT_CONTENTS][3][3];
833
834   element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
835   num_contents = fgetc(file);
836   content_xsize = fgetc(file);
837   content_ysize = fgetc(file);
838   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
839
840   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
841     for(y=0; y<3; y++)
842       for(x=0; x<3; x++)
843         content_array[i][x][y] =
844           checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
845
846   /* correct invalid number of content fields -- should never happen */
847   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
848     num_contents = STD_ELEMENT_CONTENTS;
849
850   if (element == EL_MAMPFER)
851   {
852     level->num_yam_contents = num_contents;
853
854     for(i=0; i<num_contents; i++)
855       for(y=0; y<3; y++)
856         for(x=0; x<3; x++)
857           level->yam_content[i][x][y] = content_array[i][x][y];
858   }
859   else if (element == EL_AMOEBE_BD)
860   {
861     level->amoeba_content = content_array[0][0][0];
862   }
863   else
864   {
865     Error(ERR_WARN, "cannot load content for element '%d'", element);
866   }
867
868   return chunk_size;
869 }
870
871 void LoadLevel(int level_nr)
872 {
873   char *filename = getLevelFilename(level_nr);
874   char cookie[MAX_LINE_LEN];
875   char chunk_name[CHUNK_ID_LEN + 1];
876   int chunk_size;
877   FILE *file;
878
879   /* always start with reliable default values */
880   setLevelInfoToDefaults();
881
882   if (!(file = fopen(filename, MODE_READ)))
883   {
884     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
885     return;
886   }
887
888   /* check file identifier */
889   fgets(cookie, MAX_LINE_LEN, file);
890   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
891     cookie[strlen(cookie) - 1] = '\0';
892
893   if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
894   {
895     Error(ERR_WARN, "unknown format of level file '%s'", filename);
896     fclose(file);
897     return;
898   }
899
900   if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
901   {
902     Error(ERR_WARN, "unsupported version of level file '%s'", filename);
903     fclose(file);
904     return;
905   }
906
907   if (level.file_version < FILE_VERSION_1_2)
908   {
909     /* level files from versions before 1.2.0 without chunk structure */
910     LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
911     LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
912   }
913   else
914   {
915     static struct
916     {
917       char *name;
918       int size;
919       int (*loader)(struct LevelInfo *, FILE *, int);
920     }
921     chunk_info[] =
922     {
923       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
924       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
925       { "CONT", -1,                     LoadLevel_CONT },
926       { "BODY", -1,                     LoadLevel_BODY },
927       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
928       {  NULL,  0,                      NULL }
929     };
930
931     while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
932     {
933       int i = 0;
934
935       while (chunk_info[i].name != NULL &&
936              strcmp(chunk_name, chunk_info[i].name) != 0)
937         i++;
938
939       if (chunk_info[i].name == NULL)
940       {
941         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
942               chunk_name, filename);
943         ReadUnusedBytesFromFile(file, chunk_size);
944       }
945       else if (chunk_info[i].size != -1 &&
946                chunk_info[i].size != chunk_size)
947       {
948         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
949               chunk_size, chunk_name, filename);
950         ReadUnusedBytesFromFile(file, chunk_size);
951       }
952       else
953       {
954         /* call function to load this level chunk */
955         int chunk_size_expected =
956           (chunk_info[i].loader)(&level, file, chunk_size);
957
958         /* the size of some chunks cannot be checked before reading other
959            chunks first (like "HEAD" and "BODY") or before reading some
960            header information first (like "CONT"), so check them here */
961         if (chunk_size_expected != chunk_size)
962         {
963           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
964                 chunk_size, chunk_name, filename);
965         }
966       }
967     }
968   }
969
970   fclose(file);
971
972   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
973       IS_LEVELCLASS_USER(leveldir_current))
974   {
975     /* for user contributed and private levels, use the version of
976        the game engine the levels were created for */
977     level.game_version = level.file_version;
978
979     /* player was faster than monsters in pre-1.0 levels */
980     if (level.file_version == FILE_VERSION_1_0)
981     {
982       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
983       Error(ERR_WARN, "using high speed movement for player");
984       level.double_speed = TRUE;
985     }
986   }
987   else
988   {
989     /* always use the latest version of the game engine for all but
990        user contributed and private levels */
991     level.game_version = GAME_VERSION_ACTUAL;
992   }
993
994   /* determine border element for this level */
995   SetBorderElement();
996 }
997
998 void OLD_SaveLevel(int level_nr)
999 {
1000   int i, x, y;
1001   char *filename = getLevelFilename(level_nr);
1002 #if 0
1003   boolean encoding_16bit_amoeba = FALSE;
1004   boolean encoding_16bit_yamyam = FALSE;
1005 #endif
1006   boolean encoding_16bit = FALSE;       /* default: only 8-bit elements */
1007   char *oldest_possible_cookie;
1008   FILE *file;
1009
1010   if (!(file = fopen(filename, MODE_WRITE)))
1011   {
1012     Error(ERR_WARN, "cannot save level file '%s'", filename);
1013     return;
1014   }
1015
1016   /* check yam content for 16-bit elements */
1017   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1018     for(y=0; y<3; y++)
1019       for(x=0; x<3; x++)
1020         if (level.yam_content[i][x][y] > 255)
1021           encoding_16bit = TRUE;
1022
1023   /* check level field for 16-bit elements */
1024   for(y=0; y<lev_fieldy; y++) 
1025     for(x=0; x<lev_fieldx; x++) 
1026       if (Ur[x][y] > 255)
1027         encoding_16bit = TRUE;
1028
1029   oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1030
1031   fputs(oldest_possible_cookie, file);          /* file identifier */
1032   fputc('\n', file);
1033
1034   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1035
1036   fputc(level.fieldx, file);
1037   fputc(level.fieldy, file);
1038
1039   putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1040   putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1041
1042   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1043     fputc(level.name[i], file);
1044   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1045     fputc(level.score[i], file);
1046   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1047     for(y=0; y<3; y++)
1048       for(x=0; x<3; x++)
1049         fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1050   fputc(level.amoeba_speed, file);
1051   fputc(level.time_magic_wall, file);
1052   fputc(level.time_wheel, file);
1053   fputc(level.amoeba_content, file);
1054   fputc((level.double_speed ? 1 : 0), file);
1055   fputc((level.gravity ? 1 : 0), file);
1056
1057   fputc((encoding_16bit ? 1 : 0), file);
1058
1059   for(i=0; i<LEVEL_HEADER_UNUSED; i++)  /* set unused header bytes to zero */
1060     fputc(0, file);
1061
1062   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1063
1064   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1065     fputc(level.author[i], file);
1066
1067   putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1068                BYTE_ORDER_BIG_ENDIAN);
1069
1070   fputc(EL_MAMPFER, file);
1071   fputc(level.num_yam_contents, file);
1072   fputc(0, file);
1073   fputc(0, file);
1074
1075   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1076     for(y=0; y<3; y++)
1077       for(x=0; x<3; x++)
1078         if (encoding_16bit)
1079           putFile16BitInteger(file, level.yam_content[i][x][y],
1080                               BYTE_ORDER_BIG_ENDIAN);
1081         else
1082           fputc(level.yam_content[i][x][y], file);
1083
1084   putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1085
1086   for(y=0; y<lev_fieldy; y++) 
1087     for(x=0; x<lev_fieldx; x++) 
1088       if (encoding_16bit)
1089         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1090       else
1091         fputc(Ur[x][y], file);
1092
1093   fclose(file);
1094
1095   chmod(filename, LEVEL_PERMS);
1096 }
1097
1098 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1099 {
1100   int i, x, y;
1101
1102   fputc(level->fieldx, file);
1103   fputc(level->fieldy, file);
1104
1105   putFile16BitInteger(file, level->time,        BYTE_ORDER_BIG_ENDIAN);
1106   putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1107
1108   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1109     fputc(level->name[i], file);
1110
1111   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1112     fputc(level->score[i], file);
1113
1114   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1115     for(y=0; y<3; y++)
1116       for(x=0; x<3; x++)
1117         fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1118                level->yam_content[i][x][y]),
1119               file);
1120   fputc(level->amoeba_speed, file);
1121   fputc(level->time_magic_wall, file);
1122   fputc(level->time_wheel, file);
1123   fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1124         file);
1125   fputc((level->double_speed ? 1 : 0), file);
1126   fputc((level->gravity ? 1 : 0), file);
1127
1128   fputc((level->encoding_16bit_field ? 1 : 0), file);
1129
1130   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1131 }
1132
1133 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1134 {
1135   int i;
1136
1137   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1138     fputc(level->author[i], file);
1139 }
1140
1141 #if 0
1142 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1143 {
1144   int i, x, y;
1145
1146   fputc(EL_MAMPFER, file);
1147   fputc(level->num_yam_contents, file);
1148   fputc(0, file);
1149   fputc(0, file);
1150
1151   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1152     for(y=0; y<3; y++)
1153       for(x=0; x<3; x++)
1154         if (level->encoding_16bit_field)
1155           putFile16BitInteger(file, level->yam_content[i][x][y],
1156                               BYTE_ORDER_BIG_ENDIAN);
1157         else
1158           fputc(level->yam_content[i][x][y], file);
1159 }
1160 #endif
1161
1162 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1163 {
1164   int x, y;
1165
1166   for(y=0; y<lev_fieldy; y++) 
1167     for(x=0; x<lev_fieldx; x++) 
1168       if (level->encoding_16bit_field)
1169         putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1170       else
1171         fputc(Ur[x][y], file);
1172 }
1173
1174 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1175 {
1176   int i, x, y;
1177   int num_contents, content_xsize, content_ysize;
1178   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1179
1180   if (element == EL_MAMPFER)
1181   {
1182     num_contents = level->num_yam_contents;
1183     content_xsize = 3;
1184     content_ysize = 3;
1185
1186     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1187       for(y=0; y<3; y++)
1188         for(x=0; x<3; x++)
1189           content_array[i][x][y] = level->yam_content[i][x][y];
1190   }
1191   else if (element == EL_AMOEBE_BD)
1192   {
1193     num_contents = 1;
1194     content_xsize = 1;
1195     content_ysize = 1;
1196
1197     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1198       for(y=0; y<3; y++)
1199         for(x=0; x<3; x++)
1200           content_array[i][x][y] = EL_LEERRAUM;
1201     content_array[0][0][0] = level->amoeba_content;
1202   }
1203   else
1204   {
1205     /* chunk header already written -- write empty chunk data */
1206     for(i=0; i<LEVEL_CHUNK_CNT2_SIZE; i++)
1207       fputc(0, file);
1208
1209     Error(ERR_WARN, "cannot save content for element '%d'", element);
1210     return;
1211   }
1212
1213   putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1214   fputc(num_contents, file);
1215   fputc(content_xsize, file);
1216   fputc(content_ysize, file);
1217   for(i=0; i<LEVEL_CHUNK_CNT2_UNUSED; i++)
1218     fputc(0, file);
1219
1220   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1221     for(y=0; y<3; y++)
1222       for(x=0; x<3; x++)
1223         putFile16BitInteger(file, content_array[i][x][y],
1224                             BYTE_ORDER_BIG_ENDIAN);
1225 }
1226
1227 void SaveLevel(int level_nr)
1228 {
1229   int i, x, y;
1230   char *filename = getLevelFilename(level_nr);
1231   int chunk_size;
1232   FILE *file;
1233
1234   if (!(file = fopen(filename, MODE_WRITE)))
1235   {
1236     Error(ERR_WARN, "cannot save level file '%s'", filename);
1237     return;
1238   }
1239
1240   /* check level field for 16-bit elements */
1241   for(y=0; y<level.fieldy; y++) 
1242     for(x=0; x<level.fieldx; x++) 
1243       if (Ur[x][y] > 255)
1244         level.encoding_16bit_field = TRUE;
1245
1246   /* check yamyam content for 16-bit elements */
1247   for(i=0; i<level.num_yam_contents; i++)
1248     for(y=0; y<3; y++)
1249       for(x=0; x<3; x++)
1250         if (level.yam_content[i][x][y] > 255)
1251           level.encoding_16bit_yamyam = TRUE;
1252
1253   /* check amoeba content for 16-bit elements */
1254   if (level.amoeba_content > 255)
1255     level.encoding_16bit_amoeba = TRUE;
1256
1257   fputs(LEVEL_COOKIE, file);            /* file identifier */
1258   fputc('\n', file);
1259
1260   putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1261   SaveLevel_HEAD(&level, file);
1262
1263   putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1264   SaveLevel_AUTH(&level, file);
1265
1266 #if 0
1267   if (level.encoding_16bit_field)       /* obsolete since new "CNT2" chunk */
1268   {
1269     chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1270
1271     putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1272     SaveLevel_CONT(&level, file);
1273   }
1274 #endif
1275
1276   chunk_size =
1277     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1278   putFileChunk(file, "BODY", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1279   SaveLevel_BODY(&level, file);
1280
1281   if (level.encoding_16bit_yamyam ||
1282       level.num_yam_contents != STD_ELEMENT_CONTENTS)
1283   {
1284     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1285     SaveLevel_CNT2(&level, file, EL_MAMPFER);
1286   }
1287
1288   if (level.encoding_16bit_amoeba)
1289   {
1290     putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1291     SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1292   }
1293
1294   fclose(file);
1295
1296   chmod(filename, LEVEL_PERMS);
1297 }
1298
1299 void LoadTape(int level_nr)
1300 {
1301   int i, j;
1302   char *filename = getTapeFilename(level_nr);
1303   char cookie[MAX_LINE_LEN];
1304   char chunk_name[CHUNK_ID_LEN + 1];
1305   FILE *file;
1306   int num_participating_players;
1307   int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1308   int chunk_size;
1309
1310   /* always start with reliable default values (empty tape) */
1311   tape.file_version = FILE_VERSION_ACTUAL;
1312   tape.game_version = GAME_VERSION_ACTUAL;
1313   TapeErase();
1314
1315   /* default values (also for pre-1.2 tapes) with only the first player */
1316   tape.player_participates[0] = TRUE;
1317   for(i=1; i<MAX_PLAYERS; i++)
1318     tape.player_participates[i] = FALSE;
1319
1320   /* at least one (default: the first) player participates in every tape */
1321   num_participating_players = 1;
1322
1323   if (!(file = fopen(filename, MODE_READ)))
1324     return;
1325
1326   /* check file identifier */
1327   fgets(cookie, MAX_LINE_LEN, file);
1328   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1329     cookie[strlen(cookie) - 1] = '\0';
1330
1331 #if 0
1332   if (strcmp(cookie, TAPE_COOKIE_10) == 0)      /* old 1.0 tape format */
1333     file_version = FILE_VERSION_1_0;
1334   else if (strcmp(cookie, TAPE_COOKIE) != 0)    /* unknown tape format */
1335   {
1336     Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1337     fclose(file);
1338     return;
1339   }
1340 #else
1341   if (!checkCookieString(cookie, TAPE_COOKIE))  /* unknown file format */
1342   {
1343     Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1344     fclose(file);
1345     return;
1346   }
1347
1348   file_version = getFileVersionFromCookieString(cookie);
1349 #endif
1350
1351   tape.file_version = file_version;
1352   tape.game_version = file_version;
1353
1354   /* read chunk "HEAD" */
1355   if (file_version >= FILE_VERSION_1_2)
1356   {
1357     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1358     if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1359     {
1360       Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1361       fclose(file);
1362       return;
1363     }
1364   }
1365
1366   tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1367   tape.date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1368   tape.length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1369
1370   /* read header fields that are new since version 1.2 */
1371   if (file_version >= FILE_VERSION_1_2)
1372   {
1373     byte store_participating_players = fgetc(file);
1374
1375     for(i=0; i<TAPE_HEADER_UNUSED; i++)         /* skip unused header bytes */
1376       fgetc(file);
1377
1378     /* since version 1.2, tapes store which players participate in the tape */
1379     num_participating_players = 0;
1380     for(i=0; i<MAX_PLAYERS; i++)
1381     {
1382       tape.player_participates[i] = FALSE;
1383
1384       if (store_participating_players & (1 << i))
1385       {
1386         tape.player_participates[i] = TRUE;
1387         num_participating_players++;
1388       }
1389     }
1390   }
1391
1392   tape.level_nr = level_nr;
1393   tape.counter = 0;
1394   tape.changed = FALSE;
1395
1396   tape.recording = FALSE;
1397   tape.playing = FALSE;
1398   tape.pausing = FALSE;
1399
1400   /* read chunk "BODY" */
1401   if (file_version >= FILE_VERSION_1_2)
1402   {
1403     getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1404     if (strcmp(chunk_name, "BODY") ||
1405         chunk_size != (num_participating_players + 1) * tape.length)
1406     {
1407       Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1408       fclose(file);
1409       return;
1410     }
1411   }
1412
1413   for(i=0; i<tape.length; i++)
1414   {
1415     if (i >= MAX_TAPELEN)
1416       break;
1417
1418     for(j=0; j<MAX_PLAYERS; j++)
1419     {
1420       tape.pos[i].action[j] = MV_NO_MOVING;
1421
1422       if (tape.player_participates[j])
1423         tape.pos[i].action[j] = fgetc(file);
1424     }
1425
1426     tape.pos[i].delay = fgetc(file);
1427
1428     if (file_version == FILE_VERSION_1_0)
1429     {
1430       /* eliminate possible diagonal moves in old tapes */
1431       /* this is only for backward compatibility */
1432
1433       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1434       byte action = tape.pos[i].action[0];
1435       int k, num_moves = 0;
1436
1437       for (k=0; k<4; k++)
1438       {
1439         if (action & joy_dir[k])
1440         {
1441           tape.pos[i + num_moves].action[0] = joy_dir[k];
1442           if (num_moves > 0)
1443             tape.pos[i + num_moves].delay = 0;
1444           num_moves++;
1445         }
1446       }
1447
1448       if (num_moves > 1)
1449       {
1450         num_moves--;
1451         i += num_moves;
1452         tape.length += num_moves;
1453       }
1454     }
1455
1456     if (feof(file))
1457       break;
1458   }
1459
1460   fclose(file);
1461
1462   if (i != tape.length)
1463     Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1464
1465   tape.length_seconds = GetTapeLength();
1466 }
1467
1468 void SaveTape(int level_nr)
1469 {
1470   int i;
1471   char *filename = getTapeFilename(level_nr);
1472   FILE *file;
1473   boolean new_tape = TRUE;
1474   byte store_participating_players;
1475   int num_participating_players;
1476
1477   InitTapeDirectory(leveldir_current->filename);
1478
1479   /* if a tape still exists, ask to overwrite it */
1480   if (access(filename, F_OK) == 0)
1481   {
1482     new_tape = FALSE;
1483     if (!Request("Replace old tape ?", REQ_ASK))
1484       return;
1485   }
1486
1487   /* count number of players and set corresponding bits for compact storage */
1488   store_participating_players = 0;
1489   num_participating_players = 0;
1490   for(i=0; i<MAX_PLAYERS; i++)
1491   {
1492     if (tape.player_participates[i])
1493     {
1494       num_participating_players++;
1495       store_participating_players |= (1 << i);
1496     }
1497   }
1498
1499   if (!(file = fopen(filename, MODE_WRITE)))
1500   {
1501     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1502     return;
1503   }
1504
1505   fputs(TAPE_COOKIE, file);             /* file identifier */
1506   fputc('\n', file);
1507
1508   putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1509
1510   putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1511   putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1512   putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1513
1514   fputc(store_participating_players, file);
1515
1516   for(i=0; i<TAPE_HEADER_UNUSED; i++)   /* set unused header bytes to zero */
1517     fputc(0, file);
1518
1519   putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1520                BYTE_ORDER_BIG_ENDIAN);
1521
1522   for(i=0; i<tape.length; i++)
1523   {
1524     int j;
1525
1526     for(j=0; j<MAX_PLAYERS; j++)
1527       if (tape.player_participates[j])
1528         fputc(tape.pos[i].action[j], file);
1529
1530     fputc(tape.pos[i].delay, file);
1531   }
1532
1533   fclose(file);
1534
1535   chmod(filename, TAPE_PERMS);
1536
1537   tape.changed = FALSE;
1538
1539   if (new_tape)
1540     Request("tape saved !", REQ_CONFIRM);
1541 }
1542
1543 void LoadScore(int level_nr)
1544 {
1545   int i;
1546   char *filename = getScoreFilename(level_nr);
1547   char cookie[MAX_LINE_LEN];
1548   char line[MAX_LINE_LEN];
1549   char *line_ptr;
1550   FILE *file;
1551
1552   /* always start with reliable default values */
1553   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1554   {
1555     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1556     highscore[i].Score = 0;
1557   }
1558
1559   if (!(file = fopen(filename, MODE_READ)))
1560     return;
1561
1562   /* check file identifier */
1563   fgets(cookie, MAX_LINE_LEN, file);
1564   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1565     cookie[strlen(cookie) - 1] = '\0';
1566
1567 #if 0
1568   if (strcmp(cookie, SCORE_COOKIE) != 0)
1569   {
1570     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1571     fclose(file);
1572     return;
1573   }
1574 #else
1575   if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1576   {
1577     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1578     fclose(file);
1579     return;
1580   }
1581 #endif
1582
1583   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1584   {
1585     fscanf(file, "%d", &highscore[i].Score);
1586     fgets(line, MAX_LINE_LEN, file);
1587
1588     if (line[strlen(line) - 1] == '\n')
1589       line[strlen(line) - 1] = '\0';
1590
1591     for (line_ptr = line; *line_ptr; line_ptr++)
1592     {
1593       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1594       {
1595         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1596         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1597         break;
1598       }
1599     }
1600   }
1601
1602   fclose(file);
1603 }
1604
1605 void SaveScore(int level_nr)
1606 {
1607   int i;
1608   char *filename = getScoreFilename(level_nr);
1609   FILE *file;
1610
1611   InitScoreDirectory(leveldir_current->filename);
1612
1613   if (!(file = fopen(filename, MODE_WRITE)))
1614   {
1615     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1616     return;
1617   }
1618
1619   fprintf(file, "%s\n\n", SCORE_COOKIE);
1620
1621   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1622     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1623
1624   fclose(file);
1625
1626   chmod(filename, SCORE_PERMS);
1627 }
1628
1629 #define TOKEN_STR_FILE_IDENTIFIER       "file_identifier"
1630 #define TOKEN_STR_LAST_LEVEL_SERIES     "last_level_series"
1631 #define TOKEN_STR_LAST_PLAYED_LEVEL     "last_played_level"
1632 #define TOKEN_STR_HANDICAP_LEVEL        "handicap_level"
1633 #define TOKEN_STR_PLAYER_PREFIX         "player_"
1634
1635 #define TOKEN_VALUE_POSITION            30
1636
1637 /* global setup */
1638 #define SETUP_TOKEN_PLAYER_NAME         0
1639 #define SETUP_TOKEN_SOUND               1
1640 #define SETUP_TOKEN_SOUND_LOOPS         2
1641 #define SETUP_TOKEN_SOUND_MUSIC         3
1642 #define SETUP_TOKEN_SOUND_SIMPLE        4
1643
1644 #if 0
1645 #define SETUP_TOKEN_TOONS               5
1646 #define SETUP_TOKEN_DOUBLE_BUFFERING    6
1647 #endif
1648
1649 #define SETUP_TOKEN_SCROLL_DELAY        5
1650 #define SETUP_TOKEN_SOFT_SCROLLING      6
1651 #define SETUP_TOKEN_FADING              7
1652 #define SETUP_TOKEN_AUTORECORD          8
1653 #define SETUP_TOKEN_QUICK_DOORS         9
1654 #define SETUP_TOKEN_TEAM_MODE           10
1655 #define SETUP_TOKEN_HANDICAP            11
1656 #define SETUP_TOKEN_TIME_LIMIT          12
1657 #define SETUP_TOKEN_FULLSCREEN          13
1658
1659 /* player setup */
1660 #define SETUP_TOKEN_USE_JOYSTICK        14
1661 #define SETUP_TOKEN_JOY_DEVICE_NAME     15
1662 #define SETUP_TOKEN_JOY_XLEFT           16
1663 #define SETUP_TOKEN_JOY_XMIDDLE         17
1664 #define SETUP_TOKEN_JOY_XRIGHT          18
1665 #define SETUP_TOKEN_JOY_YUPPER          19
1666 #define SETUP_TOKEN_JOY_YMIDDLE         20
1667 #define SETUP_TOKEN_JOY_YLOWER          21
1668 #define SETUP_TOKEN_JOY_SNAP            22
1669 #define SETUP_TOKEN_JOY_BOMB            23
1670 #define SETUP_TOKEN_KEY_LEFT            24
1671 #define SETUP_TOKEN_KEY_RIGHT           25
1672 #define SETUP_TOKEN_KEY_UP              26
1673 #define SETUP_TOKEN_KEY_DOWN            27
1674 #define SETUP_TOKEN_KEY_SNAP            28
1675 #define SETUP_TOKEN_KEY_BOMB            29
1676
1677 /* level directory info */
1678 #define LEVELINFO_TOKEN_NAME            30
1679 #define LEVELINFO_TOKEN_NAME_SHORT      31
1680 #define LEVELINFO_TOKEN_NAME_SORTING    32
1681 #define LEVELINFO_TOKEN_AUTHOR          33
1682 #define LEVELINFO_TOKEN_IMPORTED_FROM   34
1683 #define LEVELINFO_TOKEN_LEVELS          35
1684 #define LEVELINFO_TOKEN_FIRST_LEVEL     36
1685 #define LEVELINFO_TOKEN_SORT_PRIORITY   37
1686 #define LEVELINFO_TOKEN_LEVEL_GROUP     38
1687 #define LEVELINFO_TOKEN_READONLY        39
1688
1689 #define FIRST_GLOBAL_SETUP_TOKEN        SETUP_TOKEN_PLAYER_NAME
1690 #define LAST_GLOBAL_SETUP_TOKEN         SETUP_TOKEN_FULLSCREEN
1691
1692 #define FIRST_PLAYER_SETUP_TOKEN        SETUP_TOKEN_USE_JOYSTICK
1693 #define LAST_PLAYER_SETUP_TOKEN         SETUP_TOKEN_KEY_BOMB
1694
1695 #define FIRST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_NAME
1696 #define LAST_LEVELINFO_TOKEN            LEVELINFO_TOKEN_READONLY
1697
1698 #define TYPE_BOOLEAN                    1
1699 #define TYPE_SWITCH                     2
1700 #define TYPE_KEY                        3
1701 #define TYPE_INTEGER                    4
1702 #define TYPE_STRING                     5
1703
1704 static struct SetupInfo si;
1705 static struct SetupInputInfo sii;
1706 static struct LevelDirInfo ldi;
1707 static struct
1708 {
1709   int type;
1710   void *value;
1711   char *text;
1712 } token_info[] =
1713 {
1714   /* global setup */
1715   { TYPE_STRING,  &si.player_name,      "player_name"                   },
1716   { TYPE_SWITCH,  &si.sound,            "sound"                         },
1717   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
1718   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
1719   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
1720
1721 #if 0
1722   { TYPE_SWITCH,  &si.toons,            "toons"                         },
1723   { TYPE_SWITCH,  &si.double_buffering, "double_buffering"              },
1724 #endif
1725
1726   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
1727   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
1728   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
1729   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
1730   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
1731   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
1732   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
1733   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
1734   { TYPE_SWITCH,  &si.fullscreen,       "fullscreen"                    },
1735
1736   /* player setup */
1737   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1738   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1739   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1740   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1741   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1742   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1743   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1744   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1745   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1746   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1747   { TYPE_KEY,     &sii.key.left,        ".key.move_left"                },
1748   { TYPE_KEY,     &sii.key.right,       ".key.move_right"               },
1749   { TYPE_KEY,     &sii.key.up,          ".key.move_up"                  },
1750   { TYPE_KEY,     &sii.key.down,        ".key.move_down"                },
1751   { TYPE_KEY,     &sii.key.snap,        ".key.snap_field"               },
1752   { TYPE_KEY,     &sii.key.bomb,        ".key.place_bomb"               },
1753
1754   /* level directory info */
1755   { TYPE_STRING,  &ldi.name,            "name"                          },
1756   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
1757   { TYPE_STRING,  &ldi.name_sorting,    "name_sorting"                  },
1758   { TYPE_STRING,  &ldi.author,          "author"                        },
1759   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
1760   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
1761   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
1762   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
1763   { TYPE_BOOLEAN, &ldi.level_group,     "level_group"                   },
1764   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
1765 };
1766
1767 static char *string_tolower(char *s)
1768 {
1769   static char s_lower[100];
1770   int i;
1771
1772   if (strlen(s) >= 100)
1773     return s;
1774
1775   strcpy(s_lower, s);
1776
1777   for (i=0; i<strlen(s_lower); i++)
1778     s_lower[i] = tolower(s_lower[i]);
1779
1780   return s_lower;
1781 }
1782
1783 static int get_string_integer_value(char *s)
1784 {
1785   static char *number_text[][3] =
1786   {
1787     { "0", "zero", "null", },
1788     { "1", "one", "first" },
1789     { "2", "two", "second" },
1790     { "3", "three", "third" },
1791     { "4", "four", "fourth" },
1792     { "5", "five", "fifth" },
1793     { "6", "six", "sixth" },
1794     { "7", "seven", "seventh" },
1795     { "8", "eight", "eighth" },
1796     { "9", "nine", "ninth" },
1797     { "10", "ten", "tenth" },
1798     { "11", "eleven", "eleventh" },
1799     { "12", "twelve", "twelfth" },
1800   };
1801
1802   int i, j;
1803
1804   for (i=0; i<13; i++)
1805     for (j=0; j<3; j++)
1806       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1807         return i;
1808
1809   return atoi(s);
1810 }
1811
1812 static boolean get_string_boolean_value(char *s)
1813 {
1814   if (strcmp(string_tolower(s), "true") == 0 ||
1815       strcmp(string_tolower(s), "yes") == 0 ||
1816       strcmp(string_tolower(s), "on") == 0 ||
1817       get_string_integer_value(s) == 1)
1818     return TRUE;
1819   else
1820     return FALSE;
1821 }
1822
1823 static char *getFormattedSetupEntry(char *token, char *value)
1824 {
1825   int i;
1826   static char entry[MAX_LINE_LEN];
1827
1828   sprintf(entry, "%s:", token);
1829   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1830     entry[i] = ' ';
1831   entry[i] = '\0';
1832
1833   strcat(entry, value);
1834
1835   return entry;
1836 }
1837
1838 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1839 {
1840   if (!setup_file_list)
1841     return;
1842
1843   if (setup_file_list->token)
1844     free(setup_file_list->token);
1845   if (setup_file_list->value)
1846     free(setup_file_list->value);
1847   if (setup_file_list->next)
1848     freeSetupFileList(setup_file_list->next);
1849   free(setup_file_list);
1850 }
1851
1852 static struct SetupFileList *newSetupFileList(char *token, char *value)
1853 {
1854   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1855
1856   new->token = checked_malloc(strlen(token) + 1);
1857   strcpy(new->token, token);
1858
1859   new->value = checked_malloc(strlen(value) + 1);
1860   strcpy(new->value, value);
1861
1862   new->next = NULL;
1863
1864   return new;
1865 }
1866
1867 static char *getTokenValue(struct SetupFileList *setup_file_list,
1868                            char *token)
1869 {
1870   if (!setup_file_list)
1871     return NULL;
1872
1873   if (strcmp(setup_file_list->token, token) == 0)
1874     return setup_file_list->value;
1875   else
1876     return getTokenValue(setup_file_list->next, token);
1877 }
1878
1879 static void setTokenValue(struct SetupFileList *setup_file_list,
1880                           char *token, char *value)
1881 {
1882   if (!setup_file_list)
1883     return;
1884
1885   if (strcmp(setup_file_list->token, token) == 0)
1886   {
1887     free(setup_file_list->value);
1888     setup_file_list->value = checked_malloc(strlen(value) + 1);
1889     strcpy(setup_file_list->value, value);
1890   }
1891   else if (setup_file_list->next == NULL)
1892     setup_file_list->next = newSetupFileList(token, value);
1893   else
1894     setTokenValue(setup_file_list->next, token, value);
1895 }
1896
1897 #ifdef DEBUG
1898 static void printSetupFileList(struct SetupFileList *setup_file_list)
1899 {
1900   if (!setup_file_list)
1901     return;
1902
1903   printf("token: '%s'\n", setup_file_list->token);
1904   printf("value: '%s'\n", setup_file_list->value);
1905
1906   printSetupFileList(setup_file_list->next);
1907 }
1908 #endif
1909
1910 static struct SetupFileList *loadSetupFileList(char *filename)
1911 {
1912   int line_len;
1913   char line[MAX_LINE_LEN];
1914   char *token, *value, *line_ptr;
1915   struct SetupFileList *setup_file_list = newSetupFileList("", "");
1916   struct SetupFileList *first_valid_list_entry;
1917
1918   FILE *file;
1919
1920   if (!(file = fopen(filename, MODE_READ)))
1921   {
1922     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1923     return NULL;
1924   }
1925
1926   while(!feof(file))
1927   {
1928     /* read next line of input file */
1929     if (!fgets(line, MAX_LINE_LEN, file))
1930       break;
1931
1932     /* cut trailing comment or whitespace from input line */
1933     for (line_ptr = line; *line_ptr; line_ptr++)
1934     {
1935       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1936       {
1937         *line_ptr = '\0';
1938         break;
1939       }
1940     }
1941
1942     /* cut trailing whitespaces from input line */
1943     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1944       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1945         *line_ptr = '\0';
1946
1947     /* ignore empty lines */
1948     if (*line == '\0')
1949       continue;
1950
1951     line_len = strlen(line);
1952
1953     /* cut leading whitespaces from token */
1954     for (token = line; *token; token++)
1955       if (*token != ' ' && *token != '\t')
1956         break;
1957
1958     /* find end of token */
1959     for (line_ptr = token; *line_ptr; line_ptr++)
1960     {
1961       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1962       {
1963         *line_ptr = '\0';
1964         break;
1965       }
1966     }
1967
1968     if (line_ptr < line + line_len)
1969       value = line_ptr + 1;
1970     else
1971       value = "\0";
1972
1973     /* cut leading whitespaces from value */
1974     for (; *value; value++)
1975       if (*value != ' ' && *value != '\t')
1976         break;
1977
1978     if (*token && *value)
1979       setTokenValue(setup_file_list, token, value);
1980   }
1981
1982   fclose(file);
1983
1984   first_valid_list_entry = setup_file_list->next;
1985
1986   /* free empty list header */
1987   setup_file_list->next = NULL;
1988   freeSetupFileList(setup_file_list);
1989
1990   if (first_valid_list_entry == NULL)
1991     Error(ERR_WARN, "configuration file '%s' is empty", filename);
1992
1993   return first_valid_list_entry;
1994 }
1995
1996 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1997                                          char *identifier)
1998 {
1999   if (!setup_file_list)
2000     return;
2001
2002   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2003   {
2004     if (strcmp(setup_file_list->value, identifier) != 0)
2005     {
2006       Error(ERR_WARN, "configuration file has wrong version");
2007       return;
2008     }
2009     else
2010       return;
2011   }
2012
2013   if (setup_file_list->next)
2014     checkSetupFileListIdentifier(setup_file_list->next, identifier);
2015   else
2016   {
2017     Error(ERR_WARN, "configuration file has no version information");
2018     return;
2019   }
2020 }
2021
2022 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2023 {
2024   ldi->filename = NULL;
2025   ldi->fullpath = NULL;
2026   ldi->basepath = NULL;
2027   ldi->name = getStringCopy(ANONYMOUS_NAME);
2028   ldi->name_short = NULL;
2029   ldi->name_sorting = NULL;
2030   ldi->author = getStringCopy(ANONYMOUS_NAME);
2031   ldi->imported_from = NULL;
2032   ldi->levels = 0;
2033   ldi->first_level = 0;
2034   ldi->last_level = 0;
2035   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
2036   ldi->level_group = FALSE;
2037   ldi->parent_link = FALSE;
2038   ldi->user_defined = FALSE;
2039   ldi->readonly = TRUE;
2040   ldi->color = 0;
2041   ldi->class_desc = NULL;
2042   ldi->handicap_level = 0;
2043   ldi->cl_first = -1;
2044   ldi->cl_cursor = -1;
2045
2046   ldi->node_parent = NULL;
2047   ldi->node_group = NULL;
2048   ldi->next = NULL;
2049 }
2050
2051 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2052                                                 struct LevelDirInfo *parent)
2053 {
2054   if (parent == NULL)
2055   {
2056     setLevelDirInfoToDefaults(ldi);
2057     return;
2058   }
2059
2060   /* first copy all values from the parent structure ... */
2061   *ldi = *parent;
2062
2063   /* ... then set all fields to default that cannot be inherited from parent.
2064      This is especially important for all those fields that can be set from
2065      the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2066      calls 'free()' for all already set token values which requires that no
2067      other structure's pointer may point to them!
2068   */
2069
2070   ldi->filename = NULL;
2071   ldi->fullpath = NULL;
2072   ldi->basepath = NULL;
2073   ldi->name = getStringCopy(ANONYMOUS_NAME);
2074   ldi->name_short = NULL;
2075   ldi->name_sorting = NULL;
2076   ldi->author = getStringCopy(parent->author);
2077   ldi->imported_from = getStringCopy(parent->imported_from);
2078
2079   ldi->level_group = FALSE;
2080   ldi->parent_link = FALSE;
2081
2082   ldi->node_parent = parent;
2083   ldi->node_group = NULL;
2084   ldi->next = NULL;
2085 }
2086
2087 static void setSetupInfoToDefaults(struct SetupInfo *si)
2088 {
2089   int i;
2090
2091   si->player_name = getStringCopy(getLoginName());
2092
2093   si->sound = TRUE;
2094   si->sound_loops = TRUE;
2095   si->sound_music = TRUE;
2096   si->sound_simple = TRUE;
2097   si->toons = TRUE;
2098   si->double_buffering = TRUE;
2099   si->direct_draw = !si->double_buffering;
2100   si->scroll_delay = TRUE;
2101   si->soft_scrolling = TRUE;
2102   si->fading = FALSE;
2103   si->autorecord = TRUE;
2104   si->quick_doors = FALSE;
2105   si->team_mode = FALSE;
2106   si->handicap = TRUE;
2107   si->time_limit = TRUE;
2108   si->fullscreen = FALSE;
2109
2110   for (i=0; i<MAX_PLAYERS; i++)
2111   {
2112     si->input[i].use_joystick = FALSE;
2113     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2114     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2115     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2116     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2117     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2118     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2119     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2120     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2121     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2122     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2123     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2124     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2125     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2126     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2127     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2128   }
2129 }
2130
2131 static void setSetupInfo(int token_nr, char *token_value)
2132 {
2133   int token_type = token_info[token_nr].type;
2134   void *setup_value = token_info[token_nr].value;
2135
2136   if (token_value == NULL)
2137     return;
2138
2139   /* set setup field to corresponding token value */
2140   switch (token_type)
2141   {
2142     case TYPE_BOOLEAN:
2143     case TYPE_SWITCH:
2144       *(boolean *)setup_value = get_string_boolean_value(token_value);
2145       break;
2146
2147     case TYPE_KEY:
2148       *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2149       break;
2150
2151     case TYPE_INTEGER:
2152       *(int *)setup_value = get_string_integer_value(token_value);
2153       break;
2154
2155     case TYPE_STRING:
2156       if (*(char **)setup_value != NULL)
2157         free(*(char **)setup_value);
2158       *(char **)setup_value = getStringCopy(token_value);
2159       break;
2160
2161     default:
2162       break;
2163   }
2164 }
2165
2166 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2167 {
2168   int i, pnr;
2169
2170   if (!setup_file_list)
2171     return;
2172
2173   /* handle global setup values */
2174   si = setup;
2175   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2176     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2177   setup = si;
2178
2179   /* handle player specific setup values */
2180   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2181   {
2182     char prefix[30];
2183
2184     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2185
2186     sii = setup.input[pnr];
2187     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2188     {
2189       char full_token[100];
2190
2191       sprintf(full_token, "%s%s", prefix, token_info[i].text);
2192       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2193     }
2194     setup.input[pnr] = sii;
2195   }
2196 }
2197
2198 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2199 {
2200   const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2201   const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2202   int compare_result;
2203
2204   if (entry1->parent_link || entry2->parent_link)
2205     compare_result = (entry1->parent_link ? -1 : +1);
2206   else if (entry1->sort_priority == entry2->sort_priority)
2207   {
2208     char *name1 = getStringToLower(entry1->name_sorting);
2209     char *name2 = getStringToLower(entry2->name_sorting);
2210
2211     compare_result = strcmp(name1, name2);
2212
2213     free(name1);
2214     free(name2);
2215   }
2216   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2217     compare_result = entry1->sort_priority - entry2->sort_priority;
2218   else
2219     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2220
2221   return compare_result;
2222 }
2223
2224 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2225 {
2226   struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2227
2228   setLevelDirInfoToDefaults(leveldir_new);
2229
2230   leveldir_new->node_parent = node_parent;
2231   leveldir_new->parent_link = TRUE;
2232
2233   leveldir_new->name = ".. (parent directory)";
2234   leveldir_new->name_short = getStringCopy(leveldir_new->name);
2235   leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2236
2237   leveldir_new->filename = "..";
2238   leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2239
2240   leveldir_new->sort_priority = node_parent->sort_priority;
2241   leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2242
2243   pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2244 }
2245
2246 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2247                                       struct LevelDirInfo *node_parent,
2248                                       char *level_directory)
2249 {
2250   DIR *dir;
2251   struct dirent *dir_entry;
2252   boolean valid_entry_found = FALSE;
2253
2254   if ((dir = opendir(level_directory)) == NULL)
2255   {
2256     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2257     return;
2258   }
2259
2260   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2261   {
2262     struct SetupFileList *setup_file_list = NULL;
2263     struct stat file_status;
2264     char *directory_name = dir_entry->d_name;
2265     char *directory_path = getPath2(level_directory, directory_name);
2266     char *filename = NULL;
2267
2268     /* skip entries for current and parent directory */
2269     if (strcmp(directory_name, ".")  == 0 ||
2270         strcmp(directory_name, "..") == 0)
2271     {
2272       free(directory_path);
2273       continue;
2274     }
2275
2276     /* find out if directory entry is itself a directory */
2277     if (stat(directory_path, &file_status) != 0 ||      /* cannot stat file */
2278         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
2279     {
2280       free(directory_path);
2281       continue;
2282     }
2283
2284     filename = getPath2(directory_path, LEVELINFO_FILENAME);
2285     setup_file_list = loadSetupFileList(filename);
2286
2287     if (setup_file_list)
2288     {
2289       struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2290       int i;
2291
2292       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2293       setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2294
2295       /* set all structure fields according to the token/value pairs */
2296       ldi = *leveldir_new;
2297       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2298         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2299       *leveldir_new = ldi;
2300
2301       DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2302
2303       if (leveldir_new->name_short == NULL)
2304         leveldir_new->name_short = getStringCopy(leveldir_new->name);
2305
2306       if (leveldir_new->name_sorting == NULL)
2307         leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2308
2309       leveldir_new->filename = getStringCopy(directory_name);
2310
2311       if (node_parent == NULL)          /* top level group */
2312       {
2313         leveldir_new->basepath = level_directory;
2314         leveldir_new->fullpath = leveldir_new->filename;
2315       }
2316       else                              /* sub level group */
2317       {
2318         leveldir_new->basepath = node_parent->basepath;
2319         leveldir_new->fullpath = getPath2(node_parent->fullpath,
2320                                           directory_name);
2321       }
2322
2323       if (leveldir_new->levels < 1)
2324         leveldir_new->levels = 1;
2325
2326       leveldir_new->last_level =
2327         leveldir_new->first_level + leveldir_new->levels - 1;
2328
2329       leveldir_new->user_defined =
2330         (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2331
2332       leveldir_new->color = LEVELCOLOR(leveldir_new);
2333       leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2334
2335       leveldir_new->handicap_level =    /* set handicap to default value */
2336         (leveldir_new->user_defined ?
2337          leveldir_new->last_level :
2338          leveldir_new->first_level);
2339
2340       pushLevelDirInfo(node_first, leveldir_new);
2341
2342       freeSetupFileList(setup_file_list);
2343       valid_entry_found = TRUE;
2344
2345       if (leveldir_new->level_group)
2346       {
2347         /* create node to link back to current level directory */
2348         createParentLevelDirNode(leveldir_new);
2349
2350         /* step into sub-directory and look for more level series */
2351         LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2352                                   leveldir_new, directory_path);
2353       }
2354     }
2355     else
2356       Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2357
2358     free(directory_path);
2359     free(filename);
2360   }
2361
2362   closedir(dir);
2363
2364   if (!valid_entry_found)
2365     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2366           level_directory);
2367 }
2368
2369 void LoadLevelInfo()
2370 {
2371   InitUserLevelDirectory(getLoginName());
2372
2373   DrawInitText("Loading level series:", 120, FC_GREEN);
2374
2375   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2376   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2377
2378   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2379
2380   if (leveldir_first == NULL)
2381     Error(ERR_EXIT, "cannot find any valid level series in any directory");
2382
2383   sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2384
2385 #if 0
2386   dumpLevelDirInfo(leveldir_first, 0);
2387 #endif
2388 }
2389
2390 static void SaveUserLevelInfo()
2391 {
2392   char *filename;
2393   FILE *file;
2394   int i;
2395
2396   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2397
2398   if (!(file = fopen(filename, MODE_WRITE)))
2399   {
2400     Error(ERR_WARN, "cannot write level info file '%s'", filename);
2401     free(filename);
2402     return;
2403   }
2404
2405   /* always start with reliable default values */
2406   setLevelDirInfoToDefaults(&ldi);
2407
2408   ldi.name = getLoginName();
2409   ldi.author = getRealName();
2410   ldi.levels = 100;
2411   ldi.first_level = 1;
2412   ldi.sort_priority = LEVELCLASS_USER_START;
2413   ldi.readonly = FALSE;
2414
2415   fprintf(file, "%s\n\n",
2416           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2417
2418   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2419     if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2420         i != LEVELINFO_TOKEN_NAME_SORTING &&
2421         i != LEVELINFO_TOKEN_IMPORTED_FROM)
2422       fprintf(file, "%s\n", getSetupLine("", i));
2423
2424   fclose(file);
2425   free(filename);
2426
2427   chmod(filename, SETUP_PERMS);
2428 }
2429
2430 void LoadSetup()
2431 {
2432   char *filename;
2433   struct SetupFileList *setup_file_list = NULL;
2434
2435   /* always start with reliable default values */
2436   setSetupInfoToDefaults(&setup);
2437
2438   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2439
2440   setup_file_list = loadSetupFileList(filename);
2441
2442   if (setup_file_list)
2443   {
2444     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2445     decodeSetupFileList(setup_file_list);
2446
2447     setup.direct_draw = !setup.double_buffering;
2448
2449     freeSetupFileList(setup_file_list);
2450
2451     /* needed to work around problems with fixed length strings */
2452     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2453       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2454     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2455     {
2456       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2457
2458       strcpy(new_name, setup.player_name);
2459       free(setup.player_name);
2460       setup.player_name = new_name;
2461     }
2462   }
2463   else
2464     Error(ERR_WARN, "using default setup values");
2465
2466   free(filename);
2467 }
2468
2469 static char *getSetupLine(char *prefix, int token_nr)
2470 {
2471   int i;
2472   static char entry[MAX_LINE_LEN];
2473   int token_type = token_info[token_nr].type;
2474   void *setup_value = token_info[token_nr].value;
2475   char *token_text = token_info[token_nr].text;
2476
2477   /* start with the prefix, token and some spaces to format output line */
2478   sprintf(entry, "%s%s:", prefix, token_text);
2479   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2480     strcat(entry, " ");
2481
2482   /* continue with the token's value (which can have different types) */
2483   switch (token_type)
2484   {
2485     case TYPE_BOOLEAN:
2486       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2487       break;
2488
2489     case TYPE_SWITCH:
2490       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2491       break;
2492
2493     case TYPE_KEY:
2494       {
2495         Key key = *(Key *)setup_value;
2496         char *keyname = getKeyNameFromKey(key);
2497
2498         strcat(entry, getX11KeyNameFromKey(key));
2499         for (i=strlen(entry); i<50; i++)
2500           strcat(entry, " ");
2501
2502         /* add comment, if useful */
2503         if (strcmp(keyname, "(undefined)") != 0 &&
2504             strcmp(keyname, "(unknown)") != 0)
2505         {
2506           strcat(entry, "# ");
2507           strcat(entry, keyname);
2508         }
2509       }
2510       break;
2511
2512     case TYPE_INTEGER:
2513       {
2514         char buffer[MAX_LINE_LEN];
2515
2516         sprintf(buffer, "%d", *(int *)setup_value);
2517         strcat(entry, buffer);
2518       }
2519       break;
2520
2521     case TYPE_STRING:
2522       strcat(entry, *(char **)setup_value);
2523       break;
2524
2525     default:
2526       break;
2527   }
2528
2529   return entry;
2530 }
2531
2532 void SaveSetup()
2533 {
2534   int i, pnr;
2535   char *filename;
2536   FILE *file;
2537
2538   InitUserDataDirectory();
2539
2540   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2541
2542   if (!(file = fopen(filename, MODE_WRITE)))
2543   {
2544     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2545     free(filename);
2546     return;
2547   }
2548
2549   fprintf(file, "%s\n",
2550           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2551   fprintf(file, "\n");
2552
2553   /* handle global setup values */
2554   si = setup;
2555   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2556   {
2557     fprintf(file, "%s\n", getSetupLine("", i));
2558
2559     /* just to make things nicer :) */
2560     if (i == SETUP_TOKEN_PLAYER_NAME)
2561       fprintf(file, "\n");
2562   }
2563
2564   /* handle player specific setup values */
2565   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2566   {
2567     char prefix[30];
2568
2569     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2570     fprintf(file, "\n");
2571
2572     sii = setup.input[pnr];
2573     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2574       fprintf(file, "%s\n", getSetupLine(prefix, i));
2575   }
2576
2577   fclose(file);
2578   free(filename);
2579
2580   chmod(filename, SETUP_PERMS);
2581 }
2582
2583 void LoadLevelSetup_LastSeries()
2584 {
2585   char *filename;
2586   struct SetupFileList *level_setup_list = NULL;
2587
2588   /* always start with reliable default values */
2589   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2590
2591   /* ----------------------------------------------------------------------- */
2592   /* ~/.rocksndiamonds/levelsetup.conf                                       */
2593   /* ----------------------------------------------------------------------- */
2594
2595   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2596
2597   if ((level_setup_list = loadSetupFileList(filename)))
2598   {
2599     char *last_level_series =
2600       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2601
2602     leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2603     if (leveldir_current == NULL)
2604       leveldir_current = leveldir_first;
2605
2606     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2607
2608     freeSetupFileList(level_setup_list);
2609   }
2610   else
2611     Error(ERR_WARN, "using default setup values");
2612
2613   free(filename);
2614 }
2615
2616 void SaveLevelSetup_LastSeries()
2617 {
2618   char *filename;
2619   char *level_subdir = leveldir_current->filename;
2620   FILE *file;
2621
2622   /* ----------------------------------------------------------------------- */
2623   /* ~/.rocksndiamonds/levelsetup.conf                                       */
2624   /* ----------------------------------------------------------------------- */
2625
2626   InitUserDataDirectory();
2627
2628   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2629
2630   if (!(file = fopen(filename, MODE_WRITE)))
2631   {
2632     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2633     free(filename);
2634     return;
2635   }
2636
2637   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2638                                                  LEVELSETUP_COOKIE));
2639   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2640                                                level_subdir));
2641
2642   fclose(file);
2643   free(filename);
2644
2645   chmod(filename, SETUP_PERMS);
2646 }
2647
2648 static void checkSeriesInfo()
2649 {
2650   static char *level_directory = NULL;
2651   DIR *dir;
2652   struct dirent *dir_entry;
2653
2654   /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2655
2656   level_directory = getPath2((leveldir_current->user_defined ?
2657                               getUserLevelDir("") :
2658                               options.level_directory),
2659                              leveldir_current->fullpath);
2660
2661   if ((dir = opendir(level_directory)) == NULL)
2662   {
2663     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2664     return;
2665   }
2666
2667   while ((dir_entry = readdir(dir)) != NULL)    /* last directory entry */
2668   {
2669     if (strlen(dir_entry->d_name) > 4 &&
2670         dir_entry->d_name[3] == '.' &&
2671         strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2672     {
2673       char levelnum_str[4];
2674       int levelnum_value;
2675
2676       strncpy(levelnum_str, dir_entry->d_name, 3);
2677       levelnum_str[3] = '\0';
2678
2679       levelnum_value = atoi(levelnum_str);
2680
2681       if (levelnum_value < leveldir_current->first_level)
2682       {
2683         Error(ERR_WARN, "additional level %d found", levelnum_value);
2684         leveldir_current->first_level = levelnum_value;
2685       }
2686       else if (levelnum_value > leveldir_current->last_level)
2687       {
2688         Error(ERR_WARN, "additional level %d found", levelnum_value);
2689         leveldir_current->last_level = levelnum_value;
2690       }
2691     }
2692   }
2693
2694   closedir(dir);
2695 }
2696
2697 void LoadLevelSetup_SeriesInfo()
2698 {
2699   char *filename;
2700   struct SetupFileList *level_setup_list = NULL;
2701   char *level_subdir = leveldir_current->filename;
2702
2703   /* always start with reliable default values */
2704   level_nr = leveldir_current->first_level;
2705
2706   checkSeriesInfo(leveldir_current);
2707
2708   /* ----------------------------------------------------------------------- */
2709   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2710   /* ----------------------------------------------------------------------- */
2711
2712   level_subdir = leveldir_current->filename;
2713
2714   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2715
2716   if ((level_setup_list = loadSetupFileList(filename)))
2717   {
2718     char *token_value;
2719
2720     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2721
2722     if (token_value)
2723     {
2724       level_nr = atoi(token_value);
2725
2726       if (level_nr < leveldir_current->first_level)
2727         level_nr = leveldir_current->first_level;
2728       if (level_nr > leveldir_current->last_level)
2729         level_nr = leveldir_current->last_level;
2730     }
2731
2732     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2733
2734     if (token_value)
2735     {
2736       int level_nr = atoi(token_value);
2737
2738       if (level_nr < leveldir_current->first_level)
2739         level_nr = leveldir_current->first_level;
2740       if (level_nr > leveldir_current->last_level + 1)
2741         level_nr = leveldir_current->last_level;
2742
2743       if (leveldir_current->user_defined)
2744         level_nr = leveldir_current->last_level;
2745
2746       leveldir_current->handicap_level = level_nr;
2747     }
2748
2749     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2750
2751     freeSetupFileList(level_setup_list);
2752   }
2753   else
2754     Error(ERR_WARN, "using default setup values");
2755
2756   free(filename);
2757 }
2758
2759 void SaveLevelSetup_SeriesInfo()
2760 {
2761   char *filename;
2762   char *level_subdir = leveldir_current->filename;
2763   char *level_nr_str = int2str(level_nr, 0);
2764   char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2765   FILE *file;
2766
2767   /* ----------------------------------------------------------------------- */
2768   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2769   /* ----------------------------------------------------------------------- */
2770
2771   InitLevelSetupDirectory(level_subdir);
2772
2773   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2774
2775   if (!(file = fopen(filename, MODE_WRITE)))
2776   {
2777     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2778     free(filename);
2779     return;
2780   }
2781
2782   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2783                                                  LEVELSETUP_COOKIE));
2784   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2785                                                level_nr_str));
2786   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2787                                                handicap_level_str));
2788
2789   fclose(file);
2790   free(filename);
2791
2792   chmod(filename, SETUP_PERMS);
2793 }
2794 /*  LocalWords:  Rocks'n
2795  */