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