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