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