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