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