9b5b73f5b302b002695091b382af99f7ea8f33da
[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
1508 {
1509   int type;
1510   void *value;
1511   char *text;
1512 } token_info[] =
1513 {
1514   /* global setup */
1515   { TYPE_STRING,  &si.player_name,      "player_name"                   },
1516   { TYPE_SWITCH,  &si.sound,            "sound"                         },
1517   { TYPE_SWITCH,  &si.sound_loops,      "repeating_sound_loops"         },
1518   { TYPE_SWITCH,  &si.sound_music,      "background_music"              },
1519   { TYPE_SWITCH,  &si.sound_simple,     "simple_sound_effects"          },
1520   { TYPE_SWITCH,  &si.scroll_delay,     "scroll_delay"                  },
1521   { TYPE_SWITCH,  &si.soft_scrolling,   "soft_scrolling"                },
1522   { TYPE_SWITCH,  &si.fading,           "screen_fading"                 },
1523   { TYPE_SWITCH,  &si.autorecord,       "automatic_tape_recording"      },
1524   { TYPE_SWITCH,  &si.quick_doors,      "quick_doors"                   },
1525   { TYPE_SWITCH,  &si.team_mode,        "team_mode"                     },
1526   { TYPE_SWITCH,  &si.handicap,         "handicap"                      },
1527   { TYPE_SWITCH,  &si.time_limit,       "time_limit"                    },
1528   { TYPE_SWITCH,  &si.fullscreen,       "fullscreen"                    },
1529
1530   /* player setup */
1531   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1532   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1533   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1534   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1535   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1536   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1537   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1538   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1539   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1540   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1541   { TYPE_KEY,     &sii.key.left,        ".key.move_left"                },
1542   { TYPE_KEY,     &sii.key.right,       ".key.move_right"               },
1543   { TYPE_KEY,     &sii.key.up,          ".key.move_up"                  },
1544   { TYPE_KEY,     &sii.key.down,        ".key.move_down"                },
1545   { TYPE_KEY,     &sii.key.snap,        ".key.snap_field"               },
1546   { TYPE_KEY,     &sii.key.bomb,        ".key.place_bomb"               },
1547
1548   /* level directory info */
1549   { TYPE_STRING,  &ldi.name,            "name"                          },
1550   { TYPE_STRING,  &ldi.name_short,      "name_short"                    },
1551   { TYPE_STRING,  &ldi.name_sorting,    "name_sorting"                  },
1552   { TYPE_STRING,  &ldi.author,          "author"                        },
1553   { TYPE_STRING,  &ldi.imported_from,   "imported_from"                 },
1554   { TYPE_INTEGER, &ldi.levels,          "levels"                        },
1555   { TYPE_INTEGER, &ldi.first_level,     "first_level"                   },
1556   { TYPE_INTEGER, &ldi.sort_priority,   "sort_priority"                 },
1557   { TYPE_BOOLEAN, &ldi.level_group,     "level_group"                   },
1558   { TYPE_BOOLEAN, &ldi.readonly,        "readonly"                      }
1559 };
1560
1561 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1562 {
1563   ldi->filename = NULL;
1564   ldi->fullpath = NULL;
1565   ldi->basepath = NULL;
1566   ldi->name = getStringCopy(ANONYMOUS_NAME);
1567   ldi->name_short = NULL;
1568   ldi->name_sorting = NULL;
1569   ldi->author = getStringCopy(ANONYMOUS_NAME);
1570   ldi->imported_from = NULL;
1571   ldi->levels = 0;
1572   ldi->first_level = 0;
1573   ldi->last_level = 0;
1574   ldi->sort_priority = LEVELCLASS_UNDEFINED;    /* default: least priority */
1575   ldi->level_group = FALSE;
1576   ldi->parent_link = FALSE;
1577   ldi->user_defined = FALSE;
1578   ldi->readonly = TRUE;
1579   ldi->color = 0;
1580   ldi->class_desc = NULL;
1581   ldi->handicap_level = 0;
1582   ldi->cl_first = -1;
1583   ldi->cl_cursor = -1;
1584
1585   ldi->node_parent = NULL;
1586   ldi->node_group = NULL;
1587   ldi->next = NULL;
1588 }
1589
1590 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1591                                                 struct LevelDirInfo *parent)
1592 {
1593   if (parent == NULL)
1594   {
1595     setLevelDirInfoToDefaults(ldi);
1596     return;
1597   }
1598
1599   /* first copy all values from the parent structure ... */
1600   *ldi = *parent;
1601
1602   /* ... then set all fields to default that cannot be inherited from parent.
1603      This is especially important for all those fields that can be set from
1604      the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1605      calls 'free()' for all already set token values which requires that no
1606      other structure's pointer may point to them!
1607   */
1608
1609   ldi->filename = NULL;
1610   ldi->fullpath = NULL;
1611   ldi->basepath = NULL;
1612   ldi->name = getStringCopy(ANONYMOUS_NAME);
1613   ldi->name_short = NULL;
1614   ldi->name_sorting = NULL;
1615   ldi->author = getStringCopy(parent->author);
1616   ldi->imported_from = getStringCopy(parent->imported_from);
1617
1618   ldi->level_group = FALSE;
1619   ldi->parent_link = FALSE;
1620
1621   ldi->node_parent = parent;
1622   ldi->node_group = NULL;
1623   ldi->next = NULL;
1624 }
1625
1626 static void setSetupInfoToDefaults(struct SetupInfo *si)
1627 {
1628   int i;
1629
1630   si->player_name = getStringCopy(getLoginName());
1631
1632   si->sound = TRUE;
1633   si->sound_loops = TRUE;
1634   si->sound_music = TRUE;
1635   si->sound_simple = TRUE;
1636   si->toons = TRUE;
1637   si->double_buffering = TRUE;
1638   si->direct_draw = !si->double_buffering;
1639   si->scroll_delay = TRUE;
1640   si->soft_scrolling = TRUE;
1641   si->fading = FALSE;
1642   si->autorecord = TRUE;
1643   si->quick_doors = FALSE;
1644   si->team_mode = FALSE;
1645   si->handicap = TRUE;
1646   si->time_limit = TRUE;
1647   si->fullscreen = FALSE;
1648
1649   for (i=0; i<MAX_PLAYERS; i++)
1650   {
1651     si->input[i].use_joystick = FALSE;
1652     si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1653     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1654     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1655     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1656     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1657     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1658     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1659     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1660     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1661     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1662     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1663     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1664     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1665     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1666     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1667   }
1668 }
1669
1670 static void setSetupInfo(int token_nr, char *token_value)
1671 {
1672   int token_type = token_info[token_nr].type;
1673   void *setup_value = token_info[token_nr].value;
1674
1675   if (token_value == NULL)
1676     return;
1677
1678   /* set setup field to corresponding token value */
1679   switch (token_type)
1680   {
1681     case TYPE_BOOLEAN:
1682     case TYPE_SWITCH:
1683       *(boolean *)setup_value = get_string_boolean_value(token_value);
1684       break;
1685
1686     case TYPE_KEY:
1687       *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1688       break;
1689
1690     case TYPE_INTEGER:
1691       *(int *)setup_value = get_string_integer_value(token_value);
1692       break;
1693
1694     case TYPE_STRING:
1695       if (*(char **)setup_value != NULL)
1696         free(*(char **)setup_value);
1697       *(char **)setup_value = getStringCopy(token_value);
1698       break;
1699
1700     default:
1701       break;
1702   }
1703 }
1704
1705 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1706 {
1707   int i, pnr;
1708
1709   if (!setup_file_list)
1710     return;
1711
1712   /* handle global setup values */
1713   si = setup;
1714   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1715     setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1716   setup = si;
1717
1718   /* handle player specific setup values */
1719   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1720   {
1721     char prefix[30];
1722
1723     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1724
1725     sii = setup.input[pnr];
1726     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1727     {
1728       char full_token[100];
1729
1730       sprintf(full_token, "%s%s", prefix, token_info[i].text);
1731       setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1732     }
1733     setup.input[pnr] = sii;
1734   }
1735 }
1736
1737 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1738 {
1739   const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1740   const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1741   int compare_result;
1742
1743   if (entry1->parent_link || entry2->parent_link)
1744     compare_result = (entry1->parent_link ? -1 : +1);
1745   else if (entry1->sort_priority == entry2->sort_priority)
1746   {
1747     char *name1 = getStringToLower(entry1->name_sorting);
1748     char *name2 = getStringToLower(entry2->name_sorting);
1749
1750     compare_result = strcmp(name1, name2);
1751
1752     free(name1);
1753     free(name2);
1754   }
1755   else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1756     compare_result = entry1->sort_priority - entry2->sort_priority;
1757   else
1758     compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1759
1760   return compare_result;
1761 }
1762
1763 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1764 {
1765   struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1766
1767   setLevelDirInfoToDefaults(leveldir_new);
1768
1769   leveldir_new->node_parent = node_parent;
1770   leveldir_new->parent_link = TRUE;
1771
1772   leveldir_new->name = ".. (parent directory)";
1773   leveldir_new->name_short = getStringCopy(leveldir_new->name);
1774   leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1775
1776   leveldir_new->filename = "..";
1777   leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1778
1779   leveldir_new->sort_priority = node_parent->sort_priority;
1780   leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1781
1782   pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1783 }
1784
1785 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1786                                       struct LevelDirInfo *node_parent,
1787                                       char *level_directory)
1788 {
1789   DIR *dir;
1790   struct dirent *dir_entry;
1791   boolean valid_entry_found = FALSE;
1792
1793   if ((dir = opendir(level_directory)) == NULL)
1794   {
1795     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1796     return;
1797   }
1798
1799   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
1800   {
1801     struct SetupFileList *setup_file_list = NULL;
1802     struct stat file_status;
1803     char *directory_name = dir_entry->d_name;
1804     char *directory_path = getPath2(level_directory, directory_name);
1805     char *filename = NULL;
1806
1807     /* skip entries for current and parent directory */
1808     if (strcmp(directory_name, ".")  == 0 ||
1809         strcmp(directory_name, "..") == 0)
1810     {
1811       free(directory_path);
1812       continue;
1813     }
1814
1815     /* find out if directory entry is itself a directory */
1816     if (stat(directory_path, &file_status) != 0 ||      /* cannot stat file */
1817         (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
1818     {
1819       free(directory_path);
1820       continue;
1821     }
1822
1823     filename = getPath2(directory_path, LEVELINFO_FILENAME);
1824     setup_file_list = loadSetupFileList(filename);
1825
1826     if (setup_file_list)
1827     {
1828       struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1829       int i;
1830
1831       checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1832       setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1833
1834       /* set all structure fields according to the token/value pairs */
1835       ldi = *leveldir_new;
1836       for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1837         setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1838       *leveldir_new = ldi;
1839
1840       DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1841
1842       if (leveldir_new->name_short == NULL)
1843         leveldir_new->name_short = getStringCopy(leveldir_new->name);
1844
1845       if (leveldir_new->name_sorting == NULL)
1846         leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1847
1848       leveldir_new->filename = getStringCopy(directory_name);
1849
1850       if (node_parent == NULL)          /* top level group */
1851       {
1852         leveldir_new->basepath = level_directory;
1853         leveldir_new->fullpath = leveldir_new->filename;
1854       }
1855       else                              /* sub level group */
1856       {
1857         leveldir_new->basepath = node_parent->basepath;
1858         leveldir_new->fullpath = getPath2(node_parent->fullpath,
1859                                           directory_name);
1860       }
1861
1862       if (leveldir_new->levels < 1)
1863         leveldir_new->levels = 1;
1864
1865       leveldir_new->last_level =
1866         leveldir_new->first_level + leveldir_new->levels - 1;
1867
1868       leveldir_new->user_defined =
1869         (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1870
1871       leveldir_new->color = LEVELCOLOR(leveldir_new);
1872       leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1873
1874       leveldir_new->handicap_level =    /* set handicap to default value */
1875         (leveldir_new->user_defined ?
1876          leveldir_new->last_level :
1877          leveldir_new->first_level);
1878
1879       pushLevelDirInfo(node_first, leveldir_new);
1880
1881       freeSetupFileList(setup_file_list);
1882       valid_entry_found = TRUE;
1883
1884       if (leveldir_new->level_group)
1885       {
1886         /* create node to link back to current level directory */
1887         createParentLevelDirNode(leveldir_new);
1888
1889         /* step into sub-directory and look for more level series */
1890         LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1891                                   leveldir_new, directory_path);
1892       }
1893     }
1894     else
1895       Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1896
1897     free(directory_path);
1898     free(filename);
1899   }
1900
1901   closedir(dir);
1902
1903   if (!valid_entry_found)
1904     Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1905           level_directory);
1906 }
1907
1908 void LoadLevelInfo()
1909 {
1910   InitUserLevelDirectory(getLoginName());
1911
1912   DrawInitText("Loading level series:", 120, FC_GREEN);
1913
1914   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1915   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1916
1917   leveldir_current = getFirstValidLevelSeries(leveldir_first);
1918
1919   if (leveldir_first == NULL)
1920     Error(ERR_EXIT, "cannot find any valid level series in any directory");
1921
1922   sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1923
1924 #if 0
1925   dumpLevelDirInfo(leveldir_first, 0);
1926 #endif
1927 }
1928
1929 static void SaveUserLevelInfo()
1930 {
1931   char *filename;
1932   FILE *file;
1933   int i;
1934
1935   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1936
1937   if (!(file = fopen(filename, MODE_WRITE)))
1938   {
1939     Error(ERR_WARN, "cannot write level info file '%s'", filename);
1940     free(filename);
1941     return;
1942   }
1943
1944   /* always start with reliable default values */
1945   setLevelDirInfoToDefaults(&ldi);
1946
1947   ldi.name = getLoginName();
1948   ldi.author = getRealName();
1949   ldi.levels = 100;
1950   ldi.first_level = 1;
1951   ldi.sort_priority = LEVELCLASS_USER_START;
1952   ldi.readonly = FALSE;
1953
1954   fprintf(file, "%s\n\n",
1955           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1956
1957   for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1958     if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1959         i != LEVELINFO_TOKEN_NAME_SORTING &&
1960         i != LEVELINFO_TOKEN_IMPORTED_FROM)
1961       fprintf(file, "%s\n", getSetupLine("", i));
1962
1963   fclose(file);
1964   free(filename);
1965
1966   SetFilePermissions(filename, PERMS_PRIVATE);
1967 }
1968
1969 void LoadSetup()
1970 {
1971   char *filename;
1972   struct SetupFileList *setup_file_list = NULL;
1973
1974   /* always start with reliable default values */
1975   setSetupInfoToDefaults(&setup);
1976
1977   filename = getPath2(getSetupDir(), SETUP_FILENAME);
1978
1979   setup_file_list = loadSetupFileList(filename);
1980
1981   if (setup_file_list)
1982   {
1983     checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1984     decodeSetupFileList(setup_file_list);
1985
1986     setup.direct_draw = !setup.double_buffering;
1987
1988     freeSetupFileList(setup_file_list);
1989
1990     /* needed to work around problems with fixed length strings */
1991     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1992       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1993     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1994     {
1995       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1996
1997       strcpy(new_name, setup.player_name);
1998       free(setup.player_name);
1999       setup.player_name = new_name;
2000     }
2001   }
2002   else
2003     Error(ERR_WARN, "using default setup values");
2004
2005   free(filename);
2006 }
2007
2008 static char *getSetupLine(char *prefix, int token_nr)
2009 {
2010   int i;
2011   static char entry[MAX_LINE_LEN];
2012   int token_type = token_info[token_nr].type;
2013   void *setup_value = token_info[token_nr].value;
2014   char *token_text = token_info[token_nr].text;
2015
2016   /* start with the prefix, token and some spaces to format output line */
2017   sprintf(entry, "%s%s:", prefix, token_text);
2018   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2019     strcat(entry, " ");
2020
2021   /* continue with the token's value (which can have different types) */
2022   switch (token_type)
2023   {
2024     case TYPE_BOOLEAN:
2025       strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2026       break;
2027
2028     case TYPE_SWITCH:
2029       strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2030       break;
2031
2032     case TYPE_KEY:
2033       {
2034         Key key = *(Key *)setup_value;
2035         char *keyname = getKeyNameFromKey(key);
2036
2037         strcat(entry, getX11KeyNameFromKey(key));
2038         for (i=strlen(entry); i<50; i++)
2039           strcat(entry, " ");
2040
2041         /* add comment, if useful */
2042         if (strcmp(keyname, "(undefined)") != 0 &&
2043             strcmp(keyname, "(unknown)") != 0)
2044         {
2045           strcat(entry, "# ");
2046           strcat(entry, keyname);
2047         }
2048       }
2049       break;
2050
2051     case TYPE_INTEGER:
2052       {
2053         char buffer[MAX_LINE_LEN];
2054
2055         sprintf(buffer, "%d", *(int *)setup_value);
2056         strcat(entry, buffer);
2057       }
2058       break;
2059
2060     case TYPE_STRING:
2061       strcat(entry, *(char **)setup_value);
2062       break;
2063
2064     default:
2065       break;
2066   }
2067
2068   return entry;
2069 }
2070
2071 void SaveSetup()
2072 {
2073   int i, pnr;
2074   char *filename;
2075   FILE *file;
2076
2077   InitUserDataDirectory();
2078
2079   filename = getPath2(getSetupDir(), SETUP_FILENAME);
2080
2081   if (!(file = fopen(filename, MODE_WRITE)))
2082   {
2083     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2084     free(filename);
2085     return;
2086   }
2087
2088   fprintf(file, "%s\n",
2089           getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2090   fprintf(file, "\n");
2091
2092   /* handle global setup values */
2093   si = setup;
2094   for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2095   {
2096     fprintf(file, "%s\n", getSetupLine("", i));
2097
2098     /* just to make things nicer :) */
2099     if (i == SETUP_TOKEN_PLAYER_NAME)
2100       fprintf(file, "\n");
2101   }
2102
2103   /* handle player specific setup values */
2104   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2105   {
2106     char prefix[30];
2107
2108     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2109     fprintf(file, "\n");
2110
2111     sii = setup.input[pnr];
2112     for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2113       fprintf(file, "%s\n", getSetupLine(prefix, i));
2114   }
2115
2116   fclose(file);
2117   free(filename);
2118
2119   SetFilePermissions(filename, PERMS_PRIVATE);
2120 }
2121
2122 void LoadLevelSetup_LastSeries()
2123 {
2124   char *filename;
2125   struct SetupFileList *level_setup_list = NULL;
2126
2127   /* always start with reliable default values */
2128   leveldir_current = getFirstValidLevelSeries(leveldir_first);
2129
2130   /* ----------------------------------------------------------------------- */
2131   /* ~/.rocksndiamonds/levelsetup.conf                                       */
2132   /* ----------------------------------------------------------------------- */
2133
2134   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2135
2136   if ((level_setup_list = loadSetupFileList(filename)))
2137   {
2138     char *last_level_series =
2139       getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2140
2141     leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2142     if (leveldir_current == NULL)
2143       leveldir_current = leveldir_first;
2144
2145     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2146
2147     freeSetupFileList(level_setup_list);
2148   }
2149   else
2150     Error(ERR_WARN, "using default setup values");
2151
2152   free(filename);
2153 }
2154
2155 void SaveLevelSetup_LastSeries()
2156 {
2157   char *filename;
2158   char *level_subdir = leveldir_current->filename;
2159   FILE *file;
2160
2161   /* ----------------------------------------------------------------------- */
2162   /* ~/.rocksndiamonds/levelsetup.conf                                       */
2163   /* ----------------------------------------------------------------------- */
2164
2165   InitUserDataDirectory();
2166
2167   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2168
2169   if (!(file = fopen(filename, MODE_WRITE)))
2170   {
2171     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2172     free(filename);
2173     return;
2174   }
2175
2176   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2177                                                  LEVELSETUP_COOKIE));
2178   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2179                                                level_subdir));
2180
2181   fclose(file);
2182   free(filename);
2183
2184   SetFilePermissions(filename, PERMS_PRIVATE);
2185 }
2186
2187 static void checkSeriesInfo()
2188 {
2189   static char *level_directory = NULL;
2190   DIR *dir;
2191   struct dirent *dir_entry;
2192
2193   /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2194
2195   level_directory = getPath2((leveldir_current->user_defined ?
2196                               getUserLevelDir("") :
2197                               options.level_directory),
2198                              leveldir_current->fullpath);
2199
2200   if ((dir = opendir(level_directory)) == NULL)
2201   {
2202     Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2203     return;
2204   }
2205
2206   while ((dir_entry = readdir(dir)) != NULL)    /* last directory entry */
2207   {
2208     if (strlen(dir_entry->d_name) > 4 &&
2209         dir_entry->d_name[3] == '.' &&
2210         strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2211     {
2212       char levelnum_str[4];
2213       int levelnum_value;
2214
2215       strncpy(levelnum_str, dir_entry->d_name, 3);
2216       levelnum_str[3] = '\0';
2217
2218       levelnum_value = atoi(levelnum_str);
2219
2220       if (levelnum_value < leveldir_current->first_level)
2221       {
2222         Error(ERR_WARN, "additional level %d found", levelnum_value);
2223         leveldir_current->first_level = levelnum_value;
2224       }
2225       else if (levelnum_value > leveldir_current->last_level)
2226       {
2227         Error(ERR_WARN, "additional level %d found", levelnum_value);
2228         leveldir_current->last_level = levelnum_value;
2229       }
2230     }
2231   }
2232
2233   closedir(dir);
2234 }
2235
2236 void LoadLevelSetup_SeriesInfo()
2237 {
2238   char *filename;
2239   struct SetupFileList *level_setup_list = NULL;
2240   char *level_subdir = leveldir_current->filename;
2241
2242   /* always start with reliable default values */
2243   level_nr = leveldir_current->first_level;
2244
2245   checkSeriesInfo(leveldir_current);
2246
2247   /* ----------------------------------------------------------------------- */
2248   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2249   /* ----------------------------------------------------------------------- */
2250
2251   level_subdir = leveldir_current->filename;
2252
2253   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2254
2255   if ((level_setup_list = loadSetupFileList(filename)))
2256   {
2257     char *token_value;
2258
2259     token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2260
2261     if (token_value)
2262     {
2263       level_nr = atoi(token_value);
2264
2265       if (level_nr < leveldir_current->first_level)
2266         level_nr = leveldir_current->first_level;
2267       if (level_nr > leveldir_current->last_level)
2268         level_nr = leveldir_current->last_level;
2269     }
2270
2271     token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2272
2273     if (token_value)
2274     {
2275       int level_nr = atoi(token_value);
2276
2277       if (level_nr < leveldir_current->first_level)
2278         level_nr = leveldir_current->first_level;
2279       if (level_nr > leveldir_current->last_level + 1)
2280         level_nr = leveldir_current->last_level;
2281
2282       if (leveldir_current->user_defined)
2283         level_nr = leveldir_current->last_level;
2284
2285       leveldir_current->handicap_level = level_nr;
2286     }
2287
2288     checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2289
2290     freeSetupFileList(level_setup_list);
2291   }
2292   else
2293     Error(ERR_WARN, "using default setup values");
2294
2295   free(filename);
2296 }
2297
2298 void SaveLevelSetup_SeriesInfo()
2299 {
2300   char *filename;
2301   char *level_subdir = leveldir_current->filename;
2302   char *level_nr_str = int2str(level_nr, 0);
2303   char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2304   FILE *file;
2305
2306   /* ----------------------------------------------------------------------- */
2307   /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
2308   /* ----------------------------------------------------------------------- */
2309
2310   InitLevelSetupDirectory(level_subdir);
2311
2312   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2313
2314   if (!(file = fopen(filename, MODE_WRITE)))
2315   {
2316     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2317     free(filename);
2318     return;
2319   }
2320
2321   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2322                                                  LEVELSETUP_COOKIE));
2323   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2324                                                level_nr_str));
2325   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2326                                                handicap_level_str));
2327
2328   fclose(file);
2329   free(filename);
2330
2331   SetFilePermissions(filename, PERMS_PRIVATE);
2332 }