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