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