rnd-20030924-B-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_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
34 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
35 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
36 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
37 #define LEVEL_CPART_CUS4_SIZE   ???     /* size of CUS4 chunk part    */
38 #define LEVEL_CPART_CUS4_UNUSED ???     /* unused CUS4 bytes / part   */
39 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
40 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
41
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
44
45 /* file identifier strings */
46 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
47 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
48 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
49
50
51 /* ========================================================================= */
52 /* level file functions                                                      */
53 /* ========================================================================= */
54
55 void setElementChangePages(struct ElementInfo *ei, int change_pages)
56 {
57   int change_page_size = sizeof(struct ElementChangeInfo);
58
59   ei->num_change_pages = MAX(1, change_pages);
60
61   ei->change_page =
62     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
63
64   if (ei->current_change_page >= ei->num_change_pages)
65     ei->current_change_page = ei->num_change_pages - 1;
66
67   ei->change = &ei->change_page[ei->current_change_page];
68 }
69
70 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
71 {
72   int x, y;
73
74   change->can_change = FALSE;
75
76   change->events = CE_BITMASK_DEFAULT;
77   change->sides = CH_SIDE_ANY;
78
79   change->target_element = EL_EMPTY_SPACE;
80
81   change->delay_fixed = 0;
82   change->delay_random = 0;
83   change->delay_frames = -1;    /* later set to reliable default value */
84
85   change->trigger_element = EL_EMPTY_SPACE;
86
87   change->explode = FALSE;
88   change->use_content = FALSE;
89   change->only_complete = FALSE;
90   change->use_random_change = FALSE;
91   change->random = 0;
92   change->power = CP_NON_DESTRUCTIVE;
93
94   for(x=0; x<3; x++)
95     for(y=0; y<3; y++)
96       change->content[x][y] = EL_EMPTY_SPACE;
97
98   change->direct_action = 0;
99   change->other_action = 0;
100
101   change->pre_change_function = NULL;
102   change->change_function = NULL;
103   change->post_change_function = NULL;
104 }
105
106 static void setLevelInfoToDefaults(struct LevelInfo *level)
107 {
108   int i, j, x, y;
109
110   level->file_version = FILE_VERSION_ACTUAL;
111   level->game_version = GAME_VERSION_ACTUAL;
112
113   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
114   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
115   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
116
117   level->fieldx = STD_LEV_FIELDX;
118   level->fieldy = STD_LEV_FIELDY;
119
120   for(x=0; x<MAX_LEV_FIELDX; x++)
121     for(y=0; y<MAX_LEV_FIELDY; y++)
122       level->field[x][y] = EL_SAND;
123
124   level->time = 100;
125   level->gems_needed = 0;
126   level->amoeba_speed = 10;
127   level->time_magic_wall = 10;
128   level->time_wheel = 10;
129   level->time_light = 10;
130   level->time_timegate = 10;
131   level->amoeba_content = EL_DIAMOND;
132   level->double_speed = FALSE;
133   level->gravity = FALSE;
134   level->em_slippery_gems = FALSE;
135
136   level->use_custom_template = FALSE;
137
138   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
139     level->name[i] = '\0';
140   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
141     level->author[i] = '\0';
142
143   strcpy(level->name, NAMELESS_LEVEL_NAME);
144   strcpy(level->author, ANONYMOUS_NAME);
145
146   for (i=0; i<4; i++)
147   {
148     level->envelope_text[i][0] = '\0';
149     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
150     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
151   }
152
153   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
154     level->score[i] = 10;
155
156   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
157   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
158     for(x=0; x<3; x++)
159       for(y=0; y<3; y++)
160         level->yamyam_content[i][x][y] =
161           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
162
163   level->field[0][0] = EL_PLAYER_1;
164   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
165
166   for (i=0; i < MAX_NUM_ELEMENTS; i++)
167   {
168     setElementChangePages(&element_info[i], 1);
169     setElementChangeInfoToDefaults(element_info[i].change);
170   }
171
172   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
173   {
174     int element = EL_CUSTOM_START + i;
175
176     for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
177       element_info[element].description[j] = '\0';
178     if (element_info[element].custom_description != NULL)
179       strncpy(element_info[element].description,
180               element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
181     else
182       strcpy(element_info[element].description,
183              element_info[element].editor_description);
184
185     element_info[element].use_gfx_element = FALSE;
186     element_info[element].gfx_element = EL_EMPTY_SPACE;
187
188     element_info[element].collect_score = 10;           /* special default */
189     element_info[element].collect_count = 1;            /* special default */
190
191     element_info[element].push_delay_fixed = 2;         /* special default */
192     element_info[element].push_delay_random = 8;        /* special default */
193     element_info[element].move_delay_fixed = 0;
194     element_info[element].move_delay_random = 0;
195
196     element_info[element].move_pattern = MV_ALL_DIRECTIONS;
197     element_info[element].move_direction_initial = MV_NO_MOVING;
198     element_info[element].move_stepsize = TILEX / 8;
199
200     element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
201
202     for(x=0; x<3; x++)
203       for(y=0; y<3; y++)
204         element_info[element].content[x][y] = EL_EMPTY_SPACE;
205
206     element_info[element].access_type = 0;
207     element_info[element].access_layer = 0;
208     element_info[element].walk_to_action = 0;
209     element_info[element].smash_targets = 0;
210     element_info[element].deadliness = 0;
211     element_info[element].consistency = 0;
212
213     element_info[element].can_explode_by_fire = FALSE;
214     element_info[element].can_explode_smashed = FALSE;
215     element_info[element].can_explode_impact = FALSE;
216
217     element_info[element].current_change_page = 0;
218
219     /* start with no properties at all */
220     for (j=0; j < NUM_EP_BITFIELDS; j++)
221       Properties[element][j] = EP_BITMASK_DEFAULT;
222
223     element_info[element].modified_settings = FALSE;
224   }
225
226   BorderElement = EL_STEELWALL;
227
228   level->no_level_file = FALSE;
229
230   if (leveldir_current == NULL)         /* only when dumping level */
231     return;
232
233   /* try to determine better author name than 'anonymous' */
234   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
235   {
236     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
237     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
238   }
239   else
240   {
241     switch (LEVELCLASS(leveldir_current))
242     {
243       case LEVELCLASS_TUTORIAL:
244         strcpy(level->author, PROGRAM_AUTHOR_STRING);
245         break;
246
247       case LEVELCLASS_CONTRIBUTION:
248         strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
249         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
250         break;
251
252       case LEVELCLASS_USER:
253         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
254         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
255         break;
256
257       default:
258         /* keep default value */
259         break;
260     }
261   }
262 }
263
264 static void ActivateLevelTemplate()
265 {
266   /* Currently there is no special action needed to activate the template
267      data, because 'element_info' and 'Properties' overwrite the original
268      level data, while all other variables do not change. */
269 }
270
271 boolean LevelFileExists(int level_nr)
272 {
273   char *filename = getLevelFilename(level_nr);
274
275   return (access(filename, F_OK) == 0);
276 }
277
278 static int checkLevelElement(int element)
279 {
280   /* map some (historic, now obsolete) elements */
281
282 #if 1
283   switch (element)
284   {
285     case EL_PLAYER_OBSOLETE:
286       element = EL_PLAYER_1;
287       break;
288
289     case EL_KEY_OBSOLETE:
290       element = EL_KEY_1;
291
292     case EL_EM_KEY_1_FILE_OBSOLETE:
293       element = EL_EM_KEY_1;
294       break;
295
296     case EL_EM_KEY_2_FILE_OBSOLETE:
297       element = EL_EM_KEY_2;
298       break;
299
300     case EL_EM_KEY_3_FILE_OBSOLETE:
301       element = EL_EM_KEY_3;
302       break;
303
304     case EL_EM_KEY_4_FILE_OBSOLETE:
305       element = EL_EM_KEY_4;
306       break;
307
308     case EL_ENVELOPE_OBSOLETE:
309       element = EL_ENVELOPE_1;
310       break;
311
312     case EL_SP_EMPTY:
313       element = EL_EMPTY;
314       break;
315
316     default:
317       if (element >= NUM_FILE_ELEMENTS)
318       {
319         Error(ERR_WARN, "invalid level element %d", element);
320
321         element = EL_CHAR_QUESTION;
322       }
323       break;
324   }
325 #else
326   if (element >= NUM_FILE_ELEMENTS)
327   {
328     Error(ERR_WARN, "invalid level element %d", element);
329
330     element = EL_CHAR_QUESTION;
331   }
332   else if (element == EL_PLAYER_OBSOLETE)
333     element = EL_PLAYER_1;
334   else if (element == EL_KEY_OBSOLETE)
335     element = EL_KEY_1;
336 #endif
337
338   return element;
339 }
340
341 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
342 {
343   level->file_version = getFileVersion(file);
344   level->game_version = getFileVersion(file);
345
346   return chunk_size;
347 }
348
349 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
350 {
351   int i, x, y;
352
353   level->fieldx = getFile8Bit(file);
354   level->fieldy = getFile8Bit(file);
355
356   level->time           = getFile16BitBE(file);
357   level->gems_needed    = getFile16BitBE(file);
358
359   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
360     level->name[i] = getFile8Bit(file);
361   level->name[MAX_LEVEL_NAME_LEN] = 0;
362
363   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
364     level->score[i] = getFile8Bit(file);
365
366   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
367   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
368     for(y=0; y<3; y++)
369       for(x=0; x<3; x++)
370         level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
371
372   level->amoeba_speed           = getFile8Bit(file);
373   level->time_magic_wall        = getFile8Bit(file);
374   level->time_wheel             = getFile8Bit(file);
375   level->amoeba_content         = checkLevelElement(getFile8Bit(file));
376   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
377   level->gravity                = (getFile8Bit(file) == 1 ? TRUE : FALSE);
378   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
379   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
380
381   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
382
383   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
384
385   return chunk_size;
386 }
387
388 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
389 {
390   int i;
391
392   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
393     level->author[i] = getFile8Bit(file);
394   level->author[MAX_LEVEL_NAME_LEN] = 0;
395
396   return chunk_size;
397 }
398
399 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
400 {
401   int x, y;
402   int chunk_size_expected = level->fieldx * level->fieldy;
403
404   /* Note: "chunk_size" was wrong before version 2.0 when elements are
405      stored with 16-bit encoding (and should be twice as big then).
406      Even worse, playfield data was stored 16-bit when only yamyam content
407      contained 16-bit elements and vice versa. */
408
409   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
410     chunk_size_expected *= 2;
411
412   if (chunk_size_expected != chunk_size)
413   {
414     ReadUnusedBytesFromFile(file, chunk_size);
415     return chunk_size_expected;
416   }
417
418   for(y=0; y<level->fieldy; y++)
419     for(x=0; x<level->fieldx; x++)
420       level->field[x][y] =
421         checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
422                           getFile8Bit(file));
423   return chunk_size;
424 }
425
426 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
427 {
428   int i, x, y;
429   int header_size = 4;
430   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
431   int chunk_size_expected = header_size + content_size;
432
433   /* Note: "chunk_size" was wrong before version 2.0 when elements are
434      stored with 16-bit encoding (and should be twice as big then).
435      Even worse, playfield data was stored 16-bit when only yamyam content
436      contained 16-bit elements and vice versa. */
437
438   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
439     chunk_size_expected += content_size;
440
441   if (chunk_size_expected != chunk_size)
442   {
443     ReadUnusedBytesFromFile(file, chunk_size);
444     return chunk_size_expected;
445   }
446
447   getFile8Bit(file);
448   level->num_yamyam_contents = getFile8Bit(file);
449   getFile8Bit(file);
450   getFile8Bit(file);
451
452   /* correct invalid number of content fields -- should never happen */
453   if (level->num_yamyam_contents < 1 ||
454       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
455     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
456
457   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
458     for(y=0; y<3; y++)
459       for(x=0; x<3; x++)
460         level->yamyam_content[i][x][y] =
461           checkLevelElement(level->encoding_16bit_field ?
462                             getFile16BitBE(file) : getFile8Bit(file));
463   return chunk_size;
464 }
465
466 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
467 {
468   int i, x, y;
469   int element;
470   int num_contents, content_xsize, content_ysize;
471   int content_array[MAX_ELEMENT_CONTENTS][3][3];
472
473   element = checkLevelElement(getFile16BitBE(file));
474   num_contents = getFile8Bit(file);
475   content_xsize = getFile8Bit(file);
476   content_ysize = getFile8Bit(file);
477
478   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
479
480   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
481     for(y=0; y<3; y++)
482       for(x=0; x<3; x++)
483         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
484
485   /* correct invalid number of content fields -- should never happen */
486   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
487     num_contents = STD_ELEMENT_CONTENTS;
488
489   if (element == EL_YAMYAM)
490   {
491     level->num_yamyam_contents = num_contents;
492
493     for(i=0; i<num_contents; i++)
494       for(y=0; y<3; y++)
495         for(x=0; x<3; x++)
496           level->yamyam_content[i][x][y] = content_array[i][x][y];
497   }
498   else if (element == EL_BD_AMOEBA)
499   {
500     level->amoeba_content = content_array[0][0][0];
501   }
502   else
503   {
504     Error(ERR_WARN, "cannot load content for element '%d'", element);
505   }
506
507   return chunk_size;
508 }
509
510 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
511 {
512   int i;
513   int element;
514   int envelope_nr;
515   int envelope_len;
516   int chunk_size_expected;
517
518   element = checkLevelElement(getFile16BitBE(file));
519   if (!IS_ENVELOPE(element))
520     element = EL_ENVELOPE_1;
521
522   envelope_nr = element - EL_ENVELOPE_1;
523
524   envelope_len = getFile16BitBE(file);
525
526   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
527   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
528
529   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
530
531   chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
532
533   if (chunk_size_expected != chunk_size)
534   {
535     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
536     return chunk_size_expected;
537   }
538
539   for(i=0; i < envelope_len; i++)
540     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
541
542   return chunk_size;
543 }
544
545 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
546 {
547   int num_changed_custom_elements = getFile16BitBE(file);
548   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
549   int i;
550
551   if (chunk_size_expected != chunk_size)
552   {
553     ReadUnusedBytesFromFile(file, chunk_size - 2);
554     return chunk_size_expected;
555   }
556
557   for (i=0; i < num_changed_custom_elements; i++)
558   {
559     int element = getFile16BitBE(file);
560     int properties = getFile32BitBE(file);
561
562     if (IS_CUSTOM_ELEMENT(element))
563       Properties[element][EP_BITFIELD_BASE] = properties;
564     else
565       Error(ERR_WARN, "invalid custom element number %d", element);
566   }
567
568   return chunk_size;
569 }
570
571 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
572 {
573   int num_changed_custom_elements = getFile16BitBE(file);
574   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
575   int i;
576
577   if (chunk_size_expected != chunk_size)
578   {
579     ReadUnusedBytesFromFile(file, chunk_size - 2);
580     return chunk_size_expected;
581   }
582
583   for (i=0; i < num_changed_custom_elements; i++)
584   {
585     int element = getFile16BitBE(file);
586     int custom_target_element = getFile16BitBE(file);
587
588     if (IS_CUSTOM_ELEMENT(element))
589       element_info[element].change->target_element = custom_target_element;
590     else
591       Error(ERR_WARN, "invalid custom element number %d", element);
592   }
593
594   return chunk_size;
595 }
596
597 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
598 {
599   int num_changed_custom_elements = getFile16BitBE(file);
600   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
601   int i, j, x, y;
602
603   if (chunk_size_expected != chunk_size)
604   {
605     ReadUnusedBytesFromFile(file, chunk_size - 2);
606     return chunk_size_expected;
607   }
608
609   for (i=0; i < num_changed_custom_elements; i++)
610   {
611     int element = getFile16BitBE(file);
612
613     if (!IS_CUSTOM_ELEMENT(element))
614     {
615       Error(ERR_WARN, "invalid custom element number %d", element);
616
617       element = EL_DEFAULT;     /* dummy element used for artwork config */
618     }
619
620     for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
621       element_info[element].description[j] = getFile8Bit(file);
622     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
623
624     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
625
626     /* some free bytes for future properties and padding */
627     ReadUnusedBytesFromFile(file, 7);
628
629     element_info[element].use_gfx_element = getFile8Bit(file);
630     element_info[element].gfx_element =
631       checkLevelElement(getFile16BitBE(file));
632
633     element_info[element].collect_score = getFile8Bit(file);
634     element_info[element].collect_count = getFile8Bit(file);
635
636     element_info[element].push_delay_fixed = getFile16BitBE(file);
637     element_info[element].push_delay_random = getFile16BitBE(file);
638     element_info[element].move_delay_fixed = getFile16BitBE(file);
639     element_info[element].move_delay_random = getFile16BitBE(file);
640
641     element_info[element].move_pattern = getFile16BitBE(file);
642     element_info[element].move_direction_initial = getFile8Bit(file);
643     element_info[element].move_stepsize = getFile8Bit(file);
644
645     for(y=0; y<3; y++)
646       for(x=0; x<3; x++)
647         element_info[element].content[x][y] =
648           checkLevelElement(getFile16BitBE(file));
649
650     element_info[element].change->events = getFile32BitBE(file);
651
652     element_info[element].change->target_element =
653       checkLevelElement(getFile16BitBE(file));
654
655     element_info[element].change->delay_fixed = getFile16BitBE(file);
656     element_info[element].change->delay_random = getFile16BitBE(file);
657     element_info[element].change->delay_frames = getFile16BitBE(file);
658
659     element_info[element].change->trigger_element =
660       checkLevelElement(getFile16BitBE(file));
661
662     element_info[element].change->explode = getFile8Bit(file);
663     element_info[element].change->use_content = getFile8Bit(file);
664     element_info[element].change->only_complete = getFile8Bit(file);
665     element_info[element].change->use_random_change = getFile8Bit(file);
666
667     element_info[element].change->random = getFile8Bit(file);
668     element_info[element].change->power = getFile8Bit(file);
669
670     for(y=0; y<3; y++)
671       for(x=0; x<3; x++)
672         element_info[element].change->content[x][y] =
673           checkLevelElement(getFile16BitBE(file));
674
675     element_info[element].slippery_type = getFile8Bit(file);
676
677     /* some free bytes for future properties and padding */
678     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
679
680     /* mark that this custom element has been modified */
681     element_info[element].modified_settings = TRUE;
682   }
683
684   return chunk_size;
685 }
686
687 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
688 {
689   struct ElementInfo *ei;
690   int chunk_size_expected;
691   int element;
692   int i, x, y;
693
694   element = getFile16BitBE(file);
695
696   if (!IS_CUSTOM_ELEMENT(element))
697   {
698     Error(ERR_WARN, "invalid custom element number %d", element);
699
700     element = EL_DEFAULT;       /* dummy element used for artwork config */
701   }
702
703   ei = &element_info[element];
704
705   for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
706     ei->description[i] = getFile8Bit(file);
707   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
708
709   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
710   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
711
712   ei->num_change_pages = getFile8Bit(file);
713
714   /* some free bytes for future base property values and padding */
715   ReadUnusedBytesFromFile(file, 5);
716
717   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
718   if (chunk_size_expected != chunk_size)
719   {
720     ReadUnusedBytesFromFile(file, chunk_size - 48);
721     return chunk_size_expected;
722   }
723
724   /* read custom property values */
725
726   ei->use_gfx_element = getFile8Bit(file);
727   ei->gfx_element = checkLevelElement(getFile16BitBE(file));
728
729   ei->collect_score = getFile8Bit(file);
730   ei->collect_count = getFile8Bit(file);
731
732   ei->push_delay_fixed = getFile16BitBE(file);
733   ei->push_delay_random = getFile16BitBE(file);
734   ei->move_delay_fixed = getFile16BitBE(file);
735   ei->move_delay_random = getFile16BitBE(file);
736
737   ei->move_pattern = getFile16BitBE(file);
738   ei->move_direction_initial = getFile8Bit(file);
739   ei->move_stepsize = getFile8Bit(file);
740
741   ei->slippery_type = getFile8Bit(file);
742
743   for(y=0; y<3; y++)
744     for(x=0; x<3; x++)
745       ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
746
747   /* some free bytes for future custom property values and padding */
748   ReadUnusedBytesFromFile(file, 12);
749
750   /* read change property values */
751
752   setElementChangePages(ei, ei->num_change_pages);
753
754   for (i=0; i < ei->num_change_pages; i++)
755   {
756     struct ElementChangeInfo *change = &ei->change_page[i];
757
758     /* always start with reliable default values */
759     setElementChangeInfoToDefaults(change);
760
761     change->events = getFile32BitBE(file);
762
763     change->target_element = checkLevelElement(getFile16BitBE(file));
764
765     change->delay_fixed = getFile16BitBE(file);
766     change->delay_random = getFile16BitBE(file);
767     change->delay_frames = getFile16BitBE(file);
768
769     change->trigger_element = checkLevelElement(getFile16BitBE(file));
770
771     change->explode = getFile8Bit(file);
772     change->use_content = getFile8Bit(file);
773     change->only_complete = getFile8Bit(file);
774     change->use_random_change = getFile8Bit(file);
775
776     change->random = getFile8Bit(file);
777     change->power = getFile8Bit(file);
778
779     for(y=0; y<3; y++)
780       for(x=0; x<3; x++)
781         change->content[x][y] = checkLevelElement(getFile16BitBE(file));
782
783     change->can_change = getFile8Bit(file);
784
785     /* some free bytes for future change property values and padding */
786     ReadUnusedBytesFromFile(file, 9);
787   }
788
789   /* mark this custom element as modified */
790   ei->modified_settings = TRUE;
791
792   return chunk_size;
793 }
794
795 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
796 {
797   char cookie[MAX_LINE_LEN];
798   char chunk_name[CHUNK_ID_LEN + 1];
799   int chunk_size;
800   FILE *file;
801
802   /* always start with reliable default values */
803   setLevelInfoToDefaults(level);
804
805   if (!(file = fopen(filename, MODE_READ)))
806   {
807     level->no_level_file = TRUE;
808
809     if (level != &level_template)
810       Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
811
812     return;
813   }
814
815   getFileChunkBE(file, chunk_name, NULL);
816   if (strcmp(chunk_name, "RND1") == 0)
817   {
818     getFile32BitBE(file);               /* not used */
819
820     getFileChunkBE(file, chunk_name, NULL);
821     if (strcmp(chunk_name, "CAVE") != 0)
822     {
823       Error(ERR_WARN, "unknown format of level file '%s'", filename);
824       fclose(file);
825       return;
826     }
827   }
828   else  /* check for pre-2.0 file format with cookie string */
829   {
830     strcpy(cookie, chunk_name);
831     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
832     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
833       cookie[strlen(cookie) - 1] = '\0';
834
835     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
836     {
837       Error(ERR_WARN, "unknown format of level file '%s'", filename);
838       fclose(file);
839       return;
840     }
841
842     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
843     {
844       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
845       fclose(file);
846       return;
847     }
848
849     /* pre-2.0 level files have no game version, so use file version here */
850     level->game_version = level->file_version;
851   }
852
853   if (level->file_version < FILE_VERSION_1_2)
854   {
855     /* level files from versions before 1.2.0 without chunk structure */
856     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
857     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
858   }
859   else
860   {
861     static struct
862     {
863       char *name;
864       int size;
865       int (*loader)(FILE *, int, struct LevelInfo *);
866     }
867     chunk_info[] =
868     {
869       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
870       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
871       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
872       { "BODY", -1,                     LoadLevel_BODY },
873       { "CONT", -1,                     LoadLevel_CONT },
874       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
875       { "CNT3", -1,                     LoadLevel_CNT3 },
876       { "CUS1", -1,                     LoadLevel_CUS1 },
877       { "CUS2", -1,                     LoadLevel_CUS2 },
878       { "CUS3", -1,                     LoadLevel_CUS3 },
879       { "CUS4", -1,                     LoadLevel_CUS4 },
880       {  NULL,  0,                      NULL }
881     };
882
883     while (getFileChunkBE(file, chunk_name, &chunk_size))
884     {
885       int i = 0;
886
887       while (chunk_info[i].name != NULL &&
888              strcmp(chunk_name, chunk_info[i].name) != 0)
889         i++;
890
891       if (chunk_info[i].name == NULL)
892       {
893         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
894               chunk_name, filename);
895         ReadUnusedBytesFromFile(file, chunk_size);
896       }
897       else if (chunk_info[i].size != -1 &&
898                chunk_info[i].size != chunk_size)
899       {
900         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
901               chunk_size, chunk_name, filename);
902         ReadUnusedBytesFromFile(file, chunk_size);
903       }
904       else
905       {
906         /* call function to load this level chunk */
907         int chunk_size_expected =
908           (chunk_info[i].loader)(file, chunk_size, level);
909
910         /* the size of some chunks cannot be checked before reading other
911            chunks first (like "HEAD" and "BODY") that contain some header
912            information, so check them here */
913         if (chunk_size_expected != chunk_size)
914         {
915           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
916                 chunk_size, chunk_name, filename);
917         }
918       }
919     }
920   }
921
922   fclose(file);
923 }
924
925 #if 1
926
927 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
928 {
929   if (leveldir_current == NULL)         /* only when dumping level */
930     return;
931
932   /* determine correct game engine version of current level */
933   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
934       IS_LEVELCLASS_USER(leveldir_current))
935   {
936 #if 0
937     printf("\n::: This level is private or contributed: '%s'\n", filename);
938 #endif
939
940     /* For user contributed and private levels, use the version of
941        the game engine the levels were created for.
942        Since 2.0.1, the game engine version is now directly stored
943        in the level file (chunk "VERS"), so there is no need anymore
944        to set the game version from the file version (except for old,
945        pre-2.0 levels, where the game version is still taken from the
946        file format version used to store the level -- see above). */
947
948     /* do some special adjustments to support older level versions */
949     if (level->file_version == FILE_VERSION_1_0)
950     {
951       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
952       Error(ERR_WARN, "using high speed movement for player");
953
954       /* player was faster than monsters in (pre-)1.0 levels */
955       level->double_speed = TRUE;
956     }
957
958     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
959     if (level->game_version == VERSION_IDENT(2,0,1))
960       level->em_slippery_gems = TRUE;
961   }
962   else
963   {
964 #if 0
965     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
966            leveldir_current->sort_priority, filename);
967 #endif
968
969     /* Always use the latest version of the game engine for all but
970        user contributed and private levels; this allows for actual
971        corrections in the game engine to take effect for existing,
972        converted levels (from "classic" or other existing games) to
973        make the game emulation more accurate, while (hopefully) not
974        breaking existing levels created from other players. */
975
976     level->game_version = GAME_VERSION_ACTUAL;
977
978     /* Set special EM style gems behaviour: EM style gems slip down from
979        normal, steel and growing wall. As this is a more fundamental change,
980        it seems better to set the default behaviour to "off" (as it is more
981        natural) and make it configurable in the level editor (as a property
982        of gem style elements). Already existing converted levels (neither
983        private nor contributed levels) are changed to the new behaviour. */
984
985     if (level->file_version < FILE_VERSION_2_0)
986       level->em_slippery_gems = TRUE;
987   }
988 }
989
990 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
991 {
992   int i, j;
993
994   /* map custom element change events that have changed in newer versions
995      (these following values were accidentally changed in version 3.0.1) */
996   if (level->game_version <= VERSION_IDENT(3,0,0))
997   {
998     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
999     {
1000       int element = EL_CUSTOM_START + i;
1001
1002       /* order of checking and copying events to be mapped is important */
1003       for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1004       {
1005         if (HAS_CHANGE_EVENT(element, j - 2))
1006         {
1007           SET_CHANGE_EVENT(element, j - 2, FALSE);
1008           SET_CHANGE_EVENT(element, j, TRUE);
1009         }
1010       }
1011
1012       /* order of checking and copying events to be mapped is important */
1013       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1014       {
1015         if (HAS_CHANGE_EVENT(element, j - 1))
1016         {
1017           SET_CHANGE_EVENT(element, j - 1, FALSE);
1018           SET_CHANGE_EVENT(element, j, TRUE);
1019         }
1020       }
1021     }
1022   }
1023
1024   /* some custom element change events get mapped since version 3.0.3 */
1025   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1026   {
1027     int element = EL_CUSTOM_START + i;
1028
1029     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1030         HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1031     {
1032       SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1033       SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1034
1035       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1036     }
1037   }
1038
1039   /* initialize "can_change" field for old levels with only one change page */
1040   if (level->game_version <= VERSION_IDENT(3,0,2))
1041   {
1042     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1043     {
1044       int element = EL_CUSTOM_START + i;
1045
1046       if (CAN_CHANGE(element))
1047         element_info[element].change->can_change = TRUE;
1048     }
1049   }
1050
1051   /* initialize element properties for level editor etc. */
1052   InitElementPropertiesEngine(level->game_version);
1053 }
1054
1055 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1056 {
1057   int x, y;
1058
1059   /* map elements that have changed in newer versions */
1060   for(y=0; y<level->fieldy; y++)
1061   {
1062     for(x=0; x<level->fieldx; x++)
1063     {
1064       int element = level->field[x][y];
1065
1066       if (level->game_version <= VERSION_IDENT(2,2,0))
1067       {
1068         /* map game font elements */
1069         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1070                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1071                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1072                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1073       }
1074
1075       if (level->game_version < VERSION_IDENT(3,0,0))
1076       {
1077         /* map Supaplex gravity tube elements */
1078         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1079                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1080                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1081                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1082                    element);
1083       }
1084
1085       level->field[x][y] = element;
1086     }
1087   }
1088
1089   /* copy elements to runtime playfield array */
1090   for(x=0; x<MAX_LEV_FIELDX; x++)
1091     for(y=0; y<MAX_LEV_FIELDY; y++)
1092       Feld[x][y] = level->field[x][y];
1093
1094   /* initialize level size variables for faster access */
1095   lev_fieldx = level->fieldx;
1096   lev_fieldy = level->fieldy;
1097
1098   /* determine border element for this level */
1099   SetBorderElement();
1100 }
1101
1102 #else
1103
1104 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1105 {
1106   int i, j, x, y;
1107
1108   if (leveldir_current == NULL)         /* only when dumping level */
1109     return;
1110
1111   /* determine correct game engine version of current level */
1112   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1113       IS_LEVELCLASS_USER(leveldir_current))
1114   {
1115 #if 0
1116     printf("\n::: This level is private or contributed: '%s'\n", filename);
1117 #endif
1118
1119     /* For user contributed and private levels, use the version of
1120        the game engine the levels were created for.
1121        Since 2.0.1, the game engine version is now directly stored
1122        in the level file (chunk "VERS"), so there is no need anymore
1123        to set the game version from the file version (except for old,
1124        pre-2.0 levels, where the game version is still taken from the
1125        file format version used to store the level -- see above). */
1126
1127     /* do some special adjustments to support older level versions */
1128     if (level->file_version == FILE_VERSION_1_0)
1129     {
1130       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1131       Error(ERR_WARN, "using high speed movement for player");
1132
1133       /* player was faster than monsters in (pre-)1.0 levels */
1134       level->double_speed = TRUE;
1135     }
1136
1137     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1138     if (level->game_version == VERSION_IDENT(2,0,1))
1139       level->em_slippery_gems = TRUE;
1140   }
1141   else
1142   {
1143 #if 0
1144     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1145            leveldir_current->sort_priority, filename);
1146 #endif
1147
1148     /* Always use the latest version of the game engine for all but
1149        user contributed and private levels; this allows for actual
1150        corrections in the game engine to take effect for existing,
1151        converted levels (from "classic" or other existing games) to
1152        make the game emulation more accurate, while (hopefully) not
1153        breaking existing levels created from other players. */
1154
1155     level->game_version = GAME_VERSION_ACTUAL;
1156
1157     /* Set special EM style gems behaviour: EM style gems slip down from
1158        normal, steel and growing wall. As this is a more fundamental change,
1159        it seems better to set the default behaviour to "off" (as it is more
1160        natural) and make it configurable in the level editor (as a property
1161        of gem style elements). Already existing converted levels (neither
1162        private nor contributed levels) are changed to the new behaviour. */
1163
1164     if (level->file_version < FILE_VERSION_2_0)
1165       level->em_slippery_gems = TRUE;
1166   }
1167
1168   /* map elements that have changed in newer versions */
1169   for(y=0; y<level->fieldy; y++)
1170   {
1171     for(x=0; x<level->fieldx; x++)
1172     {
1173       int element = level->field[x][y];
1174
1175       if (level->game_version <= VERSION_IDENT(2,2,0))
1176       {
1177         /* map game font elements */
1178         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1179                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1180                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1181                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1182       }
1183
1184       if (level->game_version < VERSION_IDENT(3,0,0))
1185       {
1186         /* map Supaplex gravity tube elements */
1187         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1188                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1189                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1190                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1191                    element);
1192       }
1193
1194       level->field[x][y] = element;
1195     }
1196   }
1197
1198   /* map custom element change events that have changed in newer versions
1199      (these following values have accidentally changed in version 3.0.1) */
1200   if (level->game_version <= VERSION_IDENT(3,0,0))
1201   {
1202     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1203     {
1204       int element = EL_CUSTOM_START + i;
1205
1206       /* order of checking events to be mapped is important */
1207       for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1208       {
1209         if (HAS_CHANGE_EVENT(element, j - 2))
1210         {
1211           SET_CHANGE_EVENT(element, j - 2, FALSE);
1212           SET_CHANGE_EVENT(element, j, TRUE);
1213         }
1214       }
1215
1216       /* order of checking events to be mapped is important */
1217       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1218       {
1219         if (HAS_CHANGE_EVENT(element, j - 1))
1220         {
1221           SET_CHANGE_EVENT(element, j - 1, FALSE);
1222           SET_CHANGE_EVENT(element, j, TRUE);
1223         }
1224       }
1225     }
1226   }
1227
1228   /* initialize "can_change" field for old levels with only one change page */
1229   if (level->game_version <= VERSION_IDENT(3,0,2))
1230   {
1231     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1232     {
1233       int element = EL_CUSTOM_START + i;
1234
1235       if (CAN_CHANGE(element))
1236         element_info[element].change->can_change = TRUE;
1237     }
1238   }
1239
1240   /* copy elements to runtime playfield array */
1241   for(x=0; x<MAX_LEV_FIELDX; x++)
1242     for(y=0; y<MAX_LEV_FIELDY; y++)
1243       Feld[x][y] = level->field[x][y];
1244
1245   /* initialize level size variables for faster access */
1246   lev_fieldx = level->fieldx;
1247   lev_fieldy = level->fieldy;
1248
1249   /* determine border element for this level */
1250   SetBorderElement();
1251
1252   /* initialize element properties for level editor etc. */
1253   InitElementPropertiesEngine(level->game_version);
1254 }
1255
1256 #endif
1257
1258 void LoadLevelTemplate(int level_nr)
1259 {
1260   char *filename = getLevelFilename(level_nr);
1261
1262   LoadLevelFromFilename(&level_template, filename);
1263
1264   LoadLevel_InitVersion(&level, filename);
1265   LoadLevel_InitElements(&level, filename);
1266
1267   ActivateLevelTemplate();
1268 }
1269
1270 void LoadLevel(int level_nr)
1271 {
1272   char *filename = getLevelFilename(level_nr);
1273
1274   LoadLevelFromFilename(&level, filename);
1275
1276   if (level.use_custom_template)
1277     LoadLevelTemplate(-1);
1278
1279 #if 1
1280   LoadLevel_InitVersion(&level, filename);
1281   LoadLevel_InitElements(&level, filename);
1282   LoadLevel_InitPlayfield(&level, filename);
1283 #else
1284   LoadLevel_InitLevel(&level, filename);
1285 #endif
1286 }
1287
1288 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1289 {
1290   putFileVersion(file, level->file_version);
1291   putFileVersion(file, level->game_version);
1292 }
1293
1294 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1295 {
1296   int i, x, y;
1297
1298   putFile8Bit(file, level->fieldx);
1299   putFile8Bit(file, level->fieldy);
1300
1301   putFile16BitBE(file, level->time);
1302   putFile16BitBE(file, level->gems_needed);
1303
1304   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1305     putFile8Bit(file, level->name[i]);
1306
1307   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1308     putFile8Bit(file, level->score[i]);
1309
1310   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1311     for(y=0; y<3; y++)
1312       for(x=0; x<3; x++)
1313         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1314                            level->yamyam_content[i][x][y]));
1315   putFile8Bit(file, level->amoeba_speed);
1316   putFile8Bit(file, level->time_magic_wall);
1317   putFile8Bit(file, level->time_wheel);
1318   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1319                      level->amoeba_content));
1320   putFile8Bit(file, (level->double_speed ? 1 : 0));
1321   putFile8Bit(file, (level->gravity ? 1 : 0));
1322   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1323   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1324
1325   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1326
1327   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1328 }
1329
1330 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1331 {
1332   int i;
1333
1334   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1335     putFile8Bit(file, level->author[i]);
1336 }
1337
1338 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1339 {
1340   int x, y;
1341
1342   for(y=0; y<level->fieldy; y++) 
1343     for(x=0; x<level->fieldx; x++) 
1344       if (level->encoding_16bit_field)
1345         putFile16BitBE(file, level->field[x][y]);
1346       else
1347         putFile8Bit(file, level->field[x][y]);
1348 }
1349
1350 #if 0
1351 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1352 {
1353   int i, x, y;
1354
1355   putFile8Bit(file, EL_YAMYAM);
1356   putFile8Bit(file, level->num_yamyam_contents);
1357   putFile8Bit(file, 0);
1358   putFile8Bit(file, 0);
1359
1360   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1361     for(y=0; y<3; y++)
1362       for(x=0; x<3; x++)
1363         if (level->encoding_16bit_field)
1364           putFile16BitBE(file, level->yamyam_content[i][x][y]);
1365         else
1366           putFile8Bit(file, level->yamyam_content[i][x][y]);
1367 }
1368 #endif
1369
1370 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1371 {
1372   int i, x, y;
1373   int num_contents, content_xsize, content_ysize;
1374   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1375
1376   if (element == EL_YAMYAM)
1377   {
1378     num_contents = level->num_yamyam_contents;
1379     content_xsize = 3;
1380     content_ysize = 3;
1381
1382     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1383       for(y=0; y<3; y++)
1384         for(x=0; x<3; x++)
1385           content_array[i][x][y] = level->yamyam_content[i][x][y];
1386   }
1387   else if (element == EL_BD_AMOEBA)
1388   {
1389     num_contents = 1;
1390     content_xsize = 1;
1391     content_ysize = 1;
1392
1393     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1394       for(y=0; y<3; y++)
1395         for(x=0; x<3; x++)
1396           content_array[i][x][y] = EL_EMPTY;
1397     content_array[0][0][0] = level->amoeba_content;
1398   }
1399   else
1400   {
1401     /* chunk header already written -- write empty chunk data */
1402     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1403
1404     Error(ERR_WARN, "cannot save content for element '%d'", element);
1405     return;
1406   }
1407
1408   putFile16BitBE(file, element);
1409   putFile8Bit(file, num_contents);
1410   putFile8Bit(file, content_xsize);
1411   putFile8Bit(file, content_ysize);
1412
1413   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1414
1415   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1416     for(y=0; y<3; y++)
1417       for(x=0; x<3; x++)
1418         putFile16BitBE(file, content_array[i][x][y]);
1419 }
1420
1421 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1422 {
1423   int i;
1424   int envelope_nr = element - EL_ENVELOPE_1;
1425   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1426
1427   putFile16BitBE(file, element);
1428   putFile16BitBE(file, envelope_len);
1429   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1430   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1431
1432   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1433
1434   for(i=0; i < envelope_len; i++)
1435     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1436 }
1437
1438 #if 0
1439 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1440                            int num_changed_custom_elements)
1441 {
1442   int i, check = 0;
1443
1444   putFile16BitBE(file, num_changed_custom_elements);
1445
1446   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1447   {
1448     int element = EL_CUSTOM_START + i;
1449
1450     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1451     {
1452       if (check < num_changed_custom_elements)
1453       {
1454         putFile16BitBE(file, element);
1455         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1456       }
1457
1458       check++;
1459     }
1460   }
1461
1462   if (check != num_changed_custom_elements)     /* should not happen */
1463     Error(ERR_WARN, "inconsistent number of custom element properties");
1464 }
1465 #endif
1466
1467 #if 0
1468 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1469                            int num_changed_custom_elements)
1470 {
1471   int i, check = 0;
1472
1473   putFile16BitBE(file, num_changed_custom_elements);
1474
1475   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1476   {
1477     int element = EL_CUSTOM_START + i;
1478
1479     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1480     {
1481       if (check < num_changed_custom_elements)
1482       {
1483         putFile16BitBE(file, element);
1484         putFile16BitBE(file, element_info[element].change->target_element);
1485       }
1486
1487       check++;
1488     }
1489   }
1490
1491   if (check != num_changed_custom_elements)     /* should not happen */
1492     Error(ERR_WARN, "inconsistent number of custom target elements");
1493 }
1494 #endif
1495
1496 #if 0
1497 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1498                            int num_changed_custom_elements)
1499 {
1500   int i, j, x, y, check = 0;
1501
1502   putFile16BitBE(file, num_changed_custom_elements);
1503
1504   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1505   {
1506     int element = EL_CUSTOM_START + i;
1507
1508     if (element_info[element].modified_settings)
1509     {
1510       if (check < num_changed_custom_elements)
1511       {
1512         putFile16BitBE(file, element);
1513
1514         for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1515           putFile8Bit(file, element_info[element].description[j]);
1516
1517         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1518
1519         /* some free bytes for future properties and padding */
1520         WriteUnusedBytesToFile(file, 7);
1521
1522         putFile8Bit(file, element_info[element].use_gfx_element);
1523         putFile16BitBE(file, element_info[element].gfx_element);
1524
1525         putFile8Bit(file, element_info[element].collect_score);
1526         putFile8Bit(file, element_info[element].collect_count);
1527
1528         putFile16BitBE(file, element_info[element].push_delay_fixed);
1529         putFile16BitBE(file, element_info[element].push_delay_random);
1530         putFile16BitBE(file, element_info[element].move_delay_fixed);
1531         putFile16BitBE(file, element_info[element].move_delay_random);
1532
1533         putFile16BitBE(file, element_info[element].move_pattern);
1534         putFile8Bit(file, element_info[element].move_direction_initial);
1535         putFile8Bit(file, element_info[element].move_stepsize);
1536
1537         for(y=0; y<3; y++)
1538           for(x=0; x<3; x++)
1539             putFile16BitBE(file, element_info[element].content[x][y]);
1540
1541         putFile32BitBE(file, element_info[element].change->events);
1542
1543         putFile16BitBE(file, element_info[element].change->target_element);
1544
1545         putFile16BitBE(file, element_info[element].change->delay_fixed);
1546         putFile16BitBE(file, element_info[element].change->delay_random);
1547         putFile16BitBE(file, element_info[element].change->delay_frames);
1548
1549         putFile16BitBE(file, element_info[element].change->trigger_element);
1550
1551         putFile8Bit(file, element_info[element].change->explode);
1552         putFile8Bit(file, element_info[element].change->use_content);
1553         putFile8Bit(file, element_info[element].change->only_complete);
1554         putFile8Bit(file, element_info[element].change->use_random_change);
1555
1556         putFile8Bit(file, element_info[element].change->random);
1557         putFile8Bit(file, element_info[element].change->power);
1558
1559         for(y=0; y<3; y++)
1560           for(x=0; x<3; x++)
1561             putFile16BitBE(file, element_info[element].change->content[x][y]);
1562
1563         putFile8Bit(file, element_info[element].slippery_type);
1564
1565         /* some free bytes for future properties and padding */
1566         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1567       }
1568
1569       check++;
1570     }
1571   }
1572
1573   if (check != num_changed_custom_elements)     /* should not happen */
1574     Error(ERR_WARN, "inconsistent number of custom element properties");
1575 }
1576 #endif
1577
1578 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1579 {
1580   struct ElementInfo *ei = &element_info[element];
1581   int i, x, y;
1582
1583   putFile16BitBE(file, element);
1584
1585   for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1586     putFile8Bit(file, ei->description[i]);
1587
1588   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1589   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
1590
1591   putFile8Bit(file, ei->num_change_pages);
1592
1593   /* some free bytes for future base property values and padding */
1594   WriteUnusedBytesToFile(file, 5);
1595
1596   /* write custom property values */
1597
1598   putFile8Bit(file, ei->use_gfx_element);
1599   putFile16BitBE(file, ei->gfx_element);
1600
1601   putFile8Bit(file, ei->collect_score);
1602   putFile8Bit(file, ei->collect_count);
1603
1604   putFile16BitBE(file, ei->push_delay_fixed);
1605   putFile16BitBE(file, ei->push_delay_random);
1606   putFile16BitBE(file, ei->move_delay_fixed);
1607   putFile16BitBE(file, ei->move_delay_random);
1608
1609   putFile16BitBE(file, ei->move_pattern);
1610   putFile8Bit(file, ei->move_direction_initial);
1611   putFile8Bit(file, ei->move_stepsize);
1612
1613   putFile8Bit(file, ei->slippery_type);
1614
1615   for(y=0; y<3; y++)
1616     for(x=0; x<3; x++)
1617       putFile16BitBE(file, ei->content[x][y]);
1618
1619   /* some free bytes for future custom property values and padding */
1620   WriteUnusedBytesToFile(file, 12);
1621
1622   /* write change property values */
1623
1624   for (i=0; i < ei->num_change_pages; i++)
1625   {
1626     struct ElementChangeInfo *change = &ei->change_page[i];
1627
1628     putFile32BitBE(file, change->events);
1629
1630     putFile16BitBE(file, change->target_element);
1631
1632     putFile16BitBE(file, change->delay_fixed);
1633     putFile16BitBE(file, change->delay_random);
1634     putFile16BitBE(file, change->delay_frames);
1635
1636     putFile16BitBE(file, change->trigger_element);
1637
1638     putFile8Bit(file, change->explode);
1639     putFile8Bit(file, change->use_content);
1640     putFile8Bit(file, change->only_complete);
1641     putFile8Bit(file, change->use_random_change);
1642
1643     putFile8Bit(file, change->random);
1644     putFile8Bit(file, change->power);
1645
1646     for(y=0; y<3; y++)
1647       for(x=0; x<3; x++)
1648         putFile16BitBE(file, change->content[x][y]);
1649
1650     putFile8Bit(file, change->can_change);
1651
1652     /* some free bytes for future change property values and padding */
1653     WriteUnusedBytesToFile(file, 9);
1654   }
1655 }
1656
1657 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1658 {
1659   int body_chunk_size;
1660   int i, x, y;
1661   FILE *file;
1662
1663   if (!(file = fopen(filename, MODE_WRITE)))
1664   {
1665     Error(ERR_WARN, "cannot save level file '%s'", filename);
1666     return;
1667   }
1668
1669   level->file_version = FILE_VERSION_ACTUAL;
1670   level->game_version = GAME_VERSION_ACTUAL;
1671
1672   /* check level field for 16-bit elements */
1673   level->encoding_16bit_field = FALSE;
1674   for(y=0; y<level->fieldy; y++) 
1675     for(x=0; x<level->fieldx; x++) 
1676       if (level->field[x][y] > 255)
1677         level->encoding_16bit_field = TRUE;
1678
1679   /* check yamyam content for 16-bit elements */
1680   level->encoding_16bit_yamyam = FALSE;
1681   for(i=0; i<level->num_yamyam_contents; i++)
1682     for(y=0; y<3; y++)
1683       for(x=0; x<3; x++)
1684         if (level->yamyam_content[i][x][y] > 255)
1685           level->encoding_16bit_yamyam = TRUE;
1686
1687   /* check amoeba content for 16-bit elements */
1688   level->encoding_16bit_amoeba = FALSE;
1689   if (level->amoeba_content > 255)
1690     level->encoding_16bit_amoeba = TRUE;
1691
1692   /* calculate size of "BODY" chunk */
1693   body_chunk_size =
1694     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1695
1696   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1697   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1698
1699   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1700   SaveLevel_VERS(file, level);
1701
1702   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1703   SaveLevel_HEAD(file, level);
1704
1705   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1706   SaveLevel_AUTH(file, level);
1707
1708   putFileChunkBE(file, "BODY", body_chunk_size);
1709   SaveLevel_BODY(file, level);
1710
1711   if (level->encoding_16bit_yamyam ||
1712       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1713   {
1714     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1715     SaveLevel_CNT2(file, level, EL_YAMYAM);
1716   }
1717
1718   if (level->encoding_16bit_amoeba)
1719   {
1720     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1721     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1722   }
1723
1724   /* check for envelope content */
1725   for (i=0; i<4; i++)
1726   {
1727     if (strlen(level->envelope_text[i]) > 0)
1728     {
1729       int envelope_len = strlen(level->envelope_text[i]) + 1;
1730
1731       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1732       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1733     }
1734   }
1735
1736   /* check for non-default custom elements (unless using template level) */
1737   if (!level->use_custom_template)
1738   {
1739     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1740     {
1741       int element = EL_CUSTOM_START + i;
1742
1743       if (element_info[element].modified_settings)
1744       {
1745         int num_change_pages = element_info[element].num_change_pages;
1746
1747         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1748         SaveLevel_CUS4(file, level, element);
1749       }
1750     }
1751   }
1752
1753   fclose(file);
1754
1755   SetFilePermissions(filename, PERMS_PRIVATE);
1756 }
1757
1758 void SaveLevel(int level_nr)
1759 {
1760   char *filename = getLevelFilename(level_nr);
1761
1762   SaveLevelFromFilename(&level, filename);
1763 }
1764
1765 void SaveLevelTemplate()
1766 {
1767   char *filename = getLevelFilename(-1);
1768
1769   SaveLevelFromFilename(&level, filename);
1770 }
1771
1772 void DumpLevel(struct LevelInfo *level)
1773 {
1774   printf_line("-", 79);
1775   printf("Level xxx (file version %08d, game version %08d)\n",
1776          level->file_version, level->game_version);
1777   printf_line("-", 79);
1778
1779   printf("Level Author: '%s'\n", level->author);
1780   printf("Level Title:  '%s'\n", level->name);
1781   printf("\n");
1782   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1783   printf("\n");
1784   printf("Level Time:  %d seconds\n", level->time);
1785   printf("Gems needed: %d\n", level->gems_needed);
1786   printf("\n");
1787   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1788   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
1789   printf("Time for Light:      %d seconds\n", level->time_light);
1790   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
1791   printf("\n");
1792   printf("Amoeba Speed: %d\n", level->amoeba_speed);
1793   printf("\n");
1794   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
1795   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
1796   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1797
1798   printf_line("-", 79);
1799 }
1800
1801
1802 /* ========================================================================= */
1803 /* tape file functions                                                       */
1804 /* ========================================================================= */
1805
1806 static void setTapeInfoToDefaults()
1807 {
1808   int i;
1809
1810   /* always start with reliable default values (empty tape) */
1811   TapeErase();
1812
1813   /* default values (also for pre-1.2 tapes) with only the first player */
1814   tape.player_participates[0] = TRUE;
1815   for(i=1; i<MAX_PLAYERS; i++)
1816     tape.player_participates[i] = FALSE;
1817
1818   /* at least one (default: the first) player participates in every tape */
1819   tape.num_participating_players = 1;
1820
1821   tape.level_nr = level_nr;
1822   tape.counter = 0;
1823   tape.changed = FALSE;
1824
1825   tape.recording = FALSE;
1826   tape.playing = FALSE;
1827   tape.pausing = FALSE;
1828 }
1829
1830 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1831 {
1832   tape->file_version = getFileVersion(file);
1833   tape->game_version = getFileVersion(file);
1834
1835   return chunk_size;
1836 }
1837
1838 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1839 {
1840   int i;
1841
1842   tape->random_seed = getFile32BitBE(file);
1843   tape->date        = getFile32BitBE(file);
1844   tape->length      = getFile32BitBE(file);
1845
1846   /* read header fields that are new since version 1.2 */
1847   if (tape->file_version >= FILE_VERSION_1_2)
1848   {
1849     byte store_participating_players = getFile8Bit(file);
1850     int engine_version;
1851
1852     /* since version 1.2, tapes store which players participate in the tape */
1853     tape->num_participating_players = 0;
1854     for(i=0; i<MAX_PLAYERS; i++)
1855     {
1856       tape->player_participates[i] = FALSE;
1857
1858       if (store_participating_players & (1 << i))
1859       {
1860         tape->player_participates[i] = TRUE;
1861         tape->num_participating_players++;
1862       }
1863     }
1864
1865     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1866
1867     engine_version = getFileVersion(file);
1868     if (engine_version > 0)
1869       tape->engine_version = engine_version;
1870     else
1871       tape->engine_version = tape->game_version;
1872   }
1873
1874   return chunk_size;
1875 }
1876
1877 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1878 {
1879   int level_identifier_size;
1880   int i;
1881
1882   level_identifier_size = getFile16BitBE(file);
1883
1884   tape->level_identifier =
1885     checked_realloc(tape->level_identifier, level_identifier_size);
1886
1887   for(i=0; i < level_identifier_size; i++)
1888     tape->level_identifier[i] = getFile8Bit(file);
1889
1890   tape->level_nr = getFile16BitBE(file);
1891
1892   chunk_size = 2 + level_identifier_size + 2;
1893
1894   return chunk_size;
1895 }
1896
1897 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1898 {
1899   int i, j;
1900   int chunk_size_expected =
1901     (tape->num_participating_players + 1) * tape->length;
1902
1903   if (chunk_size_expected != chunk_size)
1904   {
1905     ReadUnusedBytesFromFile(file, chunk_size);
1906     return chunk_size_expected;
1907   }
1908
1909   for(i=0; i<tape->length; i++)
1910   {
1911     if (i >= MAX_TAPELEN)
1912       break;
1913
1914     for(j=0; j<MAX_PLAYERS; j++)
1915     {
1916       tape->pos[i].action[j] = MV_NO_MOVING;
1917
1918       if (tape->player_participates[j])
1919         tape->pos[i].action[j] = getFile8Bit(file);
1920     }
1921
1922     tape->pos[i].delay = getFile8Bit(file);
1923
1924     if (tape->file_version == FILE_VERSION_1_0)
1925     {
1926       /* eliminate possible diagonal moves in old tapes */
1927       /* this is only for backward compatibility */
1928
1929       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1930       byte action = tape->pos[i].action[0];
1931       int k, num_moves = 0;
1932
1933       for (k=0; k<4; k++)
1934       {
1935         if (action & joy_dir[k])
1936         {
1937           tape->pos[i + num_moves].action[0] = joy_dir[k];
1938           if (num_moves > 0)
1939             tape->pos[i + num_moves].delay = 0;
1940           num_moves++;
1941         }
1942       }
1943
1944       if (num_moves > 1)
1945       {
1946         num_moves--;
1947         i += num_moves;
1948         tape->length += num_moves;
1949       }
1950     }
1951     else if (tape->file_version < FILE_VERSION_2_0)
1952     {
1953       /* convert pre-2.0 tapes to new tape format */
1954
1955       if (tape->pos[i].delay > 1)
1956       {
1957         /* action part */
1958         tape->pos[i + 1] = tape->pos[i];
1959         tape->pos[i + 1].delay = 1;
1960
1961         /* delay part */
1962         for(j=0; j<MAX_PLAYERS; j++)
1963           tape->pos[i].action[j] = MV_NO_MOVING;
1964         tape->pos[i].delay--;
1965
1966         i++;
1967         tape->length++;
1968       }
1969     }
1970
1971     if (feof(file))
1972       break;
1973   }
1974
1975   if (i != tape->length)
1976     chunk_size = (tape->num_participating_players + 1) * i;
1977
1978   return chunk_size;
1979 }
1980
1981 void LoadTapeFromFilename(char *filename)
1982 {
1983   char cookie[MAX_LINE_LEN];
1984   char chunk_name[CHUNK_ID_LEN + 1];
1985   FILE *file;
1986   int chunk_size;
1987
1988   /* always start with reliable default values */
1989   setTapeInfoToDefaults();
1990
1991   if (!(file = fopen(filename, MODE_READ)))
1992     return;
1993
1994   getFileChunkBE(file, chunk_name, NULL);
1995   if (strcmp(chunk_name, "RND1") == 0)
1996   {
1997     getFile32BitBE(file);               /* not used */
1998
1999     getFileChunkBE(file, chunk_name, NULL);
2000     if (strcmp(chunk_name, "TAPE") != 0)
2001     {
2002       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2003       fclose(file);
2004       return;
2005     }
2006   }
2007   else  /* check for pre-2.0 file format with cookie string */
2008   {
2009     strcpy(cookie, chunk_name);
2010     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2011     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2012       cookie[strlen(cookie) - 1] = '\0';
2013
2014     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2015     {
2016       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2017       fclose(file);
2018       return;
2019     }
2020
2021     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2022     {
2023       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2024       fclose(file);
2025       return;
2026     }
2027
2028     /* pre-2.0 tape files have no game version, so use file version here */
2029     tape.game_version = tape.file_version;
2030   }
2031
2032   if (tape.file_version < FILE_VERSION_1_2)
2033   {
2034     /* tape files from versions before 1.2.0 without chunk structure */
2035     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2036     LoadTape_BODY(file, 2 * tape.length,  &tape);
2037   }
2038   else
2039   {
2040     static struct
2041     {
2042       char *name;
2043       int size;
2044       int (*loader)(FILE *, int, struct TapeInfo *);
2045     }
2046     chunk_info[] =
2047     {
2048       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
2049       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
2050       { "INFO", -1,                     LoadTape_INFO },
2051       { "BODY", -1,                     LoadTape_BODY },
2052       {  NULL,  0,                      NULL }
2053     };
2054
2055     while (getFileChunkBE(file, chunk_name, &chunk_size))
2056     {
2057       int i = 0;
2058
2059       while (chunk_info[i].name != NULL &&
2060              strcmp(chunk_name, chunk_info[i].name) != 0)
2061         i++;
2062
2063       if (chunk_info[i].name == NULL)
2064       {
2065         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2066               chunk_name, filename);
2067         ReadUnusedBytesFromFile(file, chunk_size);
2068       }
2069       else if (chunk_info[i].size != -1 &&
2070                chunk_info[i].size != chunk_size)
2071       {
2072         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2073               chunk_size, chunk_name, filename);
2074         ReadUnusedBytesFromFile(file, chunk_size);
2075       }
2076       else
2077       {
2078         /* call function to load this tape chunk */
2079         int chunk_size_expected =
2080           (chunk_info[i].loader)(file, chunk_size, &tape);
2081
2082         /* the size of some chunks cannot be checked before reading other
2083            chunks first (like "HEAD" and "BODY") that contain some header
2084            information, so check them here */
2085         if (chunk_size_expected != chunk_size)
2086         {
2087           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2088                 chunk_size, chunk_name, filename);
2089         }
2090       }
2091     }
2092   }
2093
2094   fclose(file);
2095
2096   tape.length_seconds = GetTapeLength();
2097
2098 #if 0
2099   printf("tape game version: %d\n", tape.game_version);
2100   printf("tape engine version: %d\n", tape.engine_version);
2101 #endif
2102 }
2103
2104 void LoadTape(int level_nr)
2105 {
2106   char *filename = getTapeFilename(level_nr);
2107
2108   LoadTapeFromFilename(filename);
2109 }
2110
2111 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2112 {
2113   putFileVersion(file, tape->file_version);
2114   putFileVersion(file, tape->game_version);
2115 }
2116
2117 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2118 {
2119   int i;
2120   byte store_participating_players = 0;
2121
2122   /* set bits for participating players for compact storage */
2123   for(i=0; i<MAX_PLAYERS; i++)
2124     if (tape->player_participates[i])
2125       store_participating_players |= (1 << i);
2126
2127   putFile32BitBE(file, tape->random_seed);
2128   putFile32BitBE(file, tape->date);
2129   putFile32BitBE(file, tape->length);
2130
2131   putFile8Bit(file, store_participating_players);
2132
2133   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2134   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2135
2136   putFileVersion(file, tape->engine_version);
2137 }
2138
2139 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2140 {
2141   int level_identifier_size = strlen(tape->level_identifier) + 1;
2142   int i;
2143
2144   putFile16BitBE(file, level_identifier_size);
2145
2146   for(i=0; i < level_identifier_size; i++)
2147     putFile8Bit(file, tape->level_identifier[i]);
2148
2149   putFile16BitBE(file, tape->level_nr);
2150 }
2151
2152 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2153 {
2154   int i, j;
2155
2156   for(i=0; i<tape->length; i++)
2157   {
2158     for(j=0; j<MAX_PLAYERS; j++)
2159       if (tape->player_participates[j])
2160         putFile8Bit(file, tape->pos[i].action[j]);
2161
2162     putFile8Bit(file, tape->pos[i].delay);
2163   }
2164 }
2165
2166 void SaveTape(int level_nr)
2167 {
2168   char *filename = getTapeFilename(level_nr);
2169   FILE *file;
2170   boolean new_tape = TRUE;
2171   int num_participating_players = 0;
2172   int info_chunk_size;
2173   int body_chunk_size;
2174   int i;
2175
2176   InitTapeDirectory(leveldir_current->filename);
2177
2178   /* if a tape still exists, ask to overwrite it */
2179   if (access(filename, F_OK) == 0)
2180   {
2181     new_tape = FALSE;
2182     if (!Request("Replace old tape ?", REQ_ASK))
2183       return;
2184   }
2185
2186   if (!(file = fopen(filename, MODE_WRITE)))
2187   {
2188     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2189     return;
2190   }
2191
2192   tape.file_version = FILE_VERSION_ACTUAL;
2193   tape.game_version = GAME_VERSION_ACTUAL;
2194
2195   /* count number of participating players  */
2196   for(i=0; i<MAX_PLAYERS; i++)
2197     if (tape.player_participates[i])
2198       num_participating_players++;
2199
2200   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2201   body_chunk_size = (num_participating_players + 1) * tape.length;
2202
2203   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2204   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2205
2206   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2207   SaveTape_VERS(file, &tape);
2208
2209   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2210   SaveTape_HEAD(file, &tape);
2211
2212   putFileChunkBE(file, "INFO", info_chunk_size);
2213   SaveTape_INFO(file, &tape);
2214
2215   putFileChunkBE(file, "BODY", body_chunk_size);
2216   SaveTape_BODY(file, &tape);
2217
2218   fclose(file);
2219
2220   SetFilePermissions(filename, PERMS_PRIVATE);
2221
2222   tape.changed = FALSE;
2223
2224   if (new_tape)
2225     Request("tape saved !", REQ_CONFIRM);
2226 }
2227
2228 void DumpTape(struct TapeInfo *tape)
2229 {
2230   int i, j;
2231
2232   if (TAPE_IS_EMPTY(*tape))
2233   {
2234     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2235     return;
2236   }
2237
2238   printf_line("-", 79);
2239   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2240          tape->level_nr, tape->file_version, tape->game_version);
2241   printf("Level series identifier: '%s'\n", tape->level_identifier);
2242   printf_line("-", 79);
2243
2244   for(i=0; i<tape->length; i++)
2245   {
2246     if (i >= MAX_TAPELEN)
2247       break;
2248
2249     printf("%03d: ", i);
2250
2251     for(j=0; j<MAX_PLAYERS; j++)
2252     {
2253       if (tape->player_participates[j])
2254       {
2255         int action = tape->pos[i].action[j];
2256
2257         printf("%d:%02x ", j, action);
2258         printf("[%c%c%c%c|%c%c] - ",
2259                (action & JOY_LEFT ? '<' : ' '),
2260                (action & JOY_RIGHT ? '>' : ' '),
2261                (action & JOY_UP ? '^' : ' '),
2262                (action & JOY_DOWN ? 'v' : ' '),
2263                (action & JOY_BUTTON_1 ? '1' : ' '),
2264                (action & JOY_BUTTON_2 ? '2' : ' '));
2265       }
2266     }
2267
2268     printf("(%03d)\n", tape->pos[i].delay);
2269   }
2270
2271   printf_line("-", 79);
2272 }
2273
2274
2275 /* ========================================================================= */
2276 /* score file functions                                                      */
2277 /* ========================================================================= */
2278
2279 void LoadScore(int level_nr)
2280 {
2281   int i;
2282   char *filename = getScoreFilename(level_nr);
2283   char cookie[MAX_LINE_LEN];
2284   char line[MAX_LINE_LEN];
2285   char *line_ptr;
2286   FILE *file;
2287
2288   /* always start with reliable default values */
2289   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2290   {
2291     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2292     highscore[i].Score = 0;
2293   }
2294
2295   if (!(file = fopen(filename, MODE_READ)))
2296     return;
2297
2298   /* check file identifier */
2299   fgets(cookie, MAX_LINE_LEN, file);
2300   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2301     cookie[strlen(cookie) - 1] = '\0';
2302
2303   if (!checkCookieString(cookie, SCORE_COOKIE))
2304   {
2305     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2306     fclose(file);
2307     return;
2308   }
2309
2310   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2311   {
2312     fscanf(file, "%d", &highscore[i].Score);
2313     fgets(line, MAX_LINE_LEN, file);
2314
2315     if (line[strlen(line) - 1] == '\n')
2316       line[strlen(line) - 1] = '\0';
2317
2318     for (line_ptr = line; *line_ptr; line_ptr++)
2319     {
2320       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2321       {
2322         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2323         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2324         break;
2325       }
2326     }
2327   }
2328
2329   fclose(file);
2330 }
2331
2332 void SaveScore(int level_nr)
2333 {
2334   int i;
2335   char *filename = getScoreFilename(level_nr);
2336   FILE *file;
2337
2338   InitScoreDirectory(leveldir_current->filename);
2339
2340   if (!(file = fopen(filename, MODE_WRITE)))
2341   {
2342     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2343     return;
2344   }
2345
2346   fprintf(file, "%s\n\n", SCORE_COOKIE);
2347
2348   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2349     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2350
2351   fclose(file);
2352
2353   SetFilePermissions(filename, PERMS_PUBLIC);
2354 }
2355
2356
2357 /* ========================================================================= */
2358 /* setup file functions                                                      */
2359 /* ========================================================================= */
2360
2361 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2362
2363 /* global setup */
2364 #define SETUP_TOKEN_PLAYER_NAME                 0
2365 #define SETUP_TOKEN_SOUND                       1
2366 #define SETUP_TOKEN_SOUND_LOOPS                 2
2367 #define SETUP_TOKEN_SOUND_MUSIC                 3
2368 #define SETUP_TOKEN_SOUND_SIMPLE                4
2369 #define SETUP_TOKEN_TOONS                       5
2370 #define SETUP_TOKEN_SCROLL_DELAY                6
2371 #define SETUP_TOKEN_SOFT_SCROLLING              7
2372 #define SETUP_TOKEN_FADING                      8
2373 #define SETUP_TOKEN_AUTORECORD                  9
2374 #define SETUP_TOKEN_QUICK_DOORS                 10
2375 #define SETUP_TOKEN_TEAM_MODE                   11
2376 #define SETUP_TOKEN_HANDICAP                    12
2377 #define SETUP_TOKEN_TIME_LIMIT                  13
2378 #define SETUP_TOKEN_FULLSCREEN                  14
2379 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2380 #define SETUP_TOKEN_GRAPHICS_SET                16
2381 #define SETUP_TOKEN_SOUNDS_SET                  17
2382 #define SETUP_TOKEN_MUSIC_SET                   18
2383 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2384 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2385 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2386
2387 #define NUM_GLOBAL_SETUP_TOKENS                 22
2388
2389 /* editor setup */
2390 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2391 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2392 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2393 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2394 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2395 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2396 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2397 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2398 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2399 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2400 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2401
2402 #define NUM_EDITOR_SETUP_TOKENS                 11
2403
2404 /* shortcut setup */
2405 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2406 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2407 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2408
2409 #define NUM_SHORTCUT_SETUP_TOKENS               3
2410
2411 /* player setup */
2412 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2413 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2414 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2415 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2416 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2417 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2418 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2419 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2420 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2421 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
2422 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2423 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2424 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2425 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2426 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2427 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
2428
2429 #define NUM_PLAYER_SETUP_TOKENS                 16
2430
2431 /* system setup */
2432 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2433 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2434
2435 #define NUM_SYSTEM_SETUP_TOKENS                 2
2436
2437 /* options setup */
2438 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2439
2440 #define NUM_OPTIONS_SETUP_TOKENS                1
2441
2442
2443 static struct SetupInfo si;
2444 static struct SetupEditorInfo sei;
2445 static struct SetupShortcutInfo ssi;
2446 static struct SetupInputInfo sii;
2447 static struct SetupSystemInfo syi;
2448 static struct OptionInfo soi;
2449
2450 static struct TokenInfo global_setup_tokens[] =
2451 {
2452   { TYPE_STRING, &si.player_name,       "player_name"                   },
2453   { TYPE_SWITCH, &si.sound,             "sound"                         },
2454   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2455   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2456   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2457   { TYPE_SWITCH, &si.toons,             "toons"                         },
2458   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2459   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2460   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2461   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2462   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2463   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2464   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2465   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2466   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2467   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2468   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2469   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2470   { TYPE_STRING, &si.music_set,         "music_set"                     },
2471   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2472   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2473   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2474 };
2475
2476 static struct TokenInfo editor_setup_tokens[] =
2477 {
2478   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2479   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2480   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2481   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2482   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2483   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2484   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2485   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2486   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2487   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2488   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2489 };
2490
2491 static struct TokenInfo shortcut_setup_tokens[] =
2492 {
2493   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2494   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2495   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2496 };
2497
2498 static struct TokenInfo player_setup_tokens[] =
2499 {
2500   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2501   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2502   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2503   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2504   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2505   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2506   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2507   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2508   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2509   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2510   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2511   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2512   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2513   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2514   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2515   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
2516 };
2517
2518 static struct TokenInfo system_setup_tokens[] =
2519 {
2520   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2521   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2522 };
2523
2524 static struct TokenInfo options_setup_tokens[] =
2525 {
2526   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2527 };
2528
2529 static char *get_corrected_login_name(char *login_name)
2530 {
2531   /* needed because player name must be a fixed length string */
2532   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2533
2534   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2535   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2536
2537   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2538     if (strchr(login_name_new, ' '))
2539       *strchr(login_name_new, ' ') = '\0';
2540
2541   return login_name_new;
2542 }
2543
2544 static void setSetupInfoToDefaults(struct SetupInfo *si)
2545 {
2546   int i;
2547
2548   si->player_name = get_corrected_login_name(getLoginName());
2549
2550   si->sound = TRUE;
2551   si->sound_loops = TRUE;
2552   si->sound_music = TRUE;
2553   si->sound_simple = TRUE;
2554   si->toons = TRUE;
2555   si->double_buffering = TRUE;
2556   si->direct_draw = !si->double_buffering;
2557   si->scroll_delay = TRUE;
2558   si->soft_scrolling = TRUE;
2559   si->fading = FALSE;
2560   si->autorecord = TRUE;
2561   si->quick_doors = FALSE;
2562   si->team_mode = FALSE;
2563   si->handicap = TRUE;
2564   si->time_limit = TRUE;
2565   si->fullscreen = FALSE;
2566   si->ask_on_escape = TRUE;
2567
2568   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2569   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2570   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2571   si->override_level_graphics = FALSE;
2572   si->override_level_sounds = FALSE;
2573   si->override_level_music = FALSE;
2574
2575   si->editor.el_boulderdash = TRUE;
2576   si->editor.el_emerald_mine = TRUE;
2577   si->editor.el_more = TRUE;
2578   si->editor.el_sokoban = TRUE;
2579   si->editor.el_supaplex = TRUE;
2580   si->editor.el_diamond_caves = TRUE;
2581   si->editor.el_dx_boulderdash = TRUE;
2582   si->editor.el_chars = TRUE;
2583   si->editor.el_custom = TRUE;
2584   si->editor.el_custom_more = FALSE;
2585
2586   si->editor.el_headlines = TRUE;
2587
2588   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2589   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2590   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2591
2592   for (i=0; i<MAX_PLAYERS; i++)
2593   {
2594     si->input[i].use_joystick = FALSE;
2595     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2596     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2597     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2598     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2599     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2600     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2601     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2602     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2603     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2604     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2605     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2606     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2607     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2608     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2609     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2610   }
2611
2612   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2613   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2614
2615   si->options.verbose = FALSE;
2616 }
2617
2618 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2619 {
2620   int i, pnr;
2621
2622   if (!setup_file_hash)
2623     return;
2624
2625   /* global setup */
2626   si = setup;
2627   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2628     setSetupInfo(global_setup_tokens, i,
2629                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2630   setup = si;
2631
2632   /* editor setup */
2633   sei = setup.editor;
2634   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2635     setSetupInfo(editor_setup_tokens, i,
2636                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2637   setup.editor = sei;
2638
2639   /* shortcut setup */
2640   ssi = setup.shortcut;
2641   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2642     setSetupInfo(shortcut_setup_tokens, i,
2643                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2644   setup.shortcut = ssi;
2645
2646   /* player setup */
2647   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2648   {
2649     char prefix[30];
2650
2651     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2652
2653     sii = setup.input[pnr];
2654     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2655     {
2656       char full_token[100];
2657
2658       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2659       setSetupInfo(player_setup_tokens, i,
2660                    getHashEntry(setup_file_hash, full_token));
2661     }
2662     setup.input[pnr] = sii;
2663   }
2664
2665   /* system setup */
2666   syi = setup.system;
2667   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2668     setSetupInfo(system_setup_tokens, i,
2669                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2670   setup.system = syi;
2671
2672   /* options setup */
2673   soi = setup.options;
2674   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2675     setSetupInfo(options_setup_tokens, i,
2676                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2677   setup.options = soi;
2678 }
2679
2680 void LoadSetup()
2681 {
2682   char *filename = getSetupFilename();
2683   SetupFileHash *setup_file_hash = NULL;
2684
2685   /* always start with reliable default values */
2686   setSetupInfoToDefaults(&setup);
2687
2688   setup_file_hash = loadSetupFileHash(filename);
2689
2690   if (setup_file_hash)
2691   {
2692     char *player_name_new;
2693
2694     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2695     decodeSetupFileHash(setup_file_hash);
2696
2697     setup.direct_draw = !setup.double_buffering;
2698
2699     freeSetupFileHash(setup_file_hash);
2700
2701     /* needed to work around problems with fixed length strings */
2702     player_name_new = get_corrected_login_name(setup.player_name);
2703     free(setup.player_name);
2704     setup.player_name = player_name_new;
2705   }
2706   else
2707     Error(ERR_WARN, "using default setup values");
2708 }
2709
2710 void SaveSetup()
2711 {
2712   char *filename = getSetupFilename();
2713   FILE *file;
2714   int i, pnr;
2715
2716   InitUserDataDirectory();
2717
2718   if (!(file = fopen(filename, MODE_WRITE)))
2719   {
2720     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2721     return;
2722   }
2723
2724   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2725                                                getCookie("SETUP")));
2726   fprintf(file, "\n");
2727
2728   /* global setup */
2729   si = setup;
2730   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2731   {
2732     /* just to make things nicer :) */
2733     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2734         i == SETUP_TOKEN_GRAPHICS_SET)
2735       fprintf(file, "\n");
2736
2737     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2738   }
2739
2740   /* editor setup */
2741   sei = setup.editor;
2742   fprintf(file, "\n");
2743   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2744     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2745
2746   /* shortcut setup */
2747   ssi = setup.shortcut;
2748   fprintf(file, "\n");
2749   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2750     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2751
2752   /* player setup */
2753   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2754   {
2755     char prefix[30];
2756
2757     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2758     fprintf(file, "\n");
2759
2760     sii = setup.input[pnr];
2761     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2762       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2763   }
2764
2765   /* system setup */
2766   syi = setup.system;
2767   fprintf(file, "\n");
2768   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2769     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2770
2771   /* options setup */
2772   soi = setup.options;
2773   fprintf(file, "\n");
2774   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2775     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2776
2777   fclose(file);
2778
2779   SetFilePermissions(filename, PERMS_PRIVATE);
2780 }
2781
2782 void LoadCustomElementDescriptions()
2783 {
2784   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2785   SetupFileHash *setup_file_hash;
2786   int i;
2787
2788   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2789   {
2790     if (element_info[i].custom_description != NULL)
2791     {
2792       free(element_info[i].custom_description);
2793       element_info[i].custom_description = NULL;
2794     }
2795   }
2796
2797   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2798     return;
2799
2800   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2801   {
2802     char *token = getStringCat2(element_info[i].token_name, ".name");
2803     char *value = getHashEntry(setup_file_hash, token);
2804
2805     if (value != NULL)
2806       element_info[i].custom_description = getStringCopy(value);
2807
2808     free(token);
2809   }
2810
2811   freeSetupFileHash(setup_file_hash);
2812 }
2813
2814 static int get_special_integer_from_string(char *string_raw)
2815 {
2816   char *string = getStringToLower(string_raw);
2817   int value = (strcmp(string, "none")  == 0 ? 0 :
2818                strcmp(string, "short") == 0 ? 1 :
2819                strcmp(string, "full")  == 0 ? 2 :
2820                strcmp(string, "default")  == 0 ? 0 :
2821                strcmp(string, "curtain")  == 0 ? 1 : -1);
2822
2823   free(string);
2824
2825   return value;
2826 }
2827
2828 void LoadSpecialMenuDesignSettings()
2829 {
2830   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2831   SetupFileHash *setup_file_hash;
2832   int i, j;
2833
2834   /* always start with reliable default values from default config */
2835   for (i=0; image_config_vars[i].token != NULL; i++)
2836     for (j=0; image_config[j].token != NULL; j++)
2837       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2838       {
2839         if (strcmp(image_config_vars[i].token, "game.envelope.anim_mode") == 0
2840             || strcmp(image_config_vars[i].token, "door.anim_mode") == 0)
2841           *image_config_vars[i].value =
2842             get_special_integer_from_string(image_config[j].value);
2843         else
2844           *image_config_vars[i].value =
2845             get_integer_from_string(image_config[j].value);
2846       }
2847
2848   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2849     return;
2850
2851   /* special case: initialize with default values that may be overwritten */
2852   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2853   {
2854     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2855     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2856     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2857
2858     if (value_x != NULL)
2859       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2860     if (value_y != NULL)
2861       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2862     if (list_size != NULL)
2863       menu.list_size[i] = get_integer_from_string(list_size);
2864   }
2865
2866   /* read (and overwrite with) values that may be specified in config file */
2867   for (i=0; image_config_vars[i].token != NULL; i++)
2868   {
2869     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2870
2871     if (value != NULL)
2872     {
2873       if (strcmp(image_config_vars[i].token, "game.envelope.anim_mode") == 0
2874           || strcmp(image_config_vars[i].token, "door.anim_mode") == 0)
2875         *image_config_vars[i].value = get_special_integer_from_string(value);
2876       else
2877         *image_config_vars[i].value = get_integer_from_string(value);
2878     }
2879   }
2880
2881   freeSetupFileHash(setup_file_hash);
2882 }