rnd-20030407-2-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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 <sys/stat.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "tools.h"
21 #include "tape.h"
22
23
24 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
25 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
26 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
27 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
29 #define LEVEL_HEADER_UNUSED     14      /* unused level header bytes  */
30 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
32 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
33 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
34
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39
40
41 /* ========================================================================= */
42 /* level file functions                                                      */
43 /* ========================================================================= */
44
45 static void setLevelInfoToDefaults()
46 {
47   int i, x, y;
48
49   level.file_version = FILE_VERSION_ACTUAL;
50   level.game_version = GAME_VERSION_ACTUAL;
51
52   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
53   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
54   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
55
56   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
58
59   for(x=0; x<MAX_LEV_FIELDX; x++)
60     for(y=0; y<MAX_LEV_FIELDY; y++)
61       Feld[x][y] = Ur[x][y] = EL_SAND;
62
63   level.time = 100;
64   level.gems_needed = 0;
65   level.amoeba_speed = 10;
66   level.time_magic_wall = 10;
67   level.time_wheel = 10;
68   level.time_light = 10;
69   level.time_timegate = 10;
70   level.amoeba_content = EL_DIAMOND;
71   level.double_speed = FALSE;
72   level.gravity = FALSE;
73   level.em_slippery_gems = FALSE;
74
75   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
76     level.name[i] = '\0';
77   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78     level.author[i] = '\0';
79
80   strcpy(level.name, NAMELESS_LEVEL_NAME);
81   strcpy(level.author, ANONYMOUS_NAME);
82
83   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
84     level.score[i] = 10;
85
86   level.num_yam_contents = STD_ELEMENT_CONTENTS;
87   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
88     for(x=0; x<3; x++)
89       for(y=0; y<3; y++)
90         level.yam_content[i][x][y] =
91           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
92
93   Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
94   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
96
97   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
98     Properties1[EL_CUSTOM_START + i] = EP_BITMASK_DEFAULT;
99
100   BorderElement = EL_STEELWALL;
101
102   level.no_level_file = FALSE;
103
104   if (leveldir_current == NULL)         /* only when dumping level */
105     return;
106
107   /* try to determine better author name than 'anonymous' */
108   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
109   {
110     strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
111     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
112   }
113   else
114   {
115     switch (LEVELCLASS(leveldir_current))
116     {
117       case LEVELCLASS_TUTORIAL:
118         strcpy(level.author, PROGRAM_AUTHOR_STRING);
119         break;
120
121       case LEVELCLASS_CONTRIBUTION:
122         strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
123         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
124         break;
125
126       case LEVELCLASS_USER:
127         strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
128         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
129         break;
130
131       default:
132         /* keep default value */
133         break;
134     }
135   }
136 }
137
138 static int checkLevelElement(int element)
139 {
140   if (element >= NUM_FILE_ELEMENTS)
141   {
142     Error(ERR_WARN, "invalid level element %d", element);
143     element = EL_CHAR_QUESTION;
144   }
145   else if (element == EL_PLAYER_OBSOLETE)
146     element = EL_PLAYER_1;
147   else if (element == EL_KEY_OBSOLETE)
148     element = EL_KEY_1;
149
150   return element;
151 }
152
153 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
154 {
155   level->file_version = getFileVersion(file);
156   level->game_version = getFileVersion(file);
157
158   return chunk_size;
159 }
160
161 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
162 {
163   int i, x, y;
164
165   lev_fieldx = level->fieldx = fgetc(file);
166   lev_fieldy = level->fieldy = fgetc(file);
167
168   level->time           = getFile16BitBE(file);
169   level->gems_needed    = getFile16BitBE(file);
170
171   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
172     level->name[i] = fgetc(file);
173   level->name[MAX_LEVEL_NAME_LEN] = 0;
174
175   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
176     level->score[i] = fgetc(file);
177
178   level->num_yam_contents = STD_ELEMENT_CONTENTS;
179   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
180     for(y=0; y<3; y++)
181       for(x=0; x<3; x++)
182         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
183
184   level->amoeba_speed           = fgetc(file);
185   level->time_magic_wall        = fgetc(file);
186   level->time_wheel             = fgetc(file);
187   level->amoeba_content         = checkLevelElement(fgetc(file));
188   level->double_speed           = (fgetc(file) == 1 ? TRUE : FALSE);
189   level->gravity                = (fgetc(file) == 1 ? TRUE : FALSE);
190   level->encoding_16bit_field   = (fgetc(file) == 1 ? TRUE : FALSE);
191   level->em_slippery_gems       = (fgetc(file) == 1 ? TRUE : FALSE);
192
193   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
194
195   return chunk_size;
196 }
197
198 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
199 {
200   int i;
201
202   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
203     level->author[i] = fgetc(file);
204   level->author[MAX_LEVEL_NAME_LEN] = 0;
205
206   return chunk_size;
207 }
208
209 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
210 {
211   int x, y;
212   int chunk_size_expected = level->fieldx * level->fieldy;
213
214   /* Note: "chunk_size" was wrong before version 2.0 when elements are
215      stored with 16-bit encoding (and should be twice as big then).
216      Even worse, playfield data was stored 16-bit when only yamyam content
217      contained 16-bit elements and vice versa. */
218
219   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
220     chunk_size_expected *= 2;
221
222   if (chunk_size_expected != chunk_size)
223   {
224     ReadUnusedBytesFromFile(file, chunk_size);
225     return chunk_size_expected;
226   }
227
228   for(y=0; y<level->fieldy; y++)
229     for(x=0; x<level->fieldx; x++)
230       Feld[x][y] = Ur[x][y] =
231         checkLevelElement(level->encoding_16bit_field ?
232                           getFile16BitBE(file) : fgetc(file));
233   return chunk_size;
234 }
235
236 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
237 {
238   int i, x, y;
239   int header_size = 4;
240   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
241   int chunk_size_expected = header_size + content_size;
242
243   /* Note: "chunk_size" was wrong before version 2.0 when elements are
244      stored with 16-bit encoding (and should be twice as big then).
245      Even worse, playfield data was stored 16-bit when only yamyam content
246      contained 16-bit elements and vice versa. */
247
248   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
249     chunk_size_expected += content_size;
250
251   if (chunk_size_expected != chunk_size)
252   {
253     ReadUnusedBytesFromFile(file, chunk_size);
254     return chunk_size_expected;
255   }
256
257   fgetc(file);
258   level->num_yam_contents = fgetc(file);
259   fgetc(file);
260   fgetc(file);
261
262   /* correct invalid number of content fields -- should never happen */
263   if (level->num_yam_contents < 1 ||
264       level->num_yam_contents > MAX_ELEMENT_CONTENTS)
265     level->num_yam_contents = STD_ELEMENT_CONTENTS;
266
267   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
268     for(y=0; y<3; y++)
269       for(x=0; x<3; x++)
270         level->yam_content[i][x][y] =
271           checkLevelElement(level->encoding_16bit_field ?
272                             getFile16BitBE(file) : fgetc(file));
273   return chunk_size;
274 }
275
276 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
277 {
278   int i, x, y;
279   int element;
280   int num_contents, content_xsize, content_ysize;
281   int content_array[MAX_ELEMENT_CONTENTS][3][3];
282
283   element = checkLevelElement(getFile16BitBE(file));
284   num_contents = fgetc(file);
285   content_xsize = fgetc(file);
286   content_ysize = fgetc(file);
287   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
288
289   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
290     for(y=0; y<3; y++)
291       for(x=0; x<3; x++)
292         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
293
294   /* correct invalid number of content fields -- should never happen */
295   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
296     num_contents = STD_ELEMENT_CONTENTS;
297
298   if (element == EL_YAMYAM)
299   {
300     level->num_yam_contents = num_contents;
301
302     for(i=0; i<num_contents; i++)
303       for(y=0; y<3; y++)
304         for(x=0; x<3; x++)
305           level->yam_content[i][x][y] = content_array[i][x][y];
306   }
307   else if (element == EL_BD_AMOEBA)
308   {
309     level->amoeba_content = content_array[0][0][0];
310   }
311   else
312   {
313     Error(ERR_WARN, "cannot load content for element '%d'", element);
314   }
315
316   return chunk_size;
317 }
318
319 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
320 {
321   int num_changed_custom_elements = getFile16BitBE(file);
322   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
323   int i;
324
325   if (chunk_size_expected != chunk_size)
326   {
327     ReadUnusedBytesFromFile(file, chunk_size - 2);
328     return chunk_size_expected;
329   }
330
331   for (i=0; i < num_changed_custom_elements; i++)
332   {
333     int element = getFile16BitBE(file);
334     int properties = getFile32BitBE(file);
335
336     if (IS_CUSTOM_ELEMENT(element))
337       Properties1[element] = properties;
338     else
339       Error(ERR_WARN, "invalid custom element number %d", element);
340   }
341
342   return chunk_size;
343 }
344
345 void LoadLevelFromFilename(char *filename)
346 {
347   char cookie[MAX_LINE_LEN];
348   char chunk_name[CHUNK_ID_LEN + 1];
349   int chunk_size;
350   FILE *file;
351
352   /* always start with reliable default values */
353   setLevelInfoToDefaults();
354
355   if (!(file = fopen(filename, MODE_READ)))
356   {
357     level.no_level_file = TRUE;
358
359     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
360     return;
361   }
362
363   getFileChunkBE(file, chunk_name, NULL);
364   if (strcmp(chunk_name, "RND1") == 0)
365   {
366     getFile32BitBE(file);               /* not used */
367
368     getFileChunkBE(file, chunk_name, NULL);
369     if (strcmp(chunk_name, "CAVE") != 0)
370     {
371       Error(ERR_WARN, "unknown format of level file '%s'", filename);
372       fclose(file);
373       return;
374     }
375   }
376   else  /* check for pre-2.0 file format with cookie string */
377   {
378     strcpy(cookie, chunk_name);
379     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
380     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
381       cookie[strlen(cookie) - 1] = '\0';
382
383     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
384     {
385       Error(ERR_WARN, "unknown format of level file '%s'", filename);
386       fclose(file);
387       return;
388     }
389
390     if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
391     {
392       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
393       fclose(file);
394       return;
395     }
396
397     /* pre-2.0 level files have no game version, so use file version here */
398     level.game_version = level.file_version;
399   }
400
401   if (level.file_version < FILE_VERSION_1_2)
402   {
403     /* level files from versions before 1.2.0 without chunk structure */
404     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,           &level);
405     LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
406   }
407   else
408   {
409     static struct
410     {
411       char *name;
412       int size;
413       int (*loader)(FILE *, int, struct LevelInfo *);
414     }
415     chunk_info[] =
416     {
417       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
418       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
419       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
420       { "BODY", -1,                     LoadLevel_BODY },
421       { "CONT", -1,                     LoadLevel_CONT },
422       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
423       { "CUS1", -1,                     LoadLevel_CUS1 },
424       {  NULL,  0,                      NULL }
425     };
426
427     while (getFileChunkBE(file, chunk_name, &chunk_size))
428     {
429       int i = 0;
430
431       while (chunk_info[i].name != NULL &&
432              strcmp(chunk_name, chunk_info[i].name) != 0)
433         i++;
434
435       if (chunk_info[i].name == NULL)
436       {
437         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
438               chunk_name, filename);
439         ReadUnusedBytesFromFile(file, chunk_size);
440       }
441       else if (chunk_info[i].size != -1 &&
442                chunk_info[i].size != chunk_size)
443       {
444         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
445               chunk_size, chunk_name, filename);
446         ReadUnusedBytesFromFile(file, chunk_size);
447       }
448       else
449       {
450         /* call function to load this level chunk */
451         int chunk_size_expected =
452           (chunk_info[i].loader)(file, chunk_size, &level);
453
454         /* the size of some chunks cannot be checked before reading other
455            chunks first (like "HEAD" and "BODY") that contain some header
456            information, so check them here */
457         if (chunk_size_expected != chunk_size)
458         {
459           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
460                 chunk_size, chunk_name, filename);
461         }
462       }
463     }
464   }
465
466   fclose(file);
467
468   if (leveldir_current == NULL)         /* only when dumping level */
469     return;
470
471   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
472       IS_LEVELCLASS_USER(leveldir_current))
473   {
474     /* For user contributed and private levels, use the version of
475        the game engine the levels were created for.
476        Since 2.0.1, the game engine version is now directly stored
477        in the level file (chunk "VERS"), so there is no need anymore
478        to set the game version from the file version (except for old,
479        pre-2.0 levels, where the game version is still taken from the
480        file format version used to store the level -- see above). */
481
482     /* do some special adjustments to support older level versions */
483     if (level.file_version == FILE_VERSION_1_0)
484     {
485       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
486       Error(ERR_WARN, "using high speed movement for player");
487
488       /* player was faster than monsters in (pre-)1.0 levels */
489       level.double_speed = TRUE;
490     }
491
492     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
493     if (level.game_version == VERSION_IDENT(2,0,1))
494       level.em_slippery_gems = TRUE;
495   }
496   else
497   {
498     /* Always use the latest version of the game engine for all but
499        user contributed and private levels; this allows for actual
500        corrections in the game engine to take effect for existing,
501        converted levels (from "classic" or other existing games) to
502        make the game emulation more accurate, while (hopefully) not
503        breaking existing levels created from other players. */
504
505     level.game_version = GAME_VERSION_ACTUAL;
506
507     /* Set special EM style gems behaviour: EM style gems slip down from
508        normal, steel and growing wall. As this is a more fundamental change,
509        it seems better to set the default behaviour to "off" (as it is more
510        natural) and make it configurable in the level editor (as a property
511        of gem style elements). Already existing converted levels (neither
512        private nor contributed levels) are changed to the new behaviour. */
513
514     if (level.file_version < FILE_VERSION_2_0)
515       level.em_slippery_gems = TRUE;
516   }
517
518   /* map some elements which have changed in newer versions */
519   if (level.game_version <= VERSION_IDENT(2,2,0))
520   {
521     int x, y;
522
523     /* map game font elements */
524     for(y=0; y<level.fieldy; y++)
525     {
526       for(x=0; x<level.fieldx; x++)
527       {
528         int element = Ur[x][y];
529
530         if (element == EL_CHAR('['))
531           element = EL_CHAR_AUMLAUT;
532         else if (element == EL_CHAR('\\'))
533           element = EL_CHAR_OUMLAUT;
534         else if (element == EL_CHAR(']'))
535           element = EL_CHAR_UUMLAUT;
536         else if (element == EL_CHAR('^'))
537           element = EL_CHAR_COPYRIGHT;
538
539         Feld[x][y] = Ur[x][y] = element;
540       }
541     }
542   }
543
544   /* determine border element for this level */
545   SetBorderElement();
546 }
547
548 void LoadLevel(int level_nr)
549 {
550   char *filename = getLevelFilename(level_nr);
551
552   LoadLevelFromFilename(filename);
553 }
554
555 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
556 {
557   putFileVersion(file, level->file_version);
558   putFileVersion(file, level->game_version);
559 }
560
561 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
562 {
563   int i, x, y;
564
565   fputc(level->fieldx, file);
566   fputc(level->fieldy, file);
567
568   putFile16BitBE(file, level->time);
569   putFile16BitBE(file, level->gems_needed);
570
571   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
572     fputc(level->name[i], file);
573
574   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
575     fputc(level->score[i], file);
576
577   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
578     for(y=0; y<3; y++)
579       for(x=0; x<3; x++)
580         fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
581                level->yam_content[i][x][y]),
582               file);
583   fputc(level->amoeba_speed, file);
584   fputc(level->time_magic_wall, file);
585   fputc(level->time_wheel, file);
586   fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
587         file);
588   fputc((level->double_speed ? 1 : 0), file);
589   fputc((level->gravity ? 1 : 0), file);
590   fputc((level->encoding_16bit_field ? 1 : 0), file);
591   fputc((level->em_slippery_gems ? 1 : 0), file);
592
593   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
594 }
595
596 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
597 {
598   int i;
599
600   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
601     fputc(level->author[i], file);
602 }
603
604 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
605 {
606   int x, y;
607
608   for(y=0; y<level->fieldy; y++) 
609     for(x=0; x<level->fieldx; x++) 
610       if (level->encoding_16bit_field)
611         putFile16BitBE(file, Ur[x][y]);
612       else
613         fputc(Ur[x][y], file);
614 }
615
616 #if 0
617 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
618 {
619   int i, x, y;
620
621   fputc(EL_YAMYAM, file);
622   fputc(level->num_yam_contents, file);
623   fputc(0, file);
624   fputc(0, file);
625
626   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
627     for(y=0; y<3; y++)
628       for(x=0; x<3; x++)
629         if (level->encoding_16bit_field)
630           putFile16BitBE(file, level->yam_content[i][x][y]);
631         else
632           fputc(level->yam_content[i][x][y], file);
633 }
634 #endif
635
636 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
637 {
638   int i, x, y;
639   int num_contents, content_xsize, content_ysize;
640   int content_array[MAX_ELEMENT_CONTENTS][3][3];
641
642   if (element == EL_YAMYAM)
643   {
644     num_contents = level->num_yam_contents;
645     content_xsize = 3;
646     content_ysize = 3;
647
648     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
649       for(y=0; y<3; y++)
650         for(x=0; x<3; x++)
651           content_array[i][x][y] = level->yam_content[i][x][y];
652   }
653   else if (element == EL_BD_AMOEBA)
654   {
655     num_contents = 1;
656     content_xsize = 1;
657     content_ysize = 1;
658
659     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
660       for(y=0; y<3; y++)
661         for(x=0; x<3; x++)
662           content_array[i][x][y] = EL_EMPTY;
663     content_array[0][0][0] = level->amoeba_content;
664   }
665   else
666   {
667     /* chunk header already written -- write empty chunk data */
668     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
669
670     Error(ERR_WARN, "cannot save content for element '%d'", element);
671     return;
672   }
673
674   putFile16BitBE(file, element);
675   fputc(num_contents, file);
676   fputc(content_xsize, file);
677   fputc(content_ysize, file);
678
679   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
680
681   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
682     for(y=0; y<3; y++)
683       for(x=0; x<3; x++)
684         putFile16BitBE(file, content_array[i][x][y]);
685 }
686
687 static void SaveLevel_CUS1(FILE *file, int num_changed_custom_elements)
688 {
689   int i, check = 0;
690
691   putFile16BitBE(file, num_changed_custom_elements);
692
693   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
694   {
695     int element = EL_CUSTOM_START + i;
696
697     if (Properties1[element] != EP_BITMASK_DEFAULT)
698     {
699       if (check < num_changed_custom_elements)
700       {
701         putFile16BitBE(file, element);
702         putFile32BitBE(file, Properties1[element]);
703       }
704
705       check++;
706     }
707   }
708
709   if (check != num_changed_custom_elements)     /* should not happen */
710     Error(ERR_WARN, "inconsistent number of custom element properties");
711 }
712
713 void SaveLevel(int level_nr)
714 {
715   char *filename = getLevelFilename(level_nr);
716   int body_chunk_size;
717   int num_changed_custom_elements = 0;
718   int i, x, y;
719   FILE *file;
720
721   if (!(file = fopen(filename, MODE_WRITE)))
722   {
723     Error(ERR_WARN, "cannot save level file '%s'", filename);
724     return;
725   }
726
727   level.file_version = FILE_VERSION_ACTUAL;
728   level.game_version = GAME_VERSION_ACTUAL;
729
730   /* check level field for 16-bit elements */
731   level.encoding_16bit_field = FALSE;
732   for(y=0; y<level.fieldy; y++) 
733     for(x=0; x<level.fieldx; x++) 
734       if (Ur[x][y] > 255)
735         level.encoding_16bit_field = TRUE;
736
737   /* check yamyam content for 16-bit elements */
738   level.encoding_16bit_yamyam = FALSE;
739   for(i=0; i<level.num_yam_contents; i++)
740     for(y=0; y<3; y++)
741       for(x=0; x<3; x++)
742         if (level.yam_content[i][x][y] > 255)
743           level.encoding_16bit_yamyam = TRUE;
744
745   /* check amoeba content for 16-bit elements */
746   level.encoding_16bit_amoeba = FALSE;
747   if (level.amoeba_content > 255)
748     level.encoding_16bit_amoeba = TRUE;
749
750   /* calculate size of "BODY" chunk */
751   body_chunk_size =
752     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
753
754   /* check for non-standard custom elements and calculate "CUS1" chunk size */
755   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
756     if (Properties1[EL_CUSTOM_START + i] != EP_BITMASK_DEFAULT)
757       num_changed_custom_elements++;
758
759   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
760   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
761
762   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
763   SaveLevel_VERS(file, &level);
764
765   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
766   SaveLevel_HEAD(file, &level);
767
768   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
769   SaveLevel_AUTH(file, &level);
770
771   putFileChunkBE(file, "BODY", body_chunk_size);
772   SaveLevel_BODY(file, &level);
773
774   if (level.encoding_16bit_yamyam ||
775       level.num_yam_contents != STD_ELEMENT_CONTENTS)
776   {
777     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
778     SaveLevel_CNT2(file, &level, EL_YAMYAM);
779   }
780
781   if (level.encoding_16bit_amoeba)
782   {
783     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
784     SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
785   }
786
787   if (num_changed_custom_elements > 0)
788   {
789     putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements * 6);
790     SaveLevel_CUS1(file, num_changed_custom_elements);
791   }
792
793   fclose(file);
794
795   SetFilePermissions(filename, PERMS_PRIVATE);
796 }
797
798 void DumpLevel(struct LevelInfo *level)
799 {
800   printf_line("-", 79);
801   printf("Level xxx (file version %06d, game version %06d)\n",
802          level->file_version, level->game_version);
803   printf_line("-", 79);
804
805   printf("Level Author: '%s'\n", level->author);
806   printf("Level Title:  '%s'\n", level->name);
807   printf("\n");
808   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
809   printf("\n");
810   printf("Level Time:  %d seconds\n", level->time);
811   printf("Gems needed: %d\n", level->gems_needed);
812   printf("\n");
813   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
814   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
815   printf("Time for Light:      %d seconds\n", level->time_light);
816   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
817   printf("\n");
818   printf("Amoeba Speed: %d\n", level->amoeba_speed);
819   printf("\n");
820   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
821   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
822   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
823
824   printf_line("-", 79);
825 }
826
827
828 /* ========================================================================= */
829 /* tape file functions                                                       */
830 /* ========================================================================= */
831
832 static void setTapeInfoToDefaults()
833 {
834   int i;
835
836   /* always start with reliable default values (empty tape) */
837   TapeErase();
838
839   /* default values (also for pre-1.2 tapes) with only the first player */
840   tape.player_participates[0] = TRUE;
841   for(i=1; i<MAX_PLAYERS; i++)
842     tape.player_participates[i] = FALSE;
843
844   /* at least one (default: the first) player participates in every tape */
845   tape.num_participating_players = 1;
846
847   tape.level_nr = level_nr;
848   tape.counter = 0;
849   tape.changed = FALSE;
850
851   tape.recording = FALSE;
852   tape.playing = FALSE;
853   tape.pausing = FALSE;
854 }
855
856 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
857 {
858   tape->file_version = getFileVersion(file);
859   tape->game_version = getFileVersion(file);
860
861   return chunk_size;
862 }
863
864 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
865 {
866   int i;
867
868   tape->random_seed = getFile32BitBE(file);
869   tape->date        = getFile32BitBE(file);
870   tape->length      = getFile32BitBE(file);
871
872   /* read header fields that are new since version 1.2 */
873   if (tape->file_version >= FILE_VERSION_1_2)
874   {
875     byte store_participating_players = fgetc(file);
876     int engine_version;
877
878     /* since version 1.2, tapes store which players participate in the tape */
879     tape->num_participating_players = 0;
880     for(i=0; i<MAX_PLAYERS; i++)
881     {
882       tape->player_participates[i] = FALSE;
883
884       if (store_participating_players & (1 << i))
885       {
886         tape->player_participates[i] = TRUE;
887         tape->num_participating_players++;
888       }
889     }
890
891     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
892
893     engine_version = getFileVersion(file);
894     if (engine_version > 0)
895       tape->engine_version = engine_version;
896   }
897
898   return chunk_size;
899 }
900
901 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
902 {
903   int level_identifier_size;
904   int i;
905
906   level_identifier_size = getFile16BitBE(file);
907
908   tape->level_identifier =
909     checked_realloc(tape->level_identifier, level_identifier_size);
910
911   for(i=0; i < level_identifier_size; i++)
912     tape->level_identifier[i] = fgetc(file);
913
914   tape->level_nr = getFile16BitBE(file);
915
916   chunk_size = 2 + level_identifier_size + 2;
917
918   return chunk_size;
919 }
920
921 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
922 {
923   int i, j;
924   int chunk_size_expected =
925     (tape->num_participating_players + 1) * tape->length;
926
927   if (chunk_size_expected != chunk_size)
928   {
929     ReadUnusedBytesFromFile(file, chunk_size);
930     return chunk_size_expected;
931   }
932
933   for(i=0; i<tape->length; i++)
934   {
935     if (i >= MAX_TAPELEN)
936       break;
937
938     for(j=0; j<MAX_PLAYERS; j++)
939     {
940       tape->pos[i].action[j] = MV_NO_MOVING;
941
942       if (tape->player_participates[j])
943         tape->pos[i].action[j] = fgetc(file);
944     }
945
946     tape->pos[i].delay = fgetc(file);
947
948     if (tape->file_version == FILE_VERSION_1_0)
949     {
950       /* eliminate possible diagonal moves in old tapes */
951       /* this is only for backward compatibility */
952
953       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
954       byte action = tape->pos[i].action[0];
955       int k, num_moves = 0;
956
957       for (k=0; k<4; k++)
958       {
959         if (action & joy_dir[k])
960         {
961           tape->pos[i + num_moves].action[0] = joy_dir[k];
962           if (num_moves > 0)
963             tape->pos[i + num_moves].delay = 0;
964           num_moves++;
965         }
966       }
967
968       if (num_moves > 1)
969       {
970         num_moves--;
971         i += num_moves;
972         tape->length += num_moves;
973       }
974     }
975     else if (tape->file_version < FILE_VERSION_2_0)
976     {
977       /* convert pre-2.0 tapes to new tape format */
978
979       if (tape->pos[i].delay > 1)
980       {
981         /* action part */
982         tape->pos[i + 1] = tape->pos[i];
983         tape->pos[i + 1].delay = 1;
984
985         /* delay part */
986         for(j=0; j<MAX_PLAYERS; j++)
987           tape->pos[i].action[j] = MV_NO_MOVING;
988         tape->pos[i].delay--;
989
990         i++;
991         tape->length++;
992       }
993     }
994
995     if (feof(file))
996       break;
997   }
998
999   if (i != tape->length)
1000     chunk_size = (tape->num_participating_players + 1) * i;
1001
1002   return chunk_size;
1003 }
1004
1005 void LoadTapeFromFilename(char *filename)
1006 {
1007   char cookie[MAX_LINE_LEN];
1008   char chunk_name[CHUNK_ID_LEN + 1];
1009   FILE *file;
1010   int chunk_size;
1011
1012   /* always start with reliable default values */
1013   setTapeInfoToDefaults();
1014
1015   if (!(file = fopen(filename, MODE_READ)))
1016     return;
1017
1018   getFileChunkBE(file, chunk_name, NULL);
1019   if (strcmp(chunk_name, "RND1") == 0)
1020   {
1021     getFile32BitBE(file);               /* not used */
1022
1023     getFileChunkBE(file, chunk_name, NULL);
1024     if (strcmp(chunk_name, "TAPE") != 0)
1025     {
1026       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1027       fclose(file);
1028       return;
1029     }
1030   }
1031   else  /* check for pre-2.0 file format with cookie string */
1032   {
1033     strcpy(cookie, chunk_name);
1034     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1035     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1036       cookie[strlen(cookie) - 1] = '\0';
1037
1038     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1039     {
1040       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1041       fclose(file);
1042       return;
1043     }
1044
1045     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1046     {
1047       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1048       fclose(file);
1049       return;
1050     }
1051
1052     /* pre-2.0 tape files have no game version, so use file version here */
1053     tape.game_version = tape.file_version;
1054   }
1055
1056   if (tape.file_version < FILE_VERSION_1_2)
1057   {
1058     /* tape files from versions before 1.2.0 without chunk structure */
1059     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1060     LoadTape_BODY(file, 2 * tape.length,  &tape);
1061   }
1062   else
1063   {
1064     static struct
1065     {
1066       char *name;
1067       int size;
1068       int (*loader)(FILE *, int, struct TapeInfo *);
1069     }
1070     chunk_info[] =
1071     {
1072       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1073       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1074       { "INFO", -1,                     LoadTape_INFO },
1075       { "BODY", -1,                     LoadTape_BODY },
1076       {  NULL,  0,                      NULL }
1077     };
1078
1079     while (getFileChunkBE(file, chunk_name, &chunk_size))
1080     {
1081       int i = 0;
1082
1083       while (chunk_info[i].name != NULL &&
1084              strcmp(chunk_name, chunk_info[i].name) != 0)
1085         i++;
1086
1087       if (chunk_info[i].name == NULL)
1088       {
1089         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1090               chunk_name, filename);
1091         ReadUnusedBytesFromFile(file, chunk_size);
1092       }
1093       else if (chunk_info[i].size != -1 &&
1094                chunk_info[i].size != chunk_size)
1095       {
1096         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1097               chunk_size, chunk_name, filename);
1098         ReadUnusedBytesFromFile(file, chunk_size);
1099       }
1100       else
1101       {
1102         /* call function to load this tape chunk */
1103         int chunk_size_expected =
1104           (chunk_info[i].loader)(file, chunk_size, &tape);
1105
1106         /* the size of some chunks cannot be checked before reading other
1107            chunks first (like "HEAD" and "BODY") that contain some header
1108            information, so check them here */
1109         if (chunk_size_expected != chunk_size)
1110         {
1111           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1112                 chunk_size, chunk_name, filename);
1113         }
1114       }
1115     }
1116   }
1117
1118   fclose(file);
1119
1120   tape.length_seconds = GetTapeLength();
1121 }
1122
1123 void LoadTape(int level_nr)
1124 {
1125   char *filename = getTapeFilename(level_nr);
1126
1127   LoadTapeFromFilename(filename);
1128 }
1129
1130 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1131 {
1132   putFileVersion(file, tape->file_version);
1133   putFileVersion(file, tape->game_version);
1134 }
1135
1136 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1137 {
1138   int i;
1139   byte store_participating_players = 0;
1140
1141   /* set bits for participating players for compact storage */
1142   for(i=0; i<MAX_PLAYERS; i++)
1143     if (tape->player_participates[i])
1144       store_participating_players |= (1 << i);
1145
1146   putFile32BitBE(file, tape->random_seed);
1147   putFile32BitBE(file, tape->date);
1148   putFile32BitBE(file, tape->length);
1149
1150   fputc(store_participating_players, file);
1151
1152   /* unused bytes not at the end here for 4-byte alignment of engine_version */
1153   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1154
1155   putFileVersion(file, tape->engine_version);
1156 }
1157
1158 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1159 {
1160   int level_identifier_size = strlen(tape->level_identifier) + 1;
1161   int i;
1162
1163   putFile16BitBE(file, level_identifier_size);
1164
1165   for(i=0; i < level_identifier_size; i++)
1166     fputc(tape->level_identifier[i], file);
1167
1168   putFile16BitBE(file, tape->level_nr);
1169 }
1170
1171 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1172 {
1173   int i, j;
1174
1175   for(i=0; i<tape->length; i++)
1176   {
1177     for(j=0; j<MAX_PLAYERS; j++)
1178       if (tape->player_participates[j])
1179         fputc(tape->pos[i].action[j], file);
1180
1181     fputc(tape->pos[i].delay, file);
1182   }
1183 }
1184
1185 void SaveTape(int level_nr)
1186 {
1187   char *filename = getTapeFilename(level_nr);
1188   FILE *file;
1189   boolean new_tape = TRUE;
1190   int num_participating_players = 0;
1191   int info_chunk_size;
1192   int body_chunk_size;
1193   int i;
1194
1195   InitTapeDirectory(leveldir_current->filename);
1196
1197   /* if a tape still exists, ask to overwrite it */
1198   if (access(filename, F_OK) == 0)
1199   {
1200     new_tape = FALSE;
1201     if (!Request("Replace old tape ?", REQ_ASK))
1202       return;
1203   }
1204
1205   if (!(file = fopen(filename, MODE_WRITE)))
1206   {
1207     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1208     return;
1209   }
1210
1211   tape.file_version = FILE_VERSION_ACTUAL;
1212   tape.game_version = GAME_VERSION_ACTUAL;
1213
1214   /* count number of participating players  */
1215   for(i=0; i<MAX_PLAYERS; i++)
1216     if (tape.player_participates[i])
1217       num_participating_players++;
1218
1219   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1220   body_chunk_size = (num_participating_players + 1) * tape.length;
1221
1222   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1223   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1224
1225   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1226   SaveTape_VERS(file, &tape);
1227
1228   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1229   SaveTape_HEAD(file, &tape);
1230
1231   putFileChunkBE(file, "INFO", info_chunk_size);
1232   SaveTape_INFO(file, &tape);
1233
1234   putFileChunkBE(file, "BODY", body_chunk_size);
1235   SaveTape_BODY(file, &tape);
1236
1237   fclose(file);
1238
1239   SetFilePermissions(filename, PERMS_PRIVATE);
1240
1241   tape.changed = FALSE;
1242
1243   if (new_tape)
1244     Request("tape saved !", REQ_CONFIRM);
1245 }
1246
1247 void DumpTape(struct TapeInfo *tape)
1248 {
1249   int i, j;
1250
1251   if (TAPE_IS_EMPTY(*tape))
1252   {
1253     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1254     return;
1255   }
1256
1257   printf_line("-", 79);
1258   printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1259          tape->level_nr, tape->file_version, tape->game_version);
1260   printf("Level series identifier: '%s'\n", tape->level_identifier);
1261   printf_line("-", 79);
1262
1263   for(i=0; i<tape->length; i++)
1264   {
1265     if (i >= MAX_TAPELEN)
1266       break;
1267
1268     printf("%03d: ", i);
1269
1270     for(j=0; j<MAX_PLAYERS; j++)
1271     {
1272       if (tape->player_participates[j])
1273       {
1274         int action = tape->pos[i].action[j];
1275
1276         printf("%d:%02x ", j, action);
1277         printf("[%c%c%c%c|%c%c] - ",
1278                (action & JOY_LEFT ? '<' : ' '),
1279                (action & JOY_RIGHT ? '>' : ' '),
1280                (action & JOY_UP ? '^' : ' '),
1281                (action & JOY_DOWN ? 'v' : ' '),
1282                (action & JOY_BUTTON_1 ? '1' : ' '),
1283                (action & JOY_BUTTON_2 ? '2' : ' '));
1284       }
1285     }
1286
1287     printf("(%03d)\n", tape->pos[i].delay);
1288   }
1289
1290   printf_line("-", 79);
1291 }
1292
1293
1294 /* ========================================================================= */
1295 /* score file functions                                                      */
1296 /* ========================================================================= */
1297
1298 void LoadScore(int level_nr)
1299 {
1300   int i;
1301   char *filename = getScoreFilename(level_nr);
1302   char cookie[MAX_LINE_LEN];
1303   char line[MAX_LINE_LEN];
1304   char *line_ptr;
1305   FILE *file;
1306
1307   /* always start with reliable default values */
1308   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1309   {
1310     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1311     highscore[i].Score = 0;
1312   }
1313
1314   if (!(file = fopen(filename, MODE_READ)))
1315     return;
1316
1317   /* check file identifier */
1318   fgets(cookie, MAX_LINE_LEN, file);
1319   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1320     cookie[strlen(cookie) - 1] = '\0';
1321
1322   if (!checkCookieString(cookie, SCORE_COOKIE))
1323   {
1324     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1325     fclose(file);
1326     return;
1327   }
1328
1329   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1330   {
1331     fscanf(file, "%d", &highscore[i].Score);
1332     fgets(line, MAX_LINE_LEN, file);
1333
1334     if (line[strlen(line) - 1] == '\n')
1335       line[strlen(line) - 1] = '\0';
1336
1337     for (line_ptr = line; *line_ptr; line_ptr++)
1338     {
1339       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1340       {
1341         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1342         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1343         break;
1344       }
1345     }
1346   }
1347
1348   fclose(file);
1349 }
1350
1351 void SaveScore(int level_nr)
1352 {
1353   int i;
1354   char *filename = getScoreFilename(level_nr);
1355   FILE *file;
1356
1357   InitScoreDirectory(leveldir_current->filename);
1358
1359   if (!(file = fopen(filename, MODE_WRITE)))
1360   {
1361     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1362     return;
1363   }
1364
1365   fprintf(file, "%s\n\n", SCORE_COOKIE);
1366
1367   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1368     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1369
1370   fclose(file);
1371
1372   SetFilePermissions(filename, PERMS_PUBLIC);
1373 }
1374
1375
1376 /* ========================================================================= */
1377 /* setup file functions                                                      */
1378 /* ========================================================================= */
1379
1380 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1381
1382 /* global setup */
1383 #define SETUP_TOKEN_PLAYER_NAME                 0
1384 #define SETUP_TOKEN_SOUND                       1
1385 #define SETUP_TOKEN_SOUND_LOOPS                 2
1386 #define SETUP_TOKEN_SOUND_MUSIC                 3
1387 #define SETUP_TOKEN_SOUND_SIMPLE                4
1388 #define SETUP_TOKEN_TOONS                       5
1389 #define SETUP_TOKEN_SCROLL_DELAY                6
1390 #define SETUP_TOKEN_SOFT_SCROLLING              7
1391 #define SETUP_TOKEN_FADING                      8
1392 #define SETUP_TOKEN_AUTORECORD                  9
1393 #define SETUP_TOKEN_QUICK_DOORS                 10
1394 #define SETUP_TOKEN_TEAM_MODE                   11
1395 #define SETUP_TOKEN_HANDICAP                    12
1396 #define SETUP_TOKEN_TIME_LIMIT                  13
1397 #define SETUP_TOKEN_FULLSCREEN                  14
1398 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1399 #define SETUP_TOKEN_GRAPHICS_SET                16
1400 #define SETUP_TOKEN_SOUNDS_SET                  17
1401 #define SETUP_TOKEN_MUSIC_SET                   18
1402 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1403 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1404 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1405
1406 #define NUM_GLOBAL_SETUP_TOKENS                 22
1407
1408 /* editor setup */
1409 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
1410 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
1411 #define SETUP_TOKEN_EDITOR_EL_MORE              2
1412 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
1413 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
1414 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
1415 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
1416 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
1417 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
1418
1419 #define NUM_EDITOR_SETUP_TOKENS                 9
1420
1421 /* shortcut setup */
1422 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
1423 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
1424 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
1425
1426 #define NUM_SHORTCUT_SETUP_TOKENS               3
1427
1428 /* player setup */
1429 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
1430 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
1431 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
1432 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
1433 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
1434 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
1435 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
1436 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
1437 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
1438 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
1439 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
1440 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
1441 #define SETUP_TOKEN_PLAYER_KEY_UP               12
1442 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
1443 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
1444 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
1445
1446 #define NUM_PLAYER_SETUP_TOKENS                 16
1447
1448 /* system setup */
1449 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
1450 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
1451
1452 #define NUM_SYSTEM_SETUP_TOKENS                 2
1453
1454 /* options setup */
1455 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
1456
1457 #define NUM_OPTIONS_SETUP_TOKENS                1
1458
1459
1460 static struct SetupInfo si;
1461 static struct SetupEditorInfo sei;
1462 static struct SetupShortcutInfo ssi;
1463 static struct SetupInputInfo sii;
1464 static struct SetupSystemInfo syi;
1465 static struct OptionInfo soi;
1466
1467 static struct TokenInfo global_setup_tokens[] =
1468 {
1469   { TYPE_STRING, &si.player_name,       "player_name"                   },
1470   { TYPE_SWITCH, &si.sound,             "sound"                         },
1471   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1472   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1473   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1474   { TYPE_SWITCH, &si.toons,             "toons"                         },
1475   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1476   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1477   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1478   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1479   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1480   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1481   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1482   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1483   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1484   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1485   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1486   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1487   { TYPE_STRING, &si.music_set,         "music_set"                     },
1488   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1489   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1490   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1491 };
1492
1493 static struct TokenInfo editor_setup_tokens[] =
1494 {
1495   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1496   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1497   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1498   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1499   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1500   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1501   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1502   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1503   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1504 };
1505
1506 static struct TokenInfo shortcut_setup_tokens[] =
1507 {
1508   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1509   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1510   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1511 };
1512
1513 static struct TokenInfo player_setup_tokens[] =
1514 {
1515   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1516   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1517   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1518   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1519   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1520   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1521   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1522   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1523   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1524   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1525   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1526   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1527   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1528   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1529   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1530   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1531 };
1532
1533 static struct TokenInfo system_setup_tokens[] =
1534 {
1535   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
1536   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1537 };
1538
1539 static struct TokenInfo options_setup_tokens[] =
1540 {
1541   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
1542 };
1543
1544 static char *get_corrected_login_name(char *login_name)
1545 {
1546   /* needed because player name must be a fixed length string */
1547   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1548
1549   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1550   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1551
1552   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
1553     if (strchr(login_name_new, ' '))
1554       *strchr(login_name_new, ' ') = '\0';
1555
1556   return login_name_new;
1557 }
1558
1559 static void setSetupInfoToDefaults(struct SetupInfo *si)
1560 {
1561   int i;
1562
1563   si->player_name = get_corrected_login_name(getLoginName());
1564
1565   si->sound = TRUE;
1566   si->sound_loops = TRUE;
1567   si->sound_music = TRUE;
1568   si->sound_simple = TRUE;
1569   si->toons = TRUE;
1570   si->double_buffering = TRUE;
1571   si->direct_draw = !si->double_buffering;
1572   si->scroll_delay = TRUE;
1573   si->soft_scrolling = TRUE;
1574   si->fading = FALSE;
1575   si->autorecord = TRUE;
1576   si->quick_doors = FALSE;
1577   si->team_mode = FALSE;
1578   si->handicap = TRUE;
1579   si->time_limit = TRUE;
1580   si->fullscreen = FALSE;
1581   si->ask_on_escape = TRUE;
1582
1583   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1584   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1585   si->music_set = getStringCopy(MUSIC_SUBDIR);
1586   si->override_level_graphics = FALSE;
1587   si->override_level_sounds = FALSE;
1588   si->override_level_music = FALSE;
1589
1590   si->editor.el_boulderdash = TRUE;
1591   si->editor.el_emerald_mine = TRUE;
1592   si->editor.el_more = TRUE;
1593   si->editor.el_sokoban = TRUE;
1594   si->editor.el_supaplex = TRUE;
1595   si->editor.el_diamond_caves = TRUE;
1596   si->editor.el_dx_boulderdash = TRUE;
1597   si->editor.el_chars = TRUE;
1598   si->editor.el_custom = TRUE;
1599
1600   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1601   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1602   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1603
1604   for (i=0; i<MAX_PLAYERS; i++)
1605   {
1606     si->input[i].use_joystick = FALSE;
1607     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1608     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1609     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1610     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1611     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1612     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1613     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1614     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1615     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1616     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1617     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1618     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1619     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1620     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1621     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1622   }
1623
1624   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1625   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1626
1627   si->options.verbose = FALSE;
1628 }
1629
1630 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1631 {
1632   int i, pnr;
1633
1634   if (!setup_file_list)
1635     return;
1636
1637   /* global setup */
1638   si = setup;
1639   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1640     setSetupInfo(global_setup_tokens, i,
1641                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1642   setup = si;
1643
1644   /* editor setup */
1645   sei = setup.editor;
1646   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1647     setSetupInfo(editor_setup_tokens, i,
1648                  getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1649   setup.editor = sei;
1650
1651   /* shortcut setup */
1652   ssi = setup.shortcut;
1653   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1654     setSetupInfo(shortcut_setup_tokens, i,
1655                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1656   setup.shortcut = ssi;
1657
1658   /* player setup */
1659   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1660   {
1661     char prefix[30];
1662
1663     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1664
1665     sii = setup.input[pnr];
1666     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1667     {
1668       char full_token[100];
1669
1670       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1671       setSetupInfo(player_setup_tokens, i,
1672                    getTokenValue(setup_file_list, full_token));
1673     }
1674     setup.input[pnr] = sii;
1675   }
1676
1677   /* system setup */
1678   syi = setup.system;
1679   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1680     setSetupInfo(system_setup_tokens, i,
1681                  getTokenValue(setup_file_list, system_setup_tokens[i].text));
1682   setup.system = syi;
1683
1684   /* options setup */
1685   soi = setup.options;
1686   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1687     setSetupInfo(options_setup_tokens, i,
1688                  getTokenValue(setup_file_list, options_setup_tokens[i].text));
1689   setup.options = soi;
1690 }
1691
1692 void LoadSetup()
1693 {
1694   char *filename = getSetupFilename();
1695   struct SetupFileList *setup_file_list = NULL;
1696
1697   /* always start with reliable default values */
1698   setSetupInfoToDefaults(&setup);
1699
1700   setup_file_list = loadSetupFileList(filename);
1701
1702   if (setup_file_list)
1703   {
1704     char *player_name_new;
1705
1706     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1707     decodeSetupFileList(setup_file_list);
1708
1709     setup.direct_draw = !setup.double_buffering;
1710
1711     freeSetupFileList(setup_file_list);
1712
1713     /* needed to work around problems with fixed length strings */
1714     player_name_new = get_corrected_login_name(setup.player_name);
1715     free(setup.player_name);
1716     setup.player_name = player_name_new;
1717   }
1718   else
1719     Error(ERR_WARN, "using default setup values");
1720 }
1721
1722 void SaveSetup()
1723 {
1724   char *filename = getSetupFilename();
1725   FILE *file;
1726   int i, pnr;
1727
1728   InitUserDataDirectory();
1729
1730   if (!(file = fopen(filename, MODE_WRITE)))
1731   {
1732     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1733     return;
1734   }
1735
1736   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1737                                                getCookie("SETUP")));
1738   fprintf(file, "\n");
1739
1740   /* global setup */
1741   si = setup;
1742   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1743   {
1744     /* just to make things nicer :) */
1745     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1746         i == SETUP_TOKEN_GRAPHICS_SET)
1747       fprintf(file, "\n");
1748
1749     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1750   }
1751
1752   /* editor setup */
1753   sei = setup.editor;
1754   fprintf(file, "\n");
1755   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1756     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1757
1758   /* shortcut setup */
1759   ssi = setup.shortcut;
1760   fprintf(file, "\n");
1761   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1762     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1763
1764   /* player setup */
1765   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1766   {
1767     char prefix[30];
1768
1769     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1770     fprintf(file, "\n");
1771
1772     sii = setup.input[pnr];
1773     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1774       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1775   }
1776
1777   /* system setup */
1778   syi = setup.system;
1779   fprintf(file, "\n");
1780   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1781     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1782
1783   /* options setup */
1784   soi = setup.options;
1785   fprintf(file, "\n");
1786   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1787     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1788
1789   fclose(file);
1790
1791   SetFilePermissions(filename, PERMS_PRIVATE);
1792 }
1793
1794 void LoadCustomElementDescriptions()
1795 {
1796   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1797   struct SetupFileList *setup_file_list;
1798   int i;
1799
1800   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1801   {
1802     if (element_info[i].custom_description != NULL)
1803     {
1804       free(element_info[i].custom_description);
1805       element_info[i].custom_description = NULL;
1806     }
1807   }
1808
1809   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1810     return;
1811
1812   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1813   {
1814     char *token = getStringCat2(element_info[i].token_name, ".name");
1815     char *value = getTokenValue(setup_file_list, token);
1816
1817     if (value != NULL)
1818       element_info[i].custom_description = getStringCopy(value);
1819
1820     free(token);
1821   }
1822
1823   freeSetupFileList(setup_file_list);
1824 }
1825
1826 void LoadSpecialMenuDesignSettings()
1827 {
1828   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1829   struct SetupFileList *setup_file_list;
1830   char *value;
1831
1832   /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1833   global.num_toons = 20;
1834   global.menu_draw_xoffset = 0;
1835   global.menu_draw_yoffset = 0;
1836   global.menu_draw_xoffset_MAIN = 0;
1837   global.menu_draw_yoffset_MAIN = 0;
1838   global.door_step_offset = 2;
1839   global.door_step_delay = 10;
1840
1841   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1842     return;
1843
1844   value = getTokenValue(setup_file_list, "global.num_toons");
1845   if (value != NULL)
1846     global.num_toons = get_integer_from_string(value);
1847
1848   value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1849   if (value != NULL)
1850     global.menu_draw_xoffset = get_integer_from_string(value);
1851
1852   value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1853   if (value != NULL)
1854     global.menu_draw_yoffset = get_integer_from_string(value);
1855
1856   value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1857   if (value != NULL)
1858     global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1859
1860   value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1861   if (value != NULL)
1862     global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1863
1864   value = getTokenValue(setup_file_list, "door.step_offset");
1865   if (value != NULL)
1866     global.door_step_offset = get_integer_from_string(value);
1867
1868   value = getTokenValue(setup_file_list, "door.step_delay");
1869   if (value != NULL)
1870     global.door_step_delay = get_integer_from_string(value);
1871
1872   freeSetupFileList(setup_file_list);
1873 }