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