3286dd07131ca5cc4a2a7ccd07a2aaf7a0b5294c
[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       else if (level->game_version < VERSION_IDENT(3,0,0))
757       {
758         /* map Supaplex gravity tube elements */
759         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
760                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
761                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
762                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
763                    element);
764       }
765
766       level->field[x][y] = element;
767     }
768   }
769
770   /* copy elements to runtime playfield array */
771   for(x=0; x<MAX_LEV_FIELDX; x++)
772     for(y=0; y<MAX_LEV_FIELDY; y++)
773       Feld[x][y] = level->field[x][y];
774
775   /* initialize level size variables for faster access */
776   lev_fieldx = level->fieldx;
777   lev_fieldy = level->fieldy;
778
779   /* determine border element for this level */
780   SetBorderElement();
781
782   /* initialize element properties for level editor etc. */
783   InitElementPropertiesEngine(level->game_version);
784 }
785
786 void LoadLevelTemplate(int level_nr)
787 {
788   char *filename = getLevelFilename(level_nr);
789
790   LoadLevelFromFilename(&level_template, filename);
791
792   ActivateLevelTemplate();
793 }
794
795 void LoadLevel(int level_nr)
796 {
797   char *filename = getLevelFilename(level_nr);
798
799   LoadLevelFromFilename(&level, filename);
800
801   if (level.use_custom_template)
802     LoadLevelTemplate(-1);
803
804   LoadLevel_InitLevel(&level, filename);
805 }
806
807 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
808 {
809   putFileVersion(file, level->file_version);
810   putFileVersion(file, level->game_version);
811 }
812
813 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
814 {
815   int i, x, y;
816
817   putFile8Bit(file, level->fieldx);
818   putFile8Bit(file, level->fieldy);
819
820   putFile16BitBE(file, level->time);
821   putFile16BitBE(file, level->gems_needed);
822
823   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
824     putFile8Bit(file, level->name[i]);
825
826   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
827     putFile8Bit(file, level->score[i]);
828
829   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
830     for(y=0; y<3; y++)
831       for(x=0; x<3; x++)
832         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
833                            level->yamyam_content[i][x][y]));
834   putFile8Bit(file, level->amoeba_speed);
835   putFile8Bit(file, level->time_magic_wall);
836   putFile8Bit(file, level->time_wheel);
837   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
838                      level->amoeba_content));
839   putFile8Bit(file, (level->double_speed ? 1 : 0));
840   putFile8Bit(file, (level->gravity ? 1 : 0));
841   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
842   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
843
844   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
845
846   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
847 }
848
849 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
850 {
851   int i;
852
853   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
854     putFile8Bit(file, level->author[i]);
855 }
856
857 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
858 {
859   int x, y;
860
861   for(y=0; y<level->fieldy; y++) 
862     for(x=0; x<level->fieldx; x++) 
863       if (level->encoding_16bit_field)
864         putFile16BitBE(file, level->field[x][y]);
865       else
866         putFile8Bit(file, level->field[x][y]);
867 }
868
869 #if 0
870 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
871 {
872   int i, x, y;
873
874   putFile8Bit(file, EL_YAMYAM);
875   putFile8Bit(file, level->num_yamyam_contents);
876   putFile8Bit(file, 0);
877   putFile8Bit(file, 0);
878
879   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
880     for(y=0; y<3; y++)
881       for(x=0; x<3; x++)
882         if (level->encoding_16bit_field)
883           putFile16BitBE(file, level->yamyam_content[i][x][y]);
884         else
885           putFile8Bit(file, level->yamyam_content[i][x][y]);
886 }
887 #endif
888
889 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
890 {
891   int i, x, y;
892   int num_contents, content_xsize, content_ysize;
893   int content_array[MAX_ELEMENT_CONTENTS][3][3];
894
895   if (element == EL_YAMYAM)
896   {
897     num_contents = level->num_yamyam_contents;
898     content_xsize = 3;
899     content_ysize = 3;
900
901     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
902       for(y=0; y<3; y++)
903         for(x=0; x<3; x++)
904           content_array[i][x][y] = level->yamyam_content[i][x][y];
905   }
906   else if (element == EL_BD_AMOEBA)
907   {
908     num_contents = 1;
909     content_xsize = 1;
910     content_ysize = 1;
911
912     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
913       for(y=0; y<3; y++)
914         for(x=0; x<3; x++)
915           content_array[i][x][y] = EL_EMPTY;
916     content_array[0][0][0] = level->amoeba_content;
917   }
918   else
919   {
920     /* chunk header already written -- write empty chunk data */
921     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
922
923     Error(ERR_WARN, "cannot save content for element '%d'", element);
924     return;
925   }
926
927   putFile16BitBE(file, element);
928   putFile8Bit(file, num_contents);
929   putFile8Bit(file, content_xsize);
930   putFile8Bit(file, content_ysize);
931
932   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
933
934   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
935     for(y=0; y<3; y++)
936       for(x=0; x<3; x++)
937         putFile16BitBE(file, content_array[i][x][y]);
938 }
939
940 #if 0
941 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
942                            int num_changed_custom_elements)
943 {
944   int i, check = 0;
945
946   putFile16BitBE(file, num_changed_custom_elements);
947
948   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
949   {
950     int element = EL_CUSTOM_START + i;
951
952     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
953     {
954       if (check < num_changed_custom_elements)
955       {
956         putFile16BitBE(file, element);
957         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
958       }
959
960       check++;
961     }
962   }
963
964   if (check != num_changed_custom_elements)     /* should not happen */
965     Error(ERR_WARN, "inconsistent number of custom element properties");
966 }
967 #endif
968
969 #if 0
970 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
971                            int num_changed_custom_elements)
972 {
973   int i, check = 0;
974
975   putFile16BitBE(file, num_changed_custom_elements);
976
977   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
978   {
979     int element = EL_CUSTOM_START + i;
980
981     if (element_info[element].change.target_element != EL_EMPTY_SPACE)
982     {
983       if (check < num_changed_custom_elements)
984       {
985         putFile16BitBE(file, element);
986         putFile16BitBE(file, element_info[element].change.target_element);
987       }
988
989       check++;
990     }
991   }
992
993   if (check != num_changed_custom_elements)     /* should not happen */
994     Error(ERR_WARN, "inconsistent number of custom target elements");
995 }
996 #endif
997
998 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
999                            int num_changed_custom_elements)
1000 {
1001   int i, j, x, y, check = 0;
1002
1003   putFile16BitBE(file, num_changed_custom_elements);
1004
1005   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1006   {
1007     int element = EL_CUSTOM_START + i;
1008
1009     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1010     {
1011       if (check < num_changed_custom_elements)
1012       {
1013         putFile16BitBE(file, element);
1014
1015         for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1016           putFile8Bit(file, element_info[element].description[j]);
1017
1018         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1019
1020         /* some free bytes for future properties and padding */
1021         WriteUnusedBytesToFile(file, 7);
1022
1023         putFile8Bit(file, element_info[element].use_gfx_element);
1024         putFile16BitBE(file, element_info[element].gfx_element);
1025
1026         putFile8Bit(file, element_info[element].score);
1027         putFile8Bit(file, element_info[element].gem_count);
1028
1029         putFile16BitBE(file, element_info[element].push_delay_fixed);
1030         putFile16BitBE(file, element_info[element].push_delay_random);
1031         putFile16BitBE(file, element_info[element].move_delay_fixed);
1032         putFile16BitBE(file, element_info[element].move_delay_random);
1033
1034         putFile16BitBE(file, element_info[element].move_pattern);
1035         putFile8Bit(file, element_info[element].move_direction_initial);
1036         putFile8Bit(file, element_info[element].move_stepsize);
1037
1038         for(y=0; y<3; y++)
1039           for(x=0; x<3; x++)
1040             putFile16BitBE(file, element_info[element].content[x][y]);
1041
1042         putFile32BitBE(file, element_info[element].change.events);
1043
1044         putFile16BitBE(file, element_info[element].change.target_element);
1045
1046         putFile16BitBE(file, element_info[element].change.delay_fixed);
1047         putFile16BitBE(file, element_info[element].change.delay_random);
1048         putFile16BitBE(file, element_info[element].change.delay_frames);
1049
1050         putFile16BitBE(file, element_info[element].change.trigger_element);
1051
1052         putFile8Bit(file, element_info[element].change.explode);
1053         putFile8Bit(file, element_info[element].change.use_content);
1054         putFile8Bit(file, element_info[element].change.only_complete);
1055         putFile8Bit(file, element_info[element].change.use_random_change);
1056
1057         putFile8Bit(file, element_info[element].change.random);
1058         putFile8Bit(file, element_info[element].change.power);
1059
1060         for(y=0; y<3; y++)
1061           for(x=0; x<3; x++)
1062             putFile16BitBE(file, element_info[element].change.content[x][y]);
1063
1064         putFile8Bit(file, element_info[element].slippery_type);
1065
1066         /* some free bytes for future properties and padding */
1067         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1068       }
1069
1070       check++;
1071     }
1072   }
1073
1074   if (check != num_changed_custom_elements)     /* should not happen */
1075     Error(ERR_WARN, "inconsistent number of custom element properties");
1076 }
1077
1078 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1079 {
1080   int body_chunk_size;
1081   int num_changed_custom_elements = 0;
1082   int level_chunk_CUS3_size;
1083   int i, x, y;
1084   FILE *file;
1085
1086   if (!(file = fopen(filename, MODE_WRITE)))
1087   {
1088     Error(ERR_WARN, "cannot save level file '%s'", filename);
1089     return;
1090   }
1091
1092   level->file_version = FILE_VERSION_ACTUAL;
1093   level->game_version = GAME_VERSION_ACTUAL;
1094
1095   /* check level field for 16-bit elements */
1096   level->encoding_16bit_field = FALSE;
1097   for(y=0; y<level->fieldy; y++) 
1098     for(x=0; x<level->fieldx; x++) 
1099       if (level->field[x][y] > 255)
1100         level->encoding_16bit_field = TRUE;
1101
1102   /* check yamyam content for 16-bit elements */
1103   level->encoding_16bit_yamyam = FALSE;
1104   for(i=0; i<level->num_yamyam_contents; i++)
1105     for(y=0; y<3; y++)
1106       for(x=0; x<3; x++)
1107         if (level->yamyam_content[i][x][y] > 255)
1108           level->encoding_16bit_yamyam = TRUE;
1109
1110   /* check amoeba content for 16-bit elements */
1111   level->encoding_16bit_amoeba = FALSE;
1112   if (level->amoeba_content > 255)
1113     level->encoding_16bit_amoeba = TRUE;
1114
1115   /* calculate size of "BODY" chunk */
1116   body_chunk_size =
1117     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1118
1119   /* check for non-standard custom elements and calculate "CUS3" chunk size */
1120   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1121     if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1122       num_changed_custom_elements++;
1123   level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1124
1125   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1126   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1127
1128   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1129   SaveLevel_VERS(file, level);
1130
1131   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1132   SaveLevel_HEAD(file, level);
1133
1134   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1135   SaveLevel_AUTH(file, level);
1136
1137   putFileChunkBE(file, "BODY", body_chunk_size);
1138   SaveLevel_BODY(file, level);
1139
1140   if (level->encoding_16bit_yamyam ||
1141       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1142   {
1143     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1144     SaveLevel_CNT2(file, level, EL_YAMYAM);
1145   }
1146
1147   if (level->encoding_16bit_amoeba)
1148   {
1149     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1150     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1151   }
1152
1153   if (num_changed_custom_elements > 0 && !level->use_custom_template)
1154   {
1155     putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1156     SaveLevel_CUS3(file, level, num_changed_custom_elements);
1157   }
1158
1159   fclose(file);
1160
1161   SetFilePermissions(filename, PERMS_PRIVATE);
1162 }
1163
1164 void SaveLevel(int level_nr)
1165 {
1166   char *filename = getLevelFilename(level_nr);
1167
1168   SaveLevelFromFilename(&level, filename);
1169 }
1170
1171 void SaveLevelTemplate()
1172 {
1173   char *filename = getLevelFilename(-1);
1174
1175   SaveLevelFromFilename(&level, filename);
1176 }
1177
1178 void DumpLevel(struct LevelInfo *level)
1179 {
1180   printf_line("-", 79);
1181   printf("Level xxx (file version %08d, game version %08d)\n",
1182          level->file_version, level->game_version);
1183   printf_line("-", 79);
1184
1185   printf("Level Author: '%s'\n", level->author);
1186   printf("Level Title:  '%s'\n", level->name);
1187   printf("\n");
1188   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1189   printf("\n");
1190   printf("Level Time:  %d seconds\n", level->time);
1191   printf("Gems needed: %d\n", level->gems_needed);
1192   printf("\n");
1193   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1194   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
1195   printf("Time for Light:      %d seconds\n", level->time_light);
1196   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
1197   printf("\n");
1198   printf("Amoeba Speed: %d\n", level->amoeba_speed);
1199   printf("\n");
1200   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
1201   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
1202   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1203
1204   printf_line("-", 79);
1205 }
1206
1207
1208 /* ========================================================================= */
1209 /* tape file functions                                                       */
1210 /* ========================================================================= */
1211
1212 static void setTapeInfoToDefaults()
1213 {
1214   int i;
1215
1216   /* always start with reliable default values (empty tape) */
1217   TapeErase();
1218
1219   /* default values (also for pre-1.2 tapes) with only the first player */
1220   tape.player_participates[0] = TRUE;
1221   for(i=1; i<MAX_PLAYERS; i++)
1222     tape.player_participates[i] = FALSE;
1223
1224   /* at least one (default: the first) player participates in every tape */
1225   tape.num_participating_players = 1;
1226
1227   tape.level_nr = level_nr;
1228   tape.counter = 0;
1229   tape.changed = FALSE;
1230
1231   tape.recording = FALSE;
1232   tape.playing = FALSE;
1233   tape.pausing = FALSE;
1234 }
1235
1236 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1237 {
1238   tape->file_version = getFileVersion(file);
1239   tape->game_version = getFileVersion(file);
1240
1241   return chunk_size;
1242 }
1243
1244 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1245 {
1246   int i;
1247
1248   tape->random_seed = getFile32BitBE(file);
1249   tape->date        = getFile32BitBE(file);
1250   tape->length      = getFile32BitBE(file);
1251
1252   /* read header fields that are new since version 1.2 */
1253   if (tape->file_version >= FILE_VERSION_1_2)
1254   {
1255     byte store_participating_players = getFile8Bit(file);
1256     int engine_version;
1257
1258     /* since version 1.2, tapes store which players participate in the tape */
1259     tape->num_participating_players = 0;
1260     for(i=0; i<MAX_PLAYERS; i++)
1261     {
1262       tape->player_participates[i] = FALSE;
1263
1264       if (store_participating_players & (1 << i))
1265       {
1266         tape->player_participates[i] = TRUE;
1267         tape->num_participating_players++;
1268       }
1269     }
1270
1271     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1272
1273     engine_version = getFileVersion(file);
1274     if (engine_version > 0)
1275       tape->engine_version = engine_version;
1276   }
1277
1278   return chunk_size;
1279 }
1280
1281 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1282 {
1283   int level_identifier_size;
1284   int i;
1285
1286   level_identifier_size = getFile16BitBE(file);
1287
1288   tape->level_identifier =
1289     checked_realloc(tape->level_identifier, level_identifier_size);
1290
1291   for(i=0; i < level_identifier_size; i++)
1292     tape->level_identifier[i] = getFile8Bit(file);
1293
1294   tape->level_nr = getFile16BitBE(file);
1295
1296   chunk_size = 2 + level_identifier_size + 2;
1297
1298   return chunk_size;
1299 }
1300
1301 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1302 {
1303   int i, j;
1304   int chunk_size_expected =
1305     (tape->num_participating_players + 1) * tape->length;
1306
1307   if (chunk_size_expected != chunk_size)
1308   {
1309     ReadUnusedBytesFromFile(file, chunk_size);
1310     return chunk_size_expected;
1311   }
1312
1313   for(i=0; i<tape->length; i++)
1314   {
1315     if (i >= MAX_TAPELEN)
1316       break;
1317
1318     for(j=0; j<MAX_PLAYERS; j++)
1319     {
1320       tape->pos[i].action[j] = MV_NO_MOVING;
1321
1322       if (tape->player_participates[j])
1323         tape->pos[i].action[j] = getFile8Bit(file);
1324     }
1325
1326     tape->pos[i].delay = getFile8Bit(file);
1327
1328     if (tape->file_version == FILE_VERSION_1_0)
1329     {
1330       /* eliminate possible diagonal moves in old tapes */
1331       /* this is only for backward compatibility */
1332
1333       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1334       byte action = tape->pos[i].action[0];
1335       int k, num_moves = 0;
1336
1337       for (k=0; k<4; k++)
1338       {
1339         if (action & joy_dir[k])
1340         {
1341           tape->pos[i + num_moves].action[0] = joy_dir[k];
1342           if (num_moves > 0)
1343             tape->pos[i + num_moves].delay = 0;
1344           num_moves++;
1345         }
1346       }
1347
1348       if (num_moves > 1)
1349       {
1350         num_moves--;
1351         i += num_moves;
1352         tape->length += num_moves;
1353       }
1354     }
1355     else if (tape->file_version < FILE_VERSION_2_0)
1356     {
1357       /* convert pre-2.0 tapes to new tape format */
1358
1359       if (tape->pos[i].delay > 1)
1360       {
1361         /* action part */
1362         tape->pos[i + 1] = tape->pos[i];
1363         tape->pos[i + 1].delay = 1;
1364
1365         /* delay part */
1366         for(j=0; j<MAX_PLAYERS; j++)
1367           tape->pos[i].action[j] = MV_NO_MOVING;
1368         tape->pos[i].delay--;
1369
1370         i++;
1371         tape->length++;
1372       }
1373     }
1374
1375     if (feof(file))
1376       break;
1377   }
1378
1379   if (i != tape->length)
1380     chunk_size = (tape->num_participating_players + 1) * i;
1381
1382   return chunk_size;
1383 }
1384
1385 void LoadTapeFromFilename(char *filename)
1386 {
1387   char cookie[MAX_LINE_LEN];
1388   char chunk_name[CHUNK_ID_LEN + 1];
1389   FILE *file;
1390   int chunk_size;
1391
1392   /* always start with reliable default values */
1393   setTapeInfoToDefaults();
1394
1395   if (!(file = fopen(filename, MODE_READ)))
1396     return;
1397
1398   getFileChunkBE(file, chunk_name, NULL);
1399   if (strcmp(chunk_name, "RND1") == 0)
1400   {
1401     getFile32BitBE(file);               /* not used */
1402
1403     getFileChunkBE(file, chunk_name, NULL);
1404     if (strcmp(chunk_name, "TAPE") != 0)
1405     {
1406       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1407       fclose(file);
1408       return;
1409     }
1410   }
1411   else  /* check for pre-2.0 file format with cookie string */
1412   {
1413     strcpy(cookie, chunk_name);
1414     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1415     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1416       cookie[strlen(cookie) - 1] = '\0';
1417
1418     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1419     {
1420       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1421       fclose(file);
1422       return;
1423     }
1424
1425     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1426     {
1427       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1428       fclose(file);
1429       return;
1430     }
1431
1432     /* pre-2.0 tape files have no game version, so use file version here */
1433     tape.game_version = tape.file_version;
1434   }
1435
1436   if (tape.file_version < FILE_VERSION_1_2)
1437   {
1438     /* tape files from versions before 1.2.0 without chunk structure */
1439     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1440     LoadTape_BODY(file, 2 * tape.length,  &tape);
1441   }
1442   else
1443   {
1444     static struct
1445     {
1446       char *name;
1447       int size;
1448       int (*loader)(FILE *, int, struct TapeInfo *);
1449     }
1450     chunk_info[] =
1451     {
1452       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1453       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1454       { "INFO", -1,                     LoadTape_INFO },
1455       { "BODY", -1,                     LoadTape_BODY },
1456       {  NULL,  0,                      NULL }
1457     };
1458
1459     while (getFileChunkBE(file, chunk_name, &chunk_size))
1460     {
1461       int i = 0;
1462
1463       while (chunk_info[i].name != NULL &&
1464              strcmp(chunk_name, chunk_info[i].name) != 0)
1465         i++;
1466
1467       if (chunk_info[i].name == NULL)
1468       {
1469         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1470               chunk_name, filename);
1471         ReadUnusedBytesFromFile(file, chunk_size);
1472       }
1473       else if (chunk_info[i].size != -1 &&
1474                chunk_info[i].size != chunk_size)
1475       {
1476         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1477               chunk_size, chunk_name, filename);
1478         ReadUnusedBytesFromFile(file, chunk_size);
1479       }
1480       else
1481       {
1482         /* call function to load this tape chunk */
1483         int chunk_size_expected =
1484           (chunk_info[i].loader)(file, chunk_size, &tape);
1485
1486         /* the size of some chunks cannot be checked before reading other
1487            chunks first (like "HEAD" and "BODY") that contain some header
1488            information, so check them here */
1489         if (chunk_size_expected != chunk_size)
1490         {
1491           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1492                 chunk_size, chunk_name, filename);
1493         }
1494       }
1495     }
1496   }
1497
1498   fclose(file);
1499
1500   tape.length_seconds = GetTapeLength();
1501
1502 #if 0
1503   printf("tape version: %d\n", tape.game_version);
1504 #endif
1505 }
1506
1507 void LoadTape(int level_nr)
1508 {
1509   char *filename = getTapeFilename(level_nr);
1510
1511   LoadTapeFromFilename(filename);
1512 }
1513
1514 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1515 {
1516   putFileVersion(file, tape->file_version);
1517   putFileVersion(file, tape->game_version);
1518 }
1519
1520 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1521 {
1522   int i;
1523   byte store_participating_players = 0;
1524
1525   /* set bits for participating players for compact storage */
1526   for(i=0; i<MAX_PLAYERS; i++)
1527     if (tape->player_participates[i])
1528       store_participating_players |= (1 << i);
1529
1530   putFile32BitBE(file, tape->random_seed);
1531   putFile32BitBE(file, tape->date);
1532   putFile32BitBE(file, tape->length);
1533
1534   putFile8Bit(file, store_participating_players);
1535
1536   /* unused bytes not at the end here for 4-byte alignment of engine_version */
1537   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1538
1539   putFileVersion(file, tape->engine_version);
1540 }
1541
1542 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1543 {
1544   int level_identifier_size = strlen(tape->level_identifier) + 1;
1545   int i;
1546
1547   putFile16BitBE(file, level_identifier_size);
1548
1549   for(i=0; i < level_identifier_size; i++)
1550     putFile8Bit(file, tape->level_identifier[i]);
1551
1552   putFile16BitBE(file, tape->level_nr);
1553 }
1554
1555 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1556 {
1557   int i, j;
1558
1559   for(i=0; i<tape->length; i++)
1560   {
1561     for(j=0; j<MAX_PLAYERS; j++)
1562       if (tape->player_participates[j])
1563         putFile8Bit(file, tape->pos[i].action[j]);
1564
1565     putFile8Bit(file, tape->pos[i].delay);
1566   }
1567 }
1568
1569 void SaveTape(int level_nr)
1570 {
1571   char *filename = getTapeFilename(level_nr);
1572   FILE *file;
1573   boolean new_tape = TRUE;
1574   int num_participating_players = 0;
1575   int info_chunk_size;
1576   int body_chunk_size;
1577   int i;
1578
1579   InitTapeDirectory(leveldir_current->filename);
1580
1581   /* if a tape still exists, ask to overwrite it */
1582   if (access(filename, F_OK) == 0)
1583   {
1584     new_tape = FALSE;
1585     if (!Request("Replace old tape ?", REQ_ASK))
1586       return;
1587   }
1588
1589   if (!(file = fopen(filename, MODE_WRITE)))
1590   {
1591     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1592     return;
1593   }
1594
1595   tape.file_version = FILE_VERSION_ACTUAL;
1596   tape.game_version = GAME_VERSION_ACTUAL;
1597
1598   /* count number of participating players  */
1599   for(i=0; i<MAX_PLAYERS; i++)
1600     if (tape.player_participates[i])
1601       num_participating_players++;
1602
1603   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1604   body_chunk_size = (num_participating_players + 1) * tape.length;
1605
1606   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1607   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1608
1609   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1610   SaveTape_VERS(file, &tape);
1611
1612   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1613   SaveTape_HEAD(file, &tape);
1614
1615   putFileChunkBE(file, "INFO", info_chunk_size);
1616   SaveTape_INFO(file, &tape);
1617
1618   putFileChunkBE(file, "BODY", body_chunk_size);
1619   SaveTape_BODY(file, &tape);
1620
1621   fclose(file);
1622
1623   SetFilePermissions(filename, PERMS_PRIVATE);
1624
1625   tape.changed = FALSE;
1626
1627   if (new_tape)
1628     Request("tape saved !", REQ_CONFIRM);
1629 }
1630
1631 void DumpTape(struct TapeInfo *tape)
1632 {
1633   int i, j;
1634
1635   if (TAPE_IS_EMPTY(*tape))
1636   {
1637     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1638     return;
1639   }
1640
1641   printf_line("-", 79);
1642   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1643          tape->level_nr, tape->file_version, tape->game_version);
1644   printf("Level series identifier: '%s'\n", tape->level_identifier);
1645   printf_line("-", 79);
1646
1647   for(i=0; i<tape->length; i++)
1648   {
1649     if (i >= MAX_TAPELEN)
1650       break;
1651
1652     printf("%03d: ", i);
1653
1654     for(j=0; j<MAX_PLAYERS; j++)
1655     {
1656       if (tape->player_participates[j])
1657       {
1658         int action = tape->pos[i].action[j];
1659
1660         printf("%d:%02x ", j, action);
1661         printf("[%c%c%c%c|%c%c] - ",
1662                (action & JOY_LEFT ? '<' : ' '),
1663                (action & JOY_RIGHT ? '>' : ' '),
1664                (action & JOY_UP ? '^' : ' '),
1665                (action & JOY_DOWN ? 'v' : ' '),
1666                (action & JOY_BUTTON_1 ? '1' : ' '),
1667                (action & JOY_BUTTON_2 ? '2' : ' '));
1668       }
1669     }
1670
1671     printf("(%03d)\n", tape->pos[i].delay);
1672   }
1673
1674   printf_line("-", 79);
1675 }
1676
1677
1678 /* ========================================================================= */
1679 /* score file functions                                                      */
1680 /* ========================================================================= */
1681
1682 void LoadScore(int level_nr)
1683 {
1684   int i;
1685   char *filename = getScoreFilename(level_nr);
1686   char cookie[MAX_LINE_LEN];
1687   char line[MAX_LINE_LEN];
1688   char *line_ptr;
1689   FILE *file;
1690
1691   /* always start with reliable default values */
1692   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1693   {
1694     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1695     highscore[i].Score = 0;
1696   }
1697
1698   if (!(file = fopen(filename, MODE_READ)))
1699     return;
1700
1701   /* check file identifier */
1702   fgets(cookie, MAX_LINE_LEN, file);
1703   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1704     cookie[strlen(cookie) - 1] = '\0';
1705
1706   if (!checkCookieString(cookie, SCORE_COOKIE))
1707   {
1708     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1709     fclose(file);
1710     return;
1711   }
1712
1713   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1714   {
1715     fscanf(file, "%d", &highscore[i].Score);
1716     fgets(line, MAX_LINE_LEN, file);
1717
1718     if (line[strlen(line) - 1] == '\n')
1719       line[strlen(line) - 1] = '\0';
1720
1721     for (line_ptr = line; *line_ptr; line_ptr++)
1722     {
1723       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1724       {
1725         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1726         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1727         break;
1728       }
1729     }
1730   }
1731
1732   fclose(file);
1733 }
1734
1735 void SaveScore(int level_nr)
1736 {
1737   int i;
1738   char *filename = getScoreFilename(level_nr);
1739   FILE *file;
1740
1741   InitScoreDirectory(leveldir_current->filename);
1742
1743   if (!(file = fopen(filename, MODE_WRITE)))
1744   {
1745     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1746     return;
1747   }
1748
1749   fprintf(file, "%s\n\n", SCORE_COOKIE);
1750
1751   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1752     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1753
1754   fclose(file);
1755
1756   SetFilePermissions(filename, PERMS_PUBLIC);
1757 }
1758
1759
1760 /* ========================================================================= */
1761 /* setup file functions                                                      */
1762 /* ========================================================================= */
1763
1764 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1765
1766 /* global setup */
1767 #define SETUP_TOKEN_PLAYER_NAME                 0
1768 #define SETUP_TOKEN_SOUND                       1
1769 #define SETUP_TOKEN_SOUND_LOOPS                 2
1770 #define SETUP_TOKEN_SOUND_MUSIC                 3
1771 #define SETUP_TOKEN_SOUND_SIMPLE                4
1772 #define SETUP_TOKEN_TOONS                       5
1773 #define SETUP_TOKEN_SCROLL_DELAY                6
1774 #define SETUP_TOKEN_SOFT_SCROLLING              7
1775 #define SETUP_TOKEN_FADING                      8
1776 #define SETUP_TOKEN_AUTORECORD                  9
1777 #define SETUP_TOKEN_QUICK_DOORS                 10
1778 #define SETUP_TOKEN_TEAM_MODE                   11
1779 #define SETUP_TOKEN_HANDICAP                    12
1780 #define SETUP_TOKEN_TIME_LIMIT                  13
1781 #define SETUP_TOKEN_FULLSCREEN                  14
1782 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1783 #define SETUP_TOKEN_GRAPHICS_SET                16
1784 #define SETUP_TOKEN_SOUNDS_SET                  17
1785 #define SETUP_TOKEN_MUSIC_SET                   18
1786 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1787 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1788 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1789
1790 #define NUM_GLOBAL_SETUP_TOKENS                 22
1791
1792 /* editor setup */
1793 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
1794 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
1795 #define SETUP_TOKEN_EDITOR_EL_MORE              2
1796 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
1797 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
1798 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
1799 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
1800 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
1801 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
1802
1803 #define NUM_EDITOR_SETUP_TOKENS                 9
1804
1805 /* shortcut setup */
1806 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
1807 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
1808 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
1809
1810 #define NUM_SHORTCUT_SETUP_TOKENS               3
1811
1812 /* player setup */
1813 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
1814 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
1815 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
1816 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
1817 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
1818 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
1819 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
1820 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
1821 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
1822 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
1823 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
1824 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
1825 #define SETUP_TOKEN_PLAYER_KEY_UP               12
1826 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
1827 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
1828 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
1829
1830 #define NUM_PLAYER_SETUP_TOKENS                 16
1831
1832 /* system setup */
1833 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
1834 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
1835
1836 #define NUM_SYSTEM_SETUP_TOKENS                 2
1837
1838 /* options setup */
1839 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
1840
1841 #define NUM_OPTIONS_SETUP_TOKENS                1
1842
1843
1844 static struct SetupInfo si;
1845 static struct SetupEditorInfo sei;
1846 static struct SetupShortcutInfo ssi;
1847 static struct SetupInputInfo sii;
1848 static struct SetupSystemInfo syi;
1849 static struct OptionInfo soi;
1850
1851 static struct TokenInfo global_setup_tokens[] =
1852 {
1853   { TYPE_STRING, &si.player_name,       "player_name"                   },
1854   { TYPE_SWITCH, &si.sound,             "sound"                         },
1855   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1856   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1857   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1858   { TYPE_SWITCH, &si.toons,             "toons"                         },
1859   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1860   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1861   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1862   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1863   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1864   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1865   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1866   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1867   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1868   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1869   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1870   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1871   { TYPE_STRING, &si.music_set,         "music_set"                     },
1872   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1873   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1874   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1875 };
1876
1877 static struct TokenInfo editor_setup_tokens[] =
1878 {
1879   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1880   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1881   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1882   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1883   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1884   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1885   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1886   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1887   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1888 };
1889
1890 static struct TokenInfo shortcut_setup_tokens[] =
1891 {
1892   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1893   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1894   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1895 };
1896
1897 static struct TokenInfo player_setup_tokens[] =
1898 {
1899   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1900   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1901   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1902   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1903   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1904   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1905   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1906   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1907   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1908   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1909   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1910   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1911   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1912   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1913   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1914   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1915 };
1916
1917 static struct TokenInfo system_setup_tokens[] =
1918 {
1919   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
1920   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1921 };
1922
1923 static struct TokenInfo options_setup_tokens[] =
1924 {
1925   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
1926 };
1927
1928 static char *get_corrected_login_name(char *login_name)
1929 {
1930   /* needed because player name must be a fixed length string */
1931   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1932
1933   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1934   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1935
1936   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
1937     if (strchr(login_name_new, ' '))
1938       *strchr(login_name_new, ' ') = '\0';
1939
1940   return login_name_new;
1941 }
1942
1943 static void setSetupInfoToDefaults(struct SetupInfo *si)
1944 {
1945   int i;
1946
1947   si->player_name = get_corrected_login_name(getLoginName());
1948
1949   si->sound = TRUE;
1950   si->sound_loops = TRUE;
1951   si->sound_music = TRUE;
1952   si->sound_simple = TRUE;
1953   si->toons = TRUE;
1954   si->double_buffering = TRUE;
1955   si->direct_draw = !si->double_buffering;
1956   si->scroll_delay = TRUE;
1957   si->soft_scrolling = TRUE;
1958   si->fading = FALSE;
1959   si->autorecord = TRUE;
1960   si->quick_doors = FALSE;
1961   si->team_mode = FALSE;
1962   si->handicap = TRUE;
1963   si->time_limit = TRUE;
1964   si->fullscreen = FALSE;
1965   si->ask_on_escape = TRUE;
1966
1967   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1968   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1969   si->music_set = getStringCopy(MUSIC_SUBDIR);
1970   si->override_level_graphics = FALSE;
1971   si->override_level_sounds = FALSE;
1972   si->override_level_music = FALSE;
1973
1974   si->editor.el_boulderdash = TRUE;
1975   si->editor.el_emerald_mine = TRUE;
1976   si->editor.el_more = TRUE;
1977   si->editor.el_sokoban = TRUE;
1978   si->editor.el_supaplex = TRUE;
1979   si->editor.el_diamond_caves = TRUE;
1980   si->editor.el_dx_boulderdash = TRUE;
1981   si->editor.el_chars = TRUE;
1982   si->editor.el_custom = TRUE;
1983
1984   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1985   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1986   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1987
1988   for (i=0; i<MAX_PLAYERS; i++)
1989   {
1990     si->input[i].use_joystick = FALSE;
1991     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1992     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1993     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1994     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1995     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1996     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1997     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1998     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1999     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2000     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2001     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2002     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2003     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2004     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2005     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2006   }
2007
2008   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2009   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2010
2011   si->options.verbose = FALSE;
2012 }
2013
2014 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2015 {
2016   int i, pnr;
2017
2018   if (!setup_file_hash)
2019     return;
2020
2021   /* global setup */
2022   si = setup;
2023   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2024     setSetupInfo(global_setup_tokens, i,
2025                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2026   setup = si;
2027
2028   /* editor setup */
2029   sei = setup.editor;
2030   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2031     setSetupInfo(editor_setup_tokens, i,
2032                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2033   setup.editor = sei;
2034
2035   /* shortcut setup */
2036   ssi = setup.shortcut;
2037   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2038     setSetupInfo(shortcut_setup_tokens, i,
2039                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2040   setup.shortcut = ssi;
2041
2042   /* player setup */
2043   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2044   {
2045     char prefix[30];
2046
2047     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2048
2049     sii = setup.input[pnr];
2050     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2051     {
2052       char full_token[100];
2053
2054       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2055       setSetupInfo(player_setup_tokens, i,
2056                    getHashEntry(setup_file_hash, full_token));
2057     }
2058     setup.input[pnr] = sii;
2059   }
2060
2061   /* system setup */
2062   syi = setup.system;
2063   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2064     setSetupInfo(system_setup_tokens, i,
2065                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2066   setup.system = syi;
2067
2068   /* options setup */
2069   soi = setup.options;
2070   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2071     setSetupInfo(options_setup_tokens, i,
2072                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2073   setup.options = soi;
2074 }
2075
2076 void LoadSetup()
2077 {
2078   char *filename = getSetupFilename();
2079   SetupFileHash *setup_file_hash = NULL;
2080
2081   /* always start with reliable default values */
2082   setSetupInfoToDefaults(&setup);
2083
2084   setup_file_hash = loadSetupFileHash(filename);
2085
2086   if (setup_file_hash)
2087   {
2088     char *player_name_new;
2089
2090     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2091     decodeSetupFileHash(setup_file_hash);
2092
2093     setup.direct_draw = !setup.double_buffering;
2094
2095     freeSetupFileHash(setup_file_hash);
2096
2097     /* needed to work around problems with fixed length strings */
2098     player_name_new = get_corrected_login_name(setup.player_name);
2099     free(setup.player_name);
2100     setup.player_name = player_name_new;
2101   }
2102   else
2103     Error(ERR_WARN, "using default setup values");
2104 }
2105
2106 void SaveSetup()
2107 {
2108   char *filename = getSetupFilename();
2109   FILE *file;
2110   int i, pnr;
2111
2112   InitUserDataDirectory();
2113
2114   if (!(file = fopen(filename, MODE_WRITE)))
2115   {
2116     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2117     return;
2118   }
2119
2120   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2121                                                getCookie("SETUP")));
2122   fprintf(file, "\n");
2123
2124   /* global setup */
2125   si = setup;
2126   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2127   {
2128     /* just to make things nicer :) */
2129     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2130         i == SETUP_TOKEN_GRAPHICS_SET)
2131       fprintf(file, "\n");
2132
2133     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2134   }
2135
2136   /* editor setup */
2137   sei = setup.editor;
2138   fprintf(file, "\n");
2139   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2140     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2141
2142   /* shortcut setup */
2143   ssi = setup.shortcut;
2144   fprintf(file, "\n");
2145   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2146     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2147
2148   /* player setup */
2149   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2150   {
2151     char prefix[30];
2152
2153     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2154     fprintf(file, "\n");
2155
2156     sii = setup.input[pnr];
2157     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2158       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2159   }
2160
2161   /* system setup */
2162   syi = setup.system;
2163   fprintf(file, "\n");
2164   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2165     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2166
2167   /* options setup */
2168   soi = setup.options;
2169   fprintf(file, "\n");
2170   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2171     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2172
2173   fclose(file);
2174
2175   SetFilePermissions(filename, PERMS_PRIVATE);
2176 }
2177
2178 void LoadCustomElementDescriptions()
2179 {
2180   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2181   SetupFileHash *setup_file_hash;
2182   int i;
2183
2184   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2185   {
2186     if (element_info[i].custom_description != NULL)
2187     {
2188       free(element_info[i].custom_description);
2189       element_info[i].custom_description = NULL;
2190     }
2191   }
2192
2193   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2194     return;
2195
2196   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2197   {
2198     char *token = getStringCat2(element_info[i].token_name, ".name");
2199     char *value = getHashEntry(setup_file_hash, token);
2200
2201     if (value != NULL)
2202       element_info[i].custom_description = getStringCopy(value);
2203
2204     free(token);
2205   }
2206
2207   freeSetupFileHash(setup_file_hash);
2208 }
2209
2210 void LoadSpecialMenuDesignSettings()
2211 {
2212   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2213   SetupFileHash *setup_file_hash;
2214   int i, j;
2215
2216   /* always start with reliable default values from default config */
2217   for (i=0; image_config_vars[i].token != NULL; i++)
2218     for (j=0; image_config[j].token != NULL; j++)
2219       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2220         *image_config_vars[i].value =
2221           get_integer_from_string(image_config[j].value);
2222
2223   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2224     return;
2225
2226   /* special case: initialize with default values that may be overwritten */
2227   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2228   {
2229     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2230     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2231     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2232
2233     if (value_x != NULL)
2234       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2235     if (value_y != NULL)
2236       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2237     if (list_size != NULL)
2238       menu.list_size[i] = get_integer_from_string(list_size);
2239   }
2240
2241   /* read (and overwrite with) values that may be specified in config file */
2242   for (i=0; image_config_vars[i].token != NULL; i++)
2243   {
2244     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2245
2246     if (value != NULL)
2247       *image_config_vars[i].value = get_integer_from_string(value);
2248   }
2249
2250   freeSetupFileHash(setup_file_hash);
2251 }