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