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