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